Jump to content

Search the Community

Showing results for tags 'tutorial'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Metin2 Dev
    • Announcements
  • Community
    • Member Representations
    • Off Topic
  • Miscellaneous
    • Metin2
    • Showcase
    • File Requests
    • Community Support - Questions & Answers
    • Paid Support / Searching / Recruiting
  • Metin2 Development
  • Metin2 Development
    • Basic Tutorials / Beginners
    • Guides & HowTo
    • Binaries
    • Programming & Development
    • Web Development & Scripts / Systems
    • Tools & Programs
    • Maps
    • Quests
    • 3D Models
    • 2D Graphics
    • Operating Systems
    • Miscellaneous
  • Private Servers
    • Private Servers
  • Uncategorized
    • Drafts
    • Trash
    • Archive
    • Temporary
    • Metin2 Download

Product Groups

  • Small Advertisement
  • Large Advertisement
  • Advertising

Categories

  • Third Party - Providers Directory

Categories

  • Overview
  • Pages
    • Overview
    • File Formats
    • Network
    • Extensions

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Pillory


Marketplace


Game Server


Country


Nationality


Github


Gitlab


Discord


Skype


Website

  1. The language of the video is Turkish, but Im adding in case you may need visuals. Skip to 19th minute, rain files start from there... Short summary for avoiding the video: Extract files from InstantServer.exe Create virtual machine using .vhd file that extracted from InstanServer SFX archive. Be generous when adjusting the RAM amount. Otherwise, swap space will not be enough. Change the local IP of the virtual machine sysinstall -> Configure -> Networking -> Interfaces -> em0 Do you want to try IPv6 blabla.. -> No (In Turkey we don't use IPv6 yet.) Do you want to try DHCP blabla.. -> Yes (With this, you can automatically assign the first available IP in the modem.) Reboot or restart netif service with; /etc/rc.d/netif restart Type the following command in root directory for start the server: ./start Extract client files from metin2.7z ( I used WinXP because, defender deletes mc.exe for containing virus. Also bunch of compability issues in win10. I strongly suggest use virtual machine. XP in virtual machine safe and sound...) Open mc.txt with text editor. Put your local IP address of FreeBSD Start metin2.exe and click the button in the image below... Use ID: rain Pass: 12345 and Booomm... For English client use the mc_eng.7z Extract everything inside to client folder and replace. Special thanks to ShinsooLord for reuploading and rain for releasing this beauty. Download links You can get InstantServer from this topic All in one => Metin2 Download Recompressed InstantServer SFX archive contents -> Mc倚天2完整版.7z VT SHA-256: 436BCFB3963B02506EEEB07713F18396CA49DA6BA084D7ACE8CC361885622062 Pass: rain Client -> metin2.7z VT SHA-256 : 27E2FCFB486E3682A898FB32F7F30F064729D7BD7323FEB4D9B202A76486FB2B Pass: rain Client Eng patch -> mc_eng.7z VT SHA-256 : 974F8402524BC10F5EF82D87F11C64721525AD00D89AD9E02171D8CD75612672 Pass: rain Rain server files extracted in the video from vhd, recompressed with lzma2 -> rain.7z VT SHA-256 : 1833A13707ABBCE334A030CD5A2E5F8D6F2C336FF9EB9D2CDD3417519758A5FA Sql file that contains all databases extracted with mysqldump from vhd -> all_rain_db.sql VT SHA-256 : E42637DA2B712A829D43186F29532A643DB02363366DDAE082D08EC2235373EF That's all folks...
  2. M2 Download Center Download Here ( Internal ) Hello, this script is available for some time already, but i didnt find time to do this earlier. I won't describe everything step by step, all you need to know is in the video. What software do you need? - 3Ds max - Noesis Only thing you need to do is download and paste gr2 script into noesis plugin/python folder, that's it. Download: Video: Original topic (on Xentax) All credits goes to jayn23 from xentax!!
  3. DISCLAIMER: maybe one or two people will recognize this topic from a different community, saying "hey, you are not the original author". I am not, but I am a co-worker of his and have the permission to do so. This topic wants to be a study case of one of the most basic mechanics in Metin: autoattacks and the in game entities' sync position. The discussion will be divided like this: 1. Analysis on the idea and implementation of the synchronization between clients and management of the attacks 1.1 Design problems and bugs in the code 1.2 Consideration about some derived "mechanics" (fly, rolling dagger skill) 2. Challenges faced when working on an highly customized revision of Metin2 3. My approach on how to fix the missing implementation done in 2009 3.1 Result 3.2 Benefits 4. Possible expansions Chapter 1: Movements and Attacks on Metin2 Metin gives charge to the entire flow logic of attacks and synchronization of the clients, to the client itself. The server is merely there and rarely intervenes to validate and force the position of the players at regular intervals (so the the clients actually know the positions of the players and not at random coords). Every client is notified of basic info, such as "I am 123 and I started attacking in this direction" or "I am 123 and I hit 456" or yet "I am 456, I am standing still, here" and every client executes, locally, with his infos on the state of the game (such as 123 was at coords x,y whereas 456 was at coords x2,y2) the animations and displacements, modifying the local state of the characters (after the hit, 456 will be at the coords x3,y3 whereas 123 moved to coords x+1,y+1). Sometimes, precisely 300ms, every client sends the server his state and the server synchronizes the other clients to have something similar to a stable and solid playable game. So, here a first overview of the logic Metin2 has been using for more than 15 years: 123's client starts wielding the sword and sends to the server the HEADER_CG_CHARACTER_MOVE with argument FUNC_COMBO Server broadcasts the packet to all the nearby clients All the clients receive the packet and show 123 wielding the sword 123's client hits 456 pushing it down on a 10m distance and sends the HEADER_CG_ATTACK packet to the server, saying it hit 456 to then process the dmg During this step, the server will give "ownership" to 123 of 456 (establishing that 123 is attacking 456) It will send all the clients the info that 123 is the only player that can push and edit the position of 456 123's client will gain the coords at which 456 will arrive after standing up and keeps them in memory Meanwhile, in 456's client, 123 eventually will hit 456 and it will push him down on a 10m distance After a maximum of 300ms, 123's client will send the position of all the entities it has "ownership" to the server, with the HEADER_CG_SYNC_POSITION packet, making few controls and broadcast the new position to all the clients, even 456's All the clients will pull the characters involved to the indicated coords, which can be corrected or not This process starts forcing the movement and eventually the KNOCK_BACK of the characters to the coords indicated by 123 Eventually, when 456 ends the STANDUP animation, the client will send the HEADER_CG_CHARACTER_MOVE packet with argument FUNC_WAIT communicating again the definitive position of 456 to all the clients This flow has the advantage that all the clients process locally all the in-between actions, therefore it looks "fast paced", just because the server rarely intervenes and supposedly the clients start from the same conditions and they arrive at the same results. One of the biggest con, though, is that the server has very little info about the real state of the game, therefore it can do very little to do something about malicious declaration by one client. Let's get more into it. Chapter 1.1: Design problems and bugs in the code First critic by the system designed by YMIR is that, as already said above, the server has not much information on the state of the clients. I wanna point out: The server only knows the initial position of the players at the start of the flow: before 123 attacks and 456 gets pushed The server will be notified about the new position of 123 and 456 only at the end of the HEADER_CG_SYNC_POSITION packet, which will immediately move the character to the indicated coords Everything that happens in-between these 2 moments is unknown to the server and can't perform any meaningful control on the attacks or coords where 456 gets pushed For example, the server could see 456 to the initial coords, but on the client's side it already moved 4 meters away from the third sword hit and when the final knock back arrives, the server will see a potential illegal movement (third sword hit + fourth) The server, during this process, can only see two states: initial and final, making it completely stupid. Taking aside the security flaw - that we saw paid very well during these years - an acceptable solution would be if the process would be consistent and functional. Unfortunately, it's not like that, at all. The first problem that it inevitably occurs is that, if for some reason, the initial states of the clients are not 100% the same, they will compute different movements, animations and angles. In this picture we can see how a miniscule difference on the B's initial position can change its final destination, rendering the interaction between A and B, kind of interesting, where you can see getting damaged while thinking to be safe. Despite thinking this is a remote situation, it's actually pretty common. Remember that first, HEADER_CG_CHARACTER_MOVE packet is sent first, with argument FUNC_COMBO to communicate the beginning of a combo and eventually further attacks and the new position. If the characters are very close to one another, the attacker might send HEADER_CG_CHARACTER_MOVE, HEADER_CG_ATTACK and HEADER_CG_SYNC_POSITION before that who gets hit even started the attack animation, forcing a little movement and a KOCK_BACK before the sword hits the character in the victim's client. It's the inevitable that until the attacker doesn't send HEADER_CG_SYNC_POSITION, the states that the players see could differ massively and when it actually arrives to the server to align everything, the players find themselves sucked, pushed or slid in positions where they didn't think to be, giving the PvP that Metin2's touch we all (don't) love. More players interact with each other, the more the imprecision rises and the more drastic the forced movements have to be made to reset the synchrony. To make the clients reactive, the developers decided to force the movements and the KNOCK_BACKs before the packet HEADER_CG_SYNC_POSITION is sent and broadcasted: In the function void CPythonPlayerEventHandler::OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket) the synchronization is forced with rkActorVictim.TEMP_Push(kVictim.m_lPixelX, kVictim.m_lPixelY); All good for now, if it wasn't for the fact that the coords synchronized are obtained from rkActorVictim.NEW_GetLastPixelPositionRef a function, which calls GetBlendingPosition that does a simple thing: void CActorInstance::GetBlendingPosition(TPixelPosition * pPosition) { if (m_PhysicsObject.isBlending()) { m_PhysicsObject.GetLastPosition(pPosition); pPosition->x += m_x; pPosition->y += m_y; pPosition->z += m_z; } else { pPosition->x = m_x; pPosition->y = m_y; pPosition->z = m_z; } } Basically, it returns the current coords if the character is standing still, or the current coords + m_PhysicsObject.GetLastPosition(pPosition); if the character is getting pushed. This mystic class called CPhysicsObject is responsible of the movements that occur on Metin. It gets told how much an entity is pushed, in what direction and the time of it, to then move the entity. The bug I found was that using m_PhysicsObject.GetLastPosition(pPosition), which returns the whole movement that the entity has to make, GetBlendingPosition doesn't return the final position of the entity, but the current coords + the movement. Despite this looks correct, it isn't when this function is called after the entity moved a little. In that case the returned value will be a little bit further of the destination initially expected. Chapter: Consideration on some derived mechanics (fly, rolling dagger skill) When eventually the positions between the two clients are synchronized, the function that processes the packet HEADER_CG_SYNC_POSITION void CActorInstance::__Push(int x, int y) { if (IsResistFallen()) return; const D3DXVECTOR3& c_rv3Src=GetPosition(); const D3DXVECTOR3 c_v3Dst=D3DXVECTOR3(x, -y, c_rv3Src.z); const D3DXVECTOR3 c_v3Delta=c_v3Dst-c_rv3Src; const int LoopValue = 100; const D3DXVECTOR3 inc=c_v3Delta / LoopValue; D3DXVECTOR3 v3Movement(0.0f, 0.0f, 0.0f); IPhysicsWorld* pWorld = IPhysicsWorld::GetPhysicsWorld(); if (!pWorld) { return; } for(int i = 0; i < LoopValue; ++i) { if (pWorld->isPhysicalCollision(c_rv3Src + v3Movement)) { ResetBlendingPosition(); return; } v3Movement += inc; } SetBlendingPosition(c_v3Dst); const TPixelPosition& kPPosLast2 = NEW_GetLastPixelPositionRef(); if (!IsUsingSkill()) { int len=sqrt(c_v3Delta.x*c_v3Delta.x+c_v3Delta.y*c_v3Delta.y); if (len>150.0f) { InterceptOnceMotion(CRaceMotionData::NAME_DAMAGE_FLYING); PushOnceMotion(CRaceMotionData::NAME_STAND_UP); } } } uses the function void SetBlendingPosition(const TPixelPosition & c_rPosition, float fBlendingTime = 1.0f) which internally says to the client to make a movement and distribute it on a second timespan. During this time, the character enters a state - __IsSyncing() - in which it cannot move at all if not using an ability. This creates that phenomenon called "Fly", where the character slowly slides and can't either move or attack. Basically the Fly is the attempt, from the client, to synchronize his state with other clients. This forces the character in a slow movement in his position and assures that the state can't be changed, blocking every action. If you want my opinion, it's ok for a sword hit to block a character, to give the possibility to perform a physical combo. This is though a mechanic that should be controlled by the developers who, for example, put half a second of "stun" to the hits instead of being a result of the synchronization process with the other clients. More fascinating is the bug with the function GetBlendingPosition: during the execution of the Rolling Dagger skill, if the player is already in a state of IsPushing (it's getting pushed by the fourth sword hit) during the execution of the function OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket) a new TEMP_Push will be performed, with the coordinates that, presumably, represent the point of arrival of the character. If this is true for the first hit of the skill, during the second, the victim will already be moved a little further. At this point, the coords returned by GetBlendingPosition are not the ones of the fourth sword hit but that plus something. The player will be then moved a little bit further and another KNOCK_BACK animation is triggered. I don't know if the developers deliberately introduced this dynamic, or if it was a happy accident, but once again I believe that if an ability like Rolling Dagger can bounce a player, this should be coded with specific parameters (external force? refresh push?), but definitely not with an interaction not so visible and counterintuitive that uses a logic most probably bugged. 2. Challenges found with an highly customized revision of Metin2 If you are people who intend to do significant changes to, maybe the skill system, pvp or want to develop a monitoring system for hacks and cheats server side, the current state makes an hard work even harder. The server cannot contribute much to the management of all the states and this is a problem for all those systems based on the precision of the characters' positions during the interactions of the player with the game. Every attempt to make the client more reactive and precise, clashes inevitably with mechanics born from weird and casual interactions of the entities in the client like the "Fly"'s case or Rolling Dagger's that, while BUGS, are still part of the game for years, therefore they must be preserved or emulated 3. My approach on how to resolve the lacks of the implementation done in 2009 My implementation is strongly linked to UniversalElements files (my server) therefore are not 1:1 applicable to most of the existing servers. I will not post much code (it's not a release) but I will describe how to arrive to a similar result and the logic who brought me to determine my choices. Briefly, short said it will be the following: Expansion of the packet HEADER_CG_ATTACK to better describe the action Broadcast of HEADER_CG_ATTACK of all the clients in real time, to fix immediately the state server-side state implementation emulating the movements of the entities in real time Usage of HEADER_CG_SYNC_POSITION only to rollback the state in case wrong info sent from a client are present sent (to cancel an illegal action) Fix of GetBlendingPosition These simple edits look foregone, so much that the only answer to the question "why was it not designed like this from the beginning" was that, at the time, the servers were not able to maintain such an updated state and the internet connections were not enough for large packets used to the frequency of the HEADER_CG_ATTACK one. Therefore, the new logic will appear like this: 123's client starts wielding and sends the server the HEADER_CG_CHARACTER_MOVE packet with argument FUNC_COMBO Server broadcasts the packet to all the clients nearby All the clients receive the packet and show 123 wielding the sword 123's client hit 456 pushing it on a distance of 10 and send the HEADER_CG_ATTACK packet to the server, saying it hit 456 so that it can process the dmg The packet contains info like 456's initial position (with double precision to be exact to the millimeter), timestamp of the attack, which part of the combo generated the hit, duration of the movement in ms The server validates the packet HEADER_CG_ATTACK, that processes the damage and broadcasts the information to the clients Furthermore the server initializes the emulation of the movement from the initial coord to the 456 final's one. Meanwhile in 456's client, 123 eventually will hit 456 making it fall at 10m of distance When 456's client receives the HEADER_CG_ATTACK packet broadcasted by 123, it corrects the internal state whit the new on-the-fly info For example if the client already processed the movement and the KNOCK_BACK, the trajectory will be corrected with the coords validated by the server So, the synchronization corrects the local execution of the client and overwrites it (whether or not the packet HEADER_CG_ATTACK arrives before or after the sword hit in the observer's clients) The big difference though, is that the server keeps an updated state of every entity at every attack and during the execution of the movement. It becomes then able to perform checks, edits and corrections on the data coming from the attacker with high precision. To give an idea to the edits, in the server I added a state that can be executed in parallel in FCM.cpp // Update void CFSM::Update() { // Check New State if(m_pNewState) { // Execute End State m_pCurrentState->ExecuteEndState(); // Set New State m_pCurrentState = m_pNewState; m_pNewState = 0; // Execute Begin State m_pCurrentState->ExecuteBeginState(); } // Check New State if(m_pNewConcurrentState) { // Execute End State if (m_pConcurrentState) m_pConcurrentState->ExecuteEndState(); // Set New State m_pConcurrentState = m_pNewConcurrentState; m_pNewConcurrentState = 0; // Execute Begin State m_pConcurrentState->ExecuteBeginState(); } if (bStopConcurrent && m_pConcurrentState) { // Execute End State m_pConcurrentState->ExecuteEndState(); m_pConcurrentState = 0; bStopConcurrent = false; } // Execute State m_pCurrentState->ExecuteState(); if (m_pConcurrentState) { m_pConcurrentState->ExecuteState(); } } This enables me to use a fourth state in the CHARACTER class CHARACTER::CHARACTER() { m_stateIdle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateIdle, &CHARACTER::EndStateEmpty); m_stateMove.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateMove, &CHARACTER::EndStateEmpty); m_stateBattle.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateBattle, &CHARACTER::EndStateEmpty); m_stateSyncing.Set(this, &CHARACTER::BeginStateEmpty, &CHARACTER::StateSyncing, &CHARACTER::EndStateEmpty); Initialize(); } void CHARACTER::StateSyncing() { if (IsStone() || IsDoor()) { StopConcurrentState(); return; } DWORD dwElapsedTime = get_dword_time() - m_dwSyncStartTime; float fRate = (float) dwElapsedTime / (float) m_dwSyncDuration; if(fRate > 1.0f) fRate = 1.0f; int x = (int) ((float) (m_posDest.x - m_posStart.x) * fRate + m_posStart.x); int y = (int) ((float) (m_posDest.y - m_posStart.y) * fRate + m_posStart.y); Sync(x, y); if(1.0f == fRate) { StopConcurrentState(); } } /////////////////// ////// To use to gradually "move" the entity on the desired position while it can do whatever it wants (to use when receiving HEADER_CG_ATTACK) bool CHARACTER::BlendSync(long x, long y, unsigned int unDuration) { // TODO distance check required // No need to go the same side as the position (automatic success) if(GetX() == x && GetY() == y) return false; m_posDest.x = m_posStart.x = GetX(); m_posDest.y = m_posStart.y = GetY(); m_posDest.x = x; m_posDest.y = y; m_dwSyncStartTime = get_dword_time(); m_dwSyncDuration = unDuration; m_dwStateDuration = 1; ConcurrentState(m_stateSyncing); return true; } The new TPacketCGAttack will present iself like this typedef struct command_attack { BYTE bHeader; BYTE bType; DWORD dwVID; BOOL bPacket; LONG lSX; LONG lSY; LONG lX; LONG lY; float fSyncDestX; float fSyncDestY; DWORD dwBlendDuration; DWORD dwComboMotion; DWORD dwTime; } TPacketCGAttack; with its counter part TPacketGCAttack typedef struct packet_attack { BYTE bHeader; BYTE bType; DWORD dwAttacakerVID; DWORD dwVID; BOOL bPacket; LONG lSX; LONG lSY; LONG lX; LONG lY; float fSyncDestX; float fSyncDestY; DWORD dwBlendDuration; } TPacketGCAttack; Client side I fixed the inherent logic to GetBlendingPosition and all his variants. To resolve it we must pass to the function that initializes it, not only the displacement, but even the final position. void CPhysicsObject::SetLastPosition(const TPixelPosition& c_rPosition, const TPixelPosition& c_rDeltaPosition, float fBlendingTime) { m_v3FinalPosition.x = float(c_rPosition.x + c_rDeltaPosition.x); m_v3FinalPosition.y = float(c_rPosition.y + c_rDeltaPosition.y); m_v3FinalPosition.z = float(c_rPosition.z + c_rDeltaPosition.z); m_v3DeltaPosition.x = float(c_rDeltaPosition.x); m_v3DeltaPosition.y = float(c_rDeltaPosition.y); m_v3DeltaPosition.z = float(c_rDeltaPosition.z); m_xPushingPosition.Setup(0.0f, c_rDeltaPosition.x, fBlendingTime); m_yPushingPosition.Setup(0.0f, c_rDeltaPosition.y, fBlendingTime); } void CPhysicsObject::GetFinalPosition(TPixelPosition* pPosition) { pPosition->x = (m_v3FinalPosition.x); pPosition->y = (m_v3FinalPosition.y); pPosition->z = (m_v3FinalPosition.z); } void CPhysicsObject::GetDeltaPosition(TPixelPosition* pPosition) { pPosition->x = (m_v3DeltaPosition.x); pPosition->y = (m_v3DeltaPosition.y); pPosition->z = (m_v3DeltaPosition.z); } and fixing the function void CActorInstance::GetBlendingPosition(TPixelPosition * pPosition) { if (m_PhysicsObject.isBlending()) { m_PhysicsObject.GetFinalPosition(pPosition); } else { GetPixelPosition(pPosition); } } The management of the new HEADER_CG_CHARACTER_ATTACK will present itself like this: bool CPythonNetworkStream::RecvCharacterAttackPacket() { TPacketGCAttack kPacket; if (!Recv(sizeof(TPacketGCAttack), &kPacket)) { Tracen("CPythonNetworkStream::RecvCharacterAttackPacket - PACKET READ ERROR"); return false; } if (kPacket.lX && kPacket.lY) { __GlobalPositionToLocalPosition(kPacket.lX, kPacket.lY); } __GlobalPositionToLocalPosition(kPacket.lSX, kPacket.lSY); TPixelPosition tSyncPosition = TPixelPosition{ kPacket.fSyncDestX, kPacket.fSyncDestY, 0 }; m_rokNetActorMgr->AttackActor(kPacket.dwVID, kPacket.dwAttacakerVID, kPacket.lX, kPacket.lY, tSyncPosition, kPacket.dwBlendDuration); return true; } void CNetworkActorManager::AttackActor(DWORD dwVID, DWORD dwAttacakerVID, LONG lDestPosX, LONG lDestPosY, const TPixelPosition& k_pSyncPos, DWORD dwBlendDuration) { std::map<DWORD, SNetworkActorData>::iterator f = m_kNetActorDict.find(dwVID); if (m_kNetActorDict.end() == f) { return; } SNetworkActorData& rkNetActorData = f->second; if (k_pSyncPos.x && k_pSyncPos.y) { CInstanceBase* pkInstFind = __FindActor(rkNetActorData); if (pkInstFind) { const bool bProcessingClientAttack = pkInstFind->ProcessingClientAttack(dwAttacakerVID); pkInstFind->ServerAttack(dwAttacakerVID); // if already blending, update if (bProcessingClientAttack && pkInstFind->IsPushing() && pkInstFind->GetBlendingRemainTime() > 0.15) { pkInstFind->SetBlendingPosition(k_pSyncPos, pkInstFind->GetBlendingRemainTime()); } else { // otherwise sync //pkInstFind->SCRIPT_SetPixelPosition(k_pSyncPos.x, k_pSyncPos.y); pkInstFind->NEW_SyncPixelPosition(k_pSyncPos, dwBlendDuration); } } rkNetActorData.SetPosition(long(k_pSyncPos.x), long(k_pSyncPos.y)); } } ////////////////////// //// Semplified __Push and called only by AttackActor void CActorInstance::__Push(const TPixelPosition& c_rkPPosDst, unsigned int unDuration) { DWORD dwVID = GetVirtualID(); Tracenf("VID %d SyncPixelPosition %f %f", dwVID, c_rkPPosDst.x, c_rkPPosDst.y); if (unDuration == 0) unDuration = 1000; const D3DXVECTOR3& c_rv3Src = GetPosition(); const D3DXVECTOR3 c_v3Delta = c_rkPPosDst - c_rv3Src; SetBlendingPosition(c_rkPPosDst, float(unDuration) / 1000); if (!IsUsingSkill() && !IsResistFallen()) { int len = sqrt(c_v3Delta.x * c_v3Delta.x + c_v3Delta.y * c_v3Delta.y); if (len > 150.0f) { InterceptOnceMotion(CRaceMotionData::NAME_DAMAGE_FLYING); PushOnceMotion(CRaceMotionData::NAME_STAND_UP); } } } ////////////////////// // To understand if the client already started processing the attack or it must be initialized with a new sync_pixelposition: void CInstanceBase::ServerAttack(DWORD dwVID) { m_GraphicThingInstance.ServerAttack(dwVID); } bool CInstanceBase::ProcessingClientAttack(DWORD dwVID) { return m_GraphicThingInstance.ProcessingClientAttack(dwVID); } // client attack decreases the count void CActorInstance::ClientAttack(DWORD dwVID) { if (m_mapAttackSync.find(dwVID) == m_mapAttackSync.end()) { m_mapAttackSync.insert(std::make_pair(dwVID, -1)); } else { if (m_mapAttackSync[dwVID] == 1) { m_mapAttackSync.erase(dwVID); return; } m_mapAttackSync[dwVID]--; } } // server attack increases void CActorInstance::ServerAttack(DWORD dwVID) { if (m_mapAttackSync.find(dwVID) == m_mapAttackSync.end()) { m_mapAttackSync.insert(std::make_pair(dwVID, 1)); } else { if (m_mapAttackSync[dwVID] == -1) { m_mapAttackSync.erase(dwVID); return; } m_mapAttackSync[dwVID]++; } } bool CActorInstance::ProcessingClientAttack(DWORD dwVID) { return m_mapAttackSync.find(dwVID) != m_mapAttackSync.end() && m_mapAttackSync[dwVID] < 0; } // bool CActorInstance::ServerAttackCameFirst(DWORD dwVID) { return m_mapAttackSync.find(dwVID) != m_mapAttackSync.end() && m_mapAttackSync[dwVID] > 0; } New management of the hit which processes locally the movement, collect the infos, and send the packet to the server: struct BlendingPosition { D3DXVECTOR3 source; D3DXVECTOR3 dest; float duration; }; void CActorInstance::__ProcessDataAttackSuccess(const NRaceData::TAttackData & c_rAttackData, CActorInstance & rVictim, const D3DXVECTOR3 & c_rv3Position, UINT uiSkill, BOOL isSendPacket) { if (NRaceData::HIT_TYPE_NONE == c_rAttackData.iHittingType) return; InsertDelay(c_rAttackData.fStiffenTime); BlendingPosition sBlending; memset(&sBlending, 0, sizeof(sBlending)); sBlending.source = rVictim.NEW_GetCurPixelPositionRef(); if (__CanPushDestActor(rVictim) && c_rAttackData.fExternalForce > 0.0f) { const bool bServerAttackAlreadyCame = rVictim.ServerAttackCameFirst(GetVirtualID()); rVictim.ClientAttack(GetVirtualID()); if (!bServerAttackAlreadyCame) { __PushCircle(rVictim); // VICTIM_COLLISION_TEST const D3DXVECTOR3& kVictimPos = rVictim.GetPosition(); rVictim.m_PhysicsObject.IncreaseExternalForce(kVictimPos, c_rAttackData.fExternalForce); rVictim.GetBlendingPosition(&(sBlending.dest)); sBlending.duration = rVictim.m_PhysicsObject.GetRemainingTime(); // VICTIM_COLLISION_TEST_END } } // Invisible Time rVictim.m_fInvisibleTime = CTimer::Instance().GetCurrentSecond() + (c_rAttackData.fInvisibleTime - __GetInvisibleTimeAdjust(uiSkill, c_rAttackData)); // Stiffen Time rVictim.InsertDelay(c_rAttackData.fStiffenTime); // Hit Effect D3DXVECTOR3 vec3Effect(rVictim.m_x, rVictim.m_y, rVictim.m_z); // #0000780: [M2KR] ¼ö·æ Ÿ°Ý±¸ ¹®Á¦ extern bool IS_HUGE_RACE(unsigned int vnum); if (IS_HUGE_RACE(rVictim.GetRace())) { vec3Effect = c_rv3Position; } const D3DXVECTOR3 & v3Pos = GetPosition(); float fHeight = D3DXToDegree(atan2(-vec3Effect.x + v3Pos.x,+vec3Effect.y - v3Pos.y)); // 2004.08.03.myevan.ºôµùÀ̳ª ¹®ÀÇ °æ¿ì Ÿ°Ý È¿°ú°¡ º¸ÀÌÁö ¾Ê´Â´Ù if (rVictim.IsBuilding()||rVictim.IsDoor()) { D3DXVECTOR3 vec3Delta=vec3Effect-v3Pos; D3DXVec3Normalize(&vec3Delta, &vec3Delta); vec3Delta*=30.0f; CEffectManager& rkEftMgr=CEffectManager::Instance(); if (m_dwBattleHitEffectID) rkEftMgr.CreateEffect(m_dwBattleHitEffectID, v3Pos+vec3Delta, D3DXVECTOR3(0.0f, 0.0f, 0.0f)); } else { if(c_rAttackData.isEnemy == 0) { if(rVictim.IsEnemy() || rVictim.IsPC() || rVictim.IsBoss() || rVictim.IsStone()) { return; } } else { CEffectManager& rkEftMgr=CEffectManager::Instance(); if (m_dwBattleHitEffectID) rkEftMgr.CreateEffect(m_dwBattleHitEffectID, vec3Effect, D3DXVECTOR3(0.0f, 0.0f, fHeight)); if (m_dwBattleAttachEffectID) rVictim.AttachEffectByID(0, NULL, m_dwBattleAttachEffectID); } } if (rVictim.IsBuilding()) { // 2004.08.03.ºôµùÀÇ °æ¿ì Èçµé¸®¸é ÀÌ»óÇÏ´Ù } else if (rVictim.IsStone() || rVictim.IsDoor()) { __HitStone(rVictim); } else { /////////// // Motion bool ForceHitGOOD = rVictim.IsPC() && (rVictim.IsKnockDown() || rVictim.__IsStandUpMotion()); if (NRaceData::HIT_TYPE_GOOD == c_rAttackData.iHittingType || (TRUE == rVictim.IsResistFallen())) { __HitGood(rVictim); } else if (NRaceData::HIT_TYPE_GREAT == c_rAttackData.iHittingType) { if(c_rAttackData.isEnemy == 0) { if(rVictim.IsEnemy() || rVictim.IsPC() || rVictim.IsBoss() || rVictim.IsStone()) { return; } else { __HitGreate(rVictim, uiSkill, c_rAttackData.isEnemy); } } else { __HitGreate(rVictim, uiSkill, c_rAttackData.isEnemy); } } else { TraceError("ProcessSucceedingAttacking: Unknown AttackingData.iHittingType %d", c_rAttackData.iHittingType); } } __OnHit(uiSkill, rVictim, isSendPacket, &sBlending); } void CPythonPlayerEventHandler::OnHit(UINT uSkill, CActorInstance& rkActorVictim, BOOL isSendPacket, BlendingPosition* sBlending) { DWORD dwVIDVictim=rkActorVictim.GetVirtualID(); CPythonCharacterManager::Instance().AdjustCollisionWithOtherObjects(&rkActorVictim); BlendingPosition kBlendingPacket; memset(&kBlendingPacket, 0, sizeof(kBlendingPacket)); kBlendingPacket.source = rkActorVictim.NEW_GetCurPixelPositionRef(); if (rkActorVictim.IsPushing()) { kBlendingPacket.dest = rkActorVictim.NEW_GetLastPixelPositionRef(); kBlendingPacket.duration = sBlending->duration; } // Update Target CPythonPlayer::Instance().SetTarget(dwVIDVictim, FALSE); // Update Target //#define ATTACK_TIME_LOG #ifdef ATTACK_TIME_LOG static std::map<DWORD, float> s_prevTimed; float curTime = timeGetTime() / 1000.0f; bool isFirst = false; if (s_prevTimed.end() == s_prevTimed.find(dwVIDVictim)) { s_prevTimed[dwVIDVictim] = curTime; isFirst = true; } float diffTime = curTime-s_prevTimed[dwVIDVictim]; if (diffTime < 0.1f && !isFirst) { TraceError("ATTACK(SPEED_HACK): %.4f(%.4f) %d", curTime, diffTime, dwVIDVictim); } else { TraceError("ATTACK: %.4f(%.4f) %d", curTime, diffTime, dwVIDVictim); } s_prevTimed[dwVIDVictim] = curTime; #endif CPythonNetworkStream& rkStream=CPythonNetworkStream::Instance(); rkStream.SendAttackPacket(uSkill, dwVIDVictim, isSendPacket, kBlendingPacket); } bool CPythonNetworkStream::SendAttackPacket(UINT uMotAttack, DWORD dwVIDVictim, BOOL bPacket, BlendingPosition& sBlending) { NANOBEGIN if (!__CanActMainInstance()) return true; CPythonCharacterManager& rkChrMgr = CPythonCharacterManager::Instance(); CInstanceBase* pkInstMain = rkChrMgr.GetMainInstancePtr(); #ifdef ATTACK_TIME_LOG static DWORD prevTime = timeGetTime(); DWORD curTime = timeGetTime(); TraceError("TIME: %.4f(%.4f) ATTACK_PACKET: %d TARGET: %d", curTime/1000.0f, (curTime-prevTime)/1000.0f, uMotAttack, dwVIDVictim); prevTime = curTime; #endif TPacketCGAttack kPacketAtk; kPacketAtk.header = HEADER_CG_ATTACK; kPacketAtk.bType = uMotAttack; kPacketAtk.dwVictimVID = dwVIDVictim; kPacketAtk.bPacket = bPacket; kPacketAtk.lX = (long)sBlending.dest.x; kPacketAtk.lY = (long)sBlending.dest.y; kPacketAtk.lSX = (long)sBlending.source.x; kPacketAtk.lSY = (long)sBlending.source.y; kPacketAtk.fSyncDestX = sBlending.dest.x; // sources and dest are normalized with both coordinates positive // since fSync are ment to be broadcasted to other clients, the Y has to preserve the negative coord kPacketAtk.fSyncDestY = -sBlending.dest.y; kPacketAtk.dwBlendDuration = (unsigned int) (sBlending.duration *1000); kPacketAtk.dwComboMotion = pkInstMain->GetComboMotion(); kPacketAtk.dwTime = ELTimer_GetServerMSec(); if (kPacketAtk.lX && kPacketAtk.lY) __LocalPositionToGlobalPosition(kPacketAtk.lX, kPacketAtk.lY); __LocalPositionToGlobalPosition(kPacketAtk.lSX, kPacketAtk.lSY); if (!SendSpecial(sizeof(kPacketAtk), &kPacketAtk)) { Tracen("Send Battle Attack Packet Error"); return false; } return SendSequence(); } To preserve the behavior of Rolling Dagger, that was based on a bug, it will be necessary to: //// InstanceBase.cpp /// void CInstanceBase::StateProcess() if (eFunc & FUNC_SKILL) .... // NOTICE HERE THE 1!!!!! NEW_UseSkill(1, eFunc & FUNC_SKILL - 1, uArg&0x0f, (uArg>>4) ? true : false); //Tracen("°¡±õ±â ¶§¹®¿¡ ¿öÇÁ °ø°Ý"); } } break; /// void CInstanceBase::MovementProcess() // TODO get skill vnum // NOTICE HERE THE 1!!!!!!!!!! NEW_UseSkill(1, m_kMovAfterFunc.eFunc & FUNC_SKILL - 1, m_kMovAfterFunc.uArg & 0x0f, (m_kMovAfterFunc.uArg >> 4) ? true : false); .... // ActorInstaceBattle.cpp void CActorInstance::__HitGreate(CActorInstance& rVictim, unsigned int uiSkill, bool isEnemy) { // DISABLE_KNOCKDOWN_ATTACK // !!!! uiSkill if (!uiSkill && rVictim.IsKnockDown()) return; if (rVictim.__IsStandUpMotion()) return; // END_OF_DISABLE_KNOCKDOWN_ATTACK float fRotRad = D3DXToRadian(GetRotation()); float fVictimRotRad = D3DXToRadian(rVictim.GetRotation()); D3DXVECTOR2 v2Normal(sin(fRotRad), cos(fRotRad)); D3DXVECTOR2 v2VictimNormal(sin(fVictimRotRad), cos(fVictimRotRad)); .... and add external force to the msa of the animation to give Rolling Dagger the ability to push therefore making the enemy jump. This way even without the daggers combo it will be possible to do a double or triple rolling. If you wanna make the daggers combo necessary, you can create a new property in the msa like "refresh_displacement" that says to the launcher, in case of a shift happening, the animation must refresh the shifting and knock_back. Chapter 3.1: Result After having applied these structural changes and implementations, you will obtain a revision where the clients will process attacks and movements client side to gain immediate reactivity and the server will perform a validation and synchronization of the states in real-time. The server will act as a referee, while the clients will just execute since they will give the server all the necessary infos to referee. This approach connects the fluidity of the execution of the client with a millimetric real time synchronization of all the clients connected, presenting a more reactive and precise pvp experience. This approach also allows for multiple entities to control the push/movements of another entity at the same time. Therefore it would be possible to do stuff like performing a rolling dagger after the combo of one of your guildmates, or pushing another character already in mid-air. Chapter 3.2: Benefits Other than the precision and reactivity of the synchronization, this approach gives complete control to the server on the actions of the clients say to have done: timestamp, comboArgument manage to detect speed and combohack, starting and final coords are able to detect antifly and rangehacks etc Therefore, in case of malicious intents, the server has the ability to reset to everyone the initial positions. Moreover these changes remove many "obscure" aspects of the combat system. Fly, Rolling Dagger, Falling duration, Flame Strike etc. will all be parameters that the developers and maintainers can manage and orchestrate, not something that happnes because it happens. The Metin2 combat system will, finally, become configurable, not something given for granted and unchangeable. Chapter 4: Possible expansions At this point, changes and integrations are over. Serverside anticheat systems, implementation of a collisions' engine and server side animation to reproduce, on the server, the client's actions, complete removal of the fly.. everything that includes the interaction and management of the synchronization system of the characters between clients and state of the game are finally possible.
  4. Hello, This tutorial is intended to help beginner who don’t know how to configure regeneration files for specific maps. It may seem obvious to some but some don’t know how it works completely. That is, choose the correct location of your Monsters, Metins, Bosses and NPC’s as well as their coordinates, direction and spawn time. Information. The tutorial presents the 4 files (regen.txt, boss.txt, stone.txt, npc.txt) at the same time because they work particular the same way. These files are located in your server files at “share/locale/country/map/name_of_your_map/” How to get started? First, you need access to your server file directory. We recommend using WinSCP as it open source as in globally used among everyone. The map folder which you will configure. I. How to configure spawns? II. Manage group.txt and group_group.txt Special thanks to @Owsap for his help ! A category Questions and Answers is available. If you have a problem or a question, feel free to post a request!
  5. Download Metin2 Download How to Update Client src c++20 Thanks lot of @ Mali First, we open the client source with visual studio 2022. Select all projects and right click on them -> properties -> General -> c++ language standard -> Here we choose c++20. [Hidden Content] need to make some step from this topic HowToc++20 If you don't understand something, you can ask your questions here.
  6. 1. Faster and cleaner boot: 2. Alias for shortcuts such as paths and executions. UPDATED! 12.10.2021 3. Disable Send Mail - Credits to: @ martysama0134 (Not waiting after Send Mail Service on boot, it will reduce your boot time with 10 more seconds) Hope it's usefull. It was for me Hope you have a great day! Shahin
  7. In this topic I would like to teach you how to install MariaDB instead Oracle MySQL and why. Why prefer MariaDB instead MySQL? MariaDB it's a fork of MySQL and it is compatible with MySQL. Forecasts indicate that the MariaDB fork replace Oracle MySQL Server as rdbms. MariaDB have more features for performance and data integrity. One of this features is Aria storage engine. Aria is the upgrade of old MyISAM storage engine used on our Metin2 Private server. This old storage engine have some bugs and it isn't ACID like InnoDB. An upgrade of InnoDB is the fork XtraDB, used on MariaDB and Percona. So if you don't want/can't convert your metin2 tables from MyISAM to InnoDB or XtraDB (the best choice) you can use the similar storage engine: Aria. How to install MariaDB on FreeBSD. Administrate MariaDB it's like MySQL. The service is mysql-server and the path of MariaDB home directory is /var/db/mysql, like classical Oracle MySQL server. Remove old MySQL server installation. If you have mysql-server installed you need to deinstall it: Make a security's backup of your mysql data directory: cd /var/db && tar -cvJf mysql.tar.xz mysql Make a real backup of your applicative db (metin2, website etc.) with dump. You can use the CLI utility mysqldump or GUI with Navicat (right click on db name->Dump SQL file->Structure and data). Stop Mysql-server with: service mysql-server stop Remove mysql directory: rm -rf /var/db/mysql Find the mysql version: mysql --version Deinstall mysql from ports: cd /usr/ports/databases/mysqlxx-server && make deinstall clean && cd /usr/ports/databases/mysqlxx-client && make deinstall clean Where xx is the version find on previous step. Install MariaDB This thread was written when the lastest version of MariaDB was 10.1 update your ports tree: portsnap fetch update Install MariaDB: cd /usr/ports/databases/mariadb101-server && make install clean if not present, enable mysql service: echo "mysql_enable=YES" >> /etc/rc.conf start mysql server: service mysql-server start Import your application databases (metin2, website etc.) from dump. Do not import from datafile! If you use navicat, remember to create a new connection for MariaDB instead MySQL Convert MyISAM to new storage engine For better performance and security of your data you need to convert all of your table with a new storage engine. You can choose a different storage engine for each table. I suggest you to choose between Aria (new MyISAM) or XtraDB (new InnoDB). Choose Aria to read fastest and low write (example item_proto, mob_proto, shop, shop_item) or FULLTEXT index feature (to search fastest on text culomn). Choose XtraDB to critical table with hight write frequency (example player, item, account). To convert from one storage engine to another you have two ways: Via GUI like Navicat (one table at time). Right click on the table -> Design Table->Options->Engine. Change and save. Via query: ALTER TABLE <table_name> ENGINE=<engine>; This solution don't exclude the necessity of db backups. Please scedule frequently backup of your applicative dbs from dump. See mysqldump utility. For much safety, do also a datafile backup of entire mysql data directory (/var/db/mysql). It's for emergency restore of all mysql if dump import don't work. Use datafile restore only for whole mysql directory and only if there aren't another solution with dump.
  8. How do I install the world editor? - created by @Raylee - - 1: Download the WorldEditor ReMIX: - 2: Create on your D:\ Drive a folder named "ymir work" and place the Downloadfiles there. - 3: Copy the following files from your client in the "ymir work" folder: - 3.1: It should look like that: - 4: Unpack the following files from your client and put it in your "ymir work" folder: - 4.1: If you unpack the files, be sure that you take the right folder! Example: pack\effect\ymir work\effect - 4.2: It should look like that: - 5: Create in your "tree"-folder a new folder named "shye" and in there a new folder named "officials". After you are done, copy all your .spt files from "tree" in your new "shye"->"officials" folder. - 5.1: It should look like that: - 5: If you want to see your character in the WorldEditor add following things. - 5.1: It should look like that: For rendering/refresh all objects + shadows: First press the "T" key to make sure your camera is alligned at the angle where it captures the whole world. Then press the "F6" key to re-render the minimap and shadows - 5: Create in your "tree"-folder a new folder named "shye" and in there a new folder named "officials". After you are done, copy all your .spt files from "tree" in your new "shye"->"officials" folder. - 5.1: It should look like that: This is not needed anymore.. I tested it actually and it worked fine without do the "shye" modification. Now you should have everything together to run the "WorldEditorRemix_MfcRelease_v34.exe"! Don't forget to take a look in the WorldEditor.txt textfile for further informations. You can read there a lot of useful informations.
  9. Hello, this is a random and unwanted tutorial for building the Metin2 source code with vcpkg. It was intended for new comers for sure, but it might get more complicated than it should be. If you remember the wiki pages about building the source and creating the Extern from scratch, this is the cleaned and updated 2020 method™ . This tutorial focuses doing changes on both some revisions that I have on the internet and the original leaked one. We will use vcpkg, but this tutorial also contains several fixes that are required to get a general source file to be built. Introduction vcpkg is a C++ package manager for Windows, Linux, Mac (and FreeBSD). It allows us to build any third party program required by any of our applications, and keep them updated. It also allows us to easily install anything new it might be required to our sources. In Metin2, this program fully replaces the old Extern directory. The tutorial is compatible for both Windows and FreeBSD, both client and server. This method also supports adding custom inclusions or libraries inside the vcpkg installation root, I'll explain more later on the topic. Why shoud you use this method? You want a quick and slimmer way of distributing your sources, the idea would be not distributing the Extern at all, and let people perform the installation of vcpkg in their PC. Keep all the third party components of Metin2 always up to date. You might get better performance and security enchanges. (Most commonly the Python update) You want to achieve static linking (removing extra DLL in your client or SO in the game). My client source only contain SpeedTreeRT and Granny2 dll files (which could be removed if you use static Granny 2.9 and build SpeedTreeRT from sources) Changes applied: Modify inclusions & libraries to use vcpkg. (No more Extern, for ever) Updated various files to use the last version from vcpkg. Changes old NANOBEGIN/NANOEND to the last SDK and fix some bugs that were generated by using Nanomites with debug builds. Cleaned up Makefile and enable static library usage (no more .so) Fixes some bugs or issues that you can get if you use the server under Windows. Compatibility and incompatibility I was not able to get libmariadb working with FreeBSD, it might have been due to the fact that I was compiling with a FreeBSD 64-bit application, in case something bad happens, I suggest you mitigate by dropping [Hidden Content]:(YOUR FREEBSD VERSION):amd64/latest/All/mariadb-connector-c-3.1.9.txz include and lib files inside (vcpkg directory)\installed\x86-freebsd. (FreeBSD recently updated their pkg to 3.1.9, it might work now) You can not build in 32-bit vcpkg under 64-bit (I was using a cross-toolchain and I never got it to work), get a 32-bit build machine. It might be possible to get multilib to build on FreeBSD, but I am not aware of any method that could be achieved under vcpkg. You need internet on your FreeBSD virtual machine or VPS, it also needs to be updated to a relavant version, any supported FreeBSD version is fine, check them at [Hidden Content] You need to use, at least, Windows 7 SP1 to build the Client (or Server). I do not reccomend using clang for building your source, I get some issues with Crypto++. Please report if cryptopp is working fine Windows prerequisites Get the last version of Visual Studio here: [Hidden Content] Choose the workload "C++ desktop application development" (or something similar) in Visual Studio installer. MAKE SURE TO INSTALL THE VISUAL STUDIO ENGLISH LANGUAGE PACK AS WELL, OR VCPKG WILL NOT WORK. YOU CAN INSTALL LANGUAGE PACK BY CLICKING THE "LANGUAGE PACK" OPTION IN VISUALSTUDIO INSTALLER. We can download Git from here [Hidden Content] and CMake from here [Hidden Content] FreeBSD Setup (Server only) This process must be executed only in the BUILD MACHINE, not in the place where the Server will be started. Type the following command: pkg install gcc makedepend gmake cmake git openssl ninja Then, type the following commands: cd (folder where we want to store vcpkg) git clone --depth=1 [Hidden Content] cd vcpkg ./bootstrap-vcpkg.sh -disableMetrics -useSystemBinaries ./vcpkg install devil cryptopp boost-system boost-container boost-unordered boost-algorithm boost-pool boost-math boost-lexical-cast Windows setup (Client and, optionally, Server) Under Windows, type the following commands: (Please note that you need to launch all this commands in an elevated/administrator command prompt) cd (folder where we want to store vcpkg) git clone --depth=1 [Hidden Content] cd vcpkg bootstrap-vcpkg -disableMetrics vcpkg integrate install vcpkg install devil:x86-windows-static cryptopp:x86-windows-static boost-system:x86-windows-static boost-container:x86-windows-static boost-unordered:x86-windows-static boost-algorithm:x86-windows-static lzo:x86-windows-static python2:x86-windows-static If you use the Type6 Snappy compression, also type the following command: vpckg install snappy:x86-windows-static If you want to build a Server under Windows, also type the following command: vcpkg install libmariadb:x86-windows-static openssl:x86-windows-static boost-pool:x86-windows-static boost-math:x86-windows-static boost-lexical-cast:x86-windows-static Inside your client source directory (where there's Metin2Client.sln) create a new folder called Extern (unfortunally some components cannot be distributed via vcpkg) and extract this pack inside that folder. Server: Common changes You need to follow the OpenSSL update to build the sources. You might also need to follow the char_skill compilation fix if you get some issue. This changes must be done inside game/src. Open cipher.h and modify the following line: encoder_->ProcessData((byte*)buffer, (const byte*)buffer, length); to: encoder_->ProcessData((CryptoPP::byte*)buffer, (const CryptoPP::byte*)buffer, length); and from: decoder_->ProcessData((byte*)buffer, (const byte*)buffer, length); to: decoder_->ProcessData((CryptoPP::byte*)buffer, (const CryptoPP::byte*)buffer, length); Open cipher.cpp and delete the following line: #include <cryptopp/cryptoppLibLink.h> Open utils.cpp and delete this lines if you have them: #ifdef WIN32 extern "C" void my_make_scrambled_password(char *to, const char *password, size_t pass_len); #endif and if you have this lines, change them: #ifdef WIN32 my_make_scrambled_password(hash_buf, tmp_pwd, strlen(tmp_pwd)); #else make_scrambled_password(hash_buf, tmp_pwd); #endif to: make_scrambled_password(hash_buf, tmp_pwd); Then, search if you have this lines: #ifndef SHA1_HASH_SIZE #define SHA1_HASH_SIZE 20 #endif if you have them, add this after those 3 lines: extern "C" { #include "sha1.h" } char *hexify(char * const result, const unsigned char *digest, const size_t size_result, size_t size_digest) { static const char * const hexchars = "0123456789ABCDEF"; char *result_pnt = result; if (size_digest <= (size_t) 0 || size_result <= (size_digest * (size_t) 2U)) { return NULL; } do { *result_pnt++ = hexchars[(*digest >> 4) & 0xf]; *result_pnt++ = hexchars[*digest & 0xf]; digest++; size_digest--; } while (size_digest > (size_t) 0U); *result_pnt = 0; return result; } // Implementation from commit 2db6b50c7b7c638104bd9639994f0574e8f4813c in Pure-ftp source. void make_scrambled_password(char scrambled_password[42], const char password[255]) { SHA1_CTX ctx; unsigned char h0[20], h1[20]; SHA1Init(&ctx); SHA1Update(&ctx, (unsigned char*)password, strlen(password)); SHA1Final(h0, &ctx); SHA1Init(&ctx); SHA1Update(&ctx, h0, sizeof h0); #ifdef HAVE_EXPLICIT_BZERO explicit_bzero(h0, strlen(password)); #else volatile unsigned char *pnt_ = (volatile unsigned char *) h0; size_t i = (size_t) 0U; while (i < strlen(password)) { pnt_[i++] = 0U; } #endif SHA1Final(h1, &ctx); *scrambled_password = '*'; hexify(scrambled_password + 1U, h1, 42, sizeof h1); } Download the following two files (sha1.c and sha1.h) and put them inside game/src folder. [Hidden Content] [Hidden Content] Open input_auth.cpp and if you have this lines, change them from: #ifdef __WIN32__ DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p, "SELECT PASSWORD('%s'),password,securitycode,social_id,id,status,availDt - NOW() > 0," "UNIX_TIMESTAMP(silver_expire)," "UNIX_TIMESTAMP(gold_expire)," "UNIX_TIMESTAMP(safebox_expire)," "UNIX_TIMESTAMP(autoloot_expire)," "UNIX_TIMESTAMP(fish_mind_expire)," "UNIX_TIMESTAMP(marriage_fast_expire)," "UNIX_TIMESTAMP(money_drop_rate_expire)," "UNIX_TIMESTAMP(create_time)" " FROM account WHERE login='%s'", szLogin); #else // @fixme138 1. PASSWORD('%s') -> %s 2. szPasswd wrapped inside mysql_hash_password(%s).c_str() DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p, "SELECT '%s',password,securitycode,social_id,id,status,availDt - NOW() > 0," "UNIX_TIMESTAMP(silver_expire)," "UNIX_TIMESTAMP(gold_expire)," "UNIX_TIMESTAMP(safebox_expire)," "UNIX_TIMESTAMP(autoloot_expire)," "UNIX_TIMESTAMP(fish_mind_expire)," "UNIX_TIMESTAMP(marriage_fast_expire)," "UNIX_TIMESTAMP(money_drop_rate_expire)," "UNIX_TIMESTAMP(create_time)" " FROM account WHERE login='%s'", mysql_hash_password(szPasswd).c_str(), szLogin); #endif to: DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p, "SELECT '%s',password,securitycode,social_id,id,status,availDt - NOW() > 0," "UNIX_TIMESTAMP(silver_expire)," "UNIX_TIMESTAMP(gold_expire)," "UNIX_TIMESTAMP(safebox_expire)," "UNIX_TIMESTAMP(autoloot_expire)," "UNIX_TIMESTAMP(fish_mind_expire)," "UNIX_TIMESTAMP(marriage_fast_expire)," "UNIX_TIMESTAMP(money_drop_rate_expire)," "UNIX_TIMESTAMP(create_time)" " FROM account WHERE login='%s'", mysql_hash_password(szPasswd).c_str(), szLogin); Inside this file, change this lines if you have them: #ifdef __WIN32__ DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p, "SELECT PASSWORD('%s'),password,securitycode,social_id,id,status,availDt - NOW() > 0," "UNIX_TIMESTAMP(silver_expire)," "UNIX_TIMESTAMP(gold_expire)," "UNIX_TIMESTAMP(safebox_expire)," "UNIX_TIMESTAMP(autoloot_expire)," "UNIX_TIMESTAMP(fish_mind_expire)," "UNIX_TIMESTAMP(marriage_fast_expire)," "UNIX_TIMESTAMP(money_drop_rate_expire)," "UNIX_TIMESTAMP(create_time)" " FROM account WHERE login='%s'", szLogin); #else // @fixme138 1. PASSWORD('%s') -> %s 2. szPasswd wrapped inside mysql_hash_password(%s).c_str() DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p, "SELECT '%s',password,securitycode,social_id,id,status,availDt - NOW() > 0," "UNIX_TIMESTAMP(silver_expire)," "UNIX_TIMESTAMP(gold_expire)," "UNIX_TIMESTAMP(safebox_expire)," "UNIX_TIMESTAMP(autoloot_expire)," "UNIX_TIMESTAMP(fish_mind_expire)," "UNIX_TIMESTAMP(marriage_fast_expire)," "UNIX_TIMESTAMP(money_drop_rate_expire)," "UNIX_TIMESTAMP(create_time)" " FROM account WHERE login='%s'", mysql_hash_password(szPasswd).c_str(), szLogin); #endif to: DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p, "SELECT '%s',password,securitycode,social_id,id,status,availDt - NOW() > 0," "UNIX_TIMESTAMP(silver_expire)," "UNIX_TIMESTAMP(gold_expire)," "UNIX_TIMESTAMP(safebox_expire)," "UNIX_TIMESTAMP(autoloot_expire)," "UNIX_TIMESTAMP(fish_mind_expire)," "UNIX_TIMESTAMP(marriage_fast_expire)," "UNIX_TIMESTAMP(money_drop_rate_expire)," "UNIX_TIMESTAMP(create_time)" " FROM account WHERE login='%s'", mysql_hash_password(szPasswd).c_str(), szLogin); Open the file libsql/AsyncSQL.cpp and change: fprintf(stdout, "AsyncSQL: connected to %s (reconnect %d)\n", m_stHost.c_str(), m_hDB.reconnect);[ in fprintf(stdout, "AsyncSQL: connected to %s\n", m_stHost.c_str()); Open the file libthecore/src/log.c and change: #ifndef __WIN32__ // log_level이 1 이상일 경우에는 테스트일 경우가 많으니 stdout에도 출력한다. if (log_level_bits > 1) { #endif in: // log_level이 1 이상일 경우에는 테스트일 경우가 많으니 stdout에도 출력한다. if (log_level_bits > 1) { also change this line, you will find it some lines after the last change: #ifndef __WIN32__ } #endif in: } Open the file libthecore/include/stdafx.h and delete //struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* and nanoseconds */ }; also delete this line: #define strtof(str, endptr) (float)strtod(str, endptr) If you have libserverkey, open libserverkey/RSACrypto.cpp and delete this lines: #ifdef _WIN32 #include <atlenc.h> #endif also ope nthe file libserverkey/RSACrypto.h and delete this lines: #ifdef _WIN32 // needed only for using openssl #pragma comment(lib, "libeay32") #endif Open libserverkey/CheckServerKey.h and change #ifdef _WIN32 #include <atlenc.h> #else #include "base64_ssl.h" #endif to: #include "base64_ssl.h" Also inside this file, modifiy this: #ifdef _WIN32 if (FALSE == Base64Decode(key.c_str(), key.size(), buf, &buflen)) { errorString = "base64 decode failed"; return false; } #else if (false == unbase64_ssl((unsigned char *)key.c_str(), key.size(), buf, buflen)) { errorString = "base64 decode failed"; return false; } buflen = strlen((const char *)buf); #endif to: if (false == unbase64_ssl((unsigned char *)key.c_str(), key.size(), buf, buflen)) { errorString = "base64 decode failed"; return false; } buflen = strlen((const char *)buf); Open db/src/CsvReader.h and change: #if _MSC_VER #include <hash_map> #else #include <map> #endif to: #include <boost/unordered_map.hpp> also change this: #if _MSC_VER typedef stdext::hash_map<std::string, size_t> NAME2INDEX_MAP; typedef stdext::hash_map<size_t, std::string> INDEX2NAME_MAP; #else typedef std::map<std::string, size_t> NAME2INDEX_MAP; typedef std::map<size_t, std::string> INDEX2NAME_MAP; #endif to this: typedef boost::unordered_map<std::string, size_t> NAME2INDEX_MAP; typedef boost::unordered_map<size_t, std::string> INDEX2NAME_MAP; Open the file db/src/Main.cpp and delete this lines: #ifdef __FreeBSD__ _malloc_options = "A"; #endif #ifdef __FreeBSD__ extern const char * _malloc_options; #endif Open game/src/MarkImage.h and before this: #include <IL/il.h> add this: #define IL_STATIC_LIB 1 Server: FreeBSD only changes We have to modify "CC = (something)" in "CC = g++" open those files with a text editor and modify it, here's the list of files: - libthecore/src/Makefile - libsql/Makefile - libpoly/Makefile - libgame/src/Makefile - libserverkey/Makefile - db/src/Makefile - game/src/Makefile - game/src/quest/Makefile (Here it's called CPP not CC) We also have to modify "CC= gccX" (where X is a number) in "CC = gcc" inside "liblua/config" (or liblua/5.0/config) Open libsql/Makefile and delete the following lines: IFLAGS = -I/usr/local/include/ #sometimes necessary included IFLAGS = -I/usr/local/include/mysql or the following lines if you have them: oppure potreste avere le seguenti righe che sono da rimuovere: IFLAGS = -I../libmysql/5.x-5.1.35 IFLAGS = -I../libmysql/7.x-5.1.35 Before "LIBS =" add this: VCPKG_DIR=(Your vcpkg directory) IFLAGS = -I$(VCPKG_DIR)/installed/x86-freebsd/include Check if you have "-m32" inside this files, otherwise add this before "LIBS = " CFLAGS += -m32 This check and add process must be done for the following files: - libthecore/src/Makefile - libsql/Makefile - libpoly/Makefile - libserverkey/Makefile - libgame/src/Makefile Open the file game/src/quest/Makefile and if you don't have "-m32" add this before "all: qc cc" CFLAGS += -m32 If you don't have "ENABLE_STATIC = 0" add this before "all: qc cc" CPP += -static If you have "ENABLE_STATIC = 0" change the 0 in 1. If you are under FreeBSD x64, you have to add this before "all: qc cc". LIBDIR += -L/usr/local/lib32/gccX While for x86 Freebsd, this LIBDIR += -L/usr/local/lib/gccX Replace X with the gcc major version, you can verify that by typing "gcc --version" in the console. You can get something like this: gcc (FreeBSD Ports Collection) 9.3.0 In my case, the number is 9. Open "db/src/Makefile", and delete this lines if you have them ifeq ($(BSD_VERSION), 7) INCDIR += -I../../libmysql/7.x-5.1.35 LIBDIR += -L../../libmysql/7.x-5.1.35 else INCDIR += -I../../libmysql/5.x-5.1.35 LIBDIR += -L../../libmysql/5.x-5.1.35 endif also delete this lines if you have them: # boost INCDIR += -I../../boost and also delete this lines: # Boost INCDIR += -I../../../Extern/include/boost # MySQL INCDIR += -I/usr/local/include/mysql LIBS += /usr/local/lib/mysql/libmysqlclient.a /usr/lib/libz.a and also this line: INCDIR += -I/usr/local/include and this ones: # Project Libraries INCDIR += -I../../../Extern/include LIBDIR += -I../../../Extern/lib also delete this word: -lmysqlclient and this one as well: -Wl,-rpath=/usr/local/lib/gccX (there could be any number where there is X) If you don't have "-m32" add before "SRCS = (some text)" or "CPPFILE = (some text)" this: CFLAGS += -m32 Add this before "SRCS = (some text)" or "CPPFILE = (some text)" this: VCPKG_ROOT (your vcpkg directory) LIBS += -lmariadbclient -lssl -lcrypto -lz INCDIR += -I$(VCPKG_ROOT)/installed/x86-freebsd/include LIBDIR += -L$(VCPKG_ROOT)/installed/x86-freebsd/lib Under FreeBSD x64, you have to add this after the LIBDIR += code you have added before: LIBDIR += -L/usr/local/lib32/gccX Under FreeBSD x86 this: LIBDIR += -L/usr/local/lib/gccX Replace X with your gcc major version code, you can grab that from "gcc --version". If you don't have "ENABLE_STATIC = 0" add this before the code we added before (LIBDIR += and INCDR +=) CFLAGS += -static If you have "ENABLE_STATIC = 0" change the 0 to 1. Open game/src/Makefile and delete the following lines (it doesn't matter if you don't have all of them) # boost INCDIR += -I../../../Extern/include/boost # DevIL INCDIR += -I../../libdevil LIBDIR += -L../../libdevil LIBS += -lIL -lpng -ltiff -lmng -llcms -ljpeg # MySQL ifeq ($(BSD_VERSION), 7) INCDIR += -I../../libmysql/7.x-5.1.35 LIBDIR += -L../../libmysql/7.x-5.1.35 else INCDIR += -I../../libmysql/5.x-5.1.35 LIBDIR += -L../../libmysql/5.x-5.1.35 endif LIBS += -lmysqlclient -lz # Miscellaneous external libraries INCDIR += -I../../../Extern/include LIBDIR += -L../../../Extern/lib LIBS += -lcryptopp -lgtest # HackShield INCDIR += -I../../libhackshield/include LIBDIR += -L../../libhackshield/lib LIBS += -lanticpxsvr # XTrap INCDIR += -I../../libxtrap/include and # Boost INCDIR += -I../../../Extern/include/boost # DevIL INCDIR += -I../../../Extern/include/IL LIBS += ../../../Extern/lib/libIL.a\ ../../../Extern/lib/libjasper.a\ ../../../Extern/lib/libpng.a\ ../../../Extern/lib/libtiff.a\ ../../../Extern/lib/libjbig.a\ ../../../Extern/lib/libmng.a\ /usr/lib/liblzma.a\ ../../../Extern/lib/liblcms.a\ ../../../Extern/lib/libjpeg.a # MySQL INCDIR += -I/usr/local/include/mysql LIBS += /usr/local/lib/mysql/libmysqlclient.a /usr/lib/libz.a # CryptoPP LIBS += ../../../Extern/lib/libcryptopp.a and # OpenSSL INCDIR += -I/usr/include LIBS += -lssl -lcrypto # LIBS += /usr/lib/libssl.a and # Project Libraries INCDIR += -I../../../Extern/include INCDIR += -I/usr/local/include LIBDIR += -L/usr/local/lib Before "CFILE = minilzo.c" add this: VCPKG_ROOT= (your vcpkg directory) INCDIR += -I$(VCPKG_ROOT)/installed/x86-freebsd/include LIBDIR += -L$(VCPKG_ROOT)/m2pkg/installed/x86-freebsd/lib LIBS += -lmariadbclient -lcryptopp -lIL -lpng -ljpeg -lssl -lcrypto Under FreeBSD x64, you have to add this after the LIBDIR += code you have added before: LIBDIR += -L/usr/local/lib32/gccX Under FreeBSD x86 this: LIBDIR += -L/usr/local/lib/gccX Replace X with your gcc major version code, you can grab that from "gcc --version". If you don't have "ENABLE_STATIC = 0" add this before the code we added before (LIBDIR += and INCDR +=) CFLAGS += -static If you have "ENABLE_STATIC = 0" change the 0 to 1. Replace: CFILE = minilzo.c with: CFILE = minilzo.c sha1.c Some sources might not have CFILE, in case you don't have it add "sha1.c" after "item_manager_read_tables.cpp" like this: item_manager_read_tables.cpp sha1.c Server: Changes for Windows Open m2server.sln or Metin2Server.sln and update the toolchain if you ask it, ignore any "ServerkeyGenerator" error. Open game.2008.vcxproj with a text editor and add after this: <ClCompile Include="war_map.cpp" /> this: <ClCompile Include="sha1.c" /> <ClCompile Include="sha1.h" /> If you used an old version of the tutorial, please remove this line: <VcpkgUserTriplet>x86-windows-static</VcpkgUserTriplet> You have to remove this line in all the vcxproj files. Open the solution with VisualStudio, select ALL projects, then right click the selection and click Properties. Inside the properties, click on the left "vcpkg" and make sure to select: "Static libraries: Yes" "Auto link: Yes" Client changes Open EterBase/cipher.h and change the following lines: Open cipher.h and modify the following line: encoder_->ProcessData((byte*)buffer, (const byte*)buffer, length); to: encoder_->ProcessData((CryptoPP::byte*)buffer, (const CryptoPP::byte*)buffer, length); and from: decoder_->ProcessData((byte*)buffer, (const byte*)buffer, length); to: decoder_->ProcessData((CryptoPP::byte*)buffer, (const CryptoPP::byte*)buffer, length); Open Eterbase/lzo.cpp and delete this line: #include <lzo-2.03/lzoLibLink.h> "lzo-2.03" may vary, it's important to delete the lzoLibLink inclusion. Open EterBase/lzo.h and check that the #include with lzo1x.h is like this: #include <lzo/lzo1x.h> Open UserInterface/MarkImage.h and before this: #include <IL/il.h> add this: #define IL_STATIC_LIB 1 Open UserInterface.cpp and delete the following line: #include <cryptopp/cryptoppLibLink.h> Inside UserInterface.cpp modifiy this: static const char * sc_apszPythonLibraryFilenames[] = { "UserDict.pyc", "future.pyc", "copy_reg.pyc", "linecache.pyc", "ntpath.pyc", "os.pyc", "site.pyc", "stat.pyc", "string.pyc", "traceback.pyc", "types.pyc", "\n", }; to: static const char * sc_apszPythonLibraryFilenames[] = { #if PY_MINOR_VERSION == 7 "abc.pyc", "bisect.pyc", "codecs.pyc", "collections.pyc", "copy.pyc", "copy_reg.pyc", "fnmatch.pyc", "functools.pyc", "genericpath.pyc", "heapq.pyc", "hashlib.pyc", // new "keyword.pyc", "linecache.pyc", "locale.pyc", "ntpath.pyc", "os.pyc", // removed pyexpat "random.pyc", "re.pyc", "shutil.pyc", "site.pyc", "sre_compile.pyc", "sre_constants.pyc", "sre_parse.pyc", "stat.pyc", "string.pyc", "sysconfig.pyc", "traceback.pyc", "types.pyc", "UserDict.pyc", "warnings.pyc", "weakref.pyc", "_abcoll.pyc", "_weakrefset.pyc", "__future__.pyc", "encodings\\aliases.pyc", "encodings\\ascii.pyc", "encodings\\cp1250.pyc", "encodings\\cp1251.pyc", "encodings\\cp1252.pyc", "encodings\\cp949.pyc", "encodings\\utf_8.pyc", "encodings\\cp850.pyc", //new "encodings\\__init__.pyc", // removed xml #else "UserDict.pyc", "__future__.pyc", "copy_reg.pyc", "linecache.pyc", "ntpath.pyc", "os.pyc", "site.pyc", "stat.pyc", "string.pyc", "traceback.pyc", "types.pyc", #endif "\n", }; Delete the following lines: #if GrannyProductMinorVersion==4 #pragma comment( lib, "granny2.4.0.10.lib" ) #elif GrannyProductMinorVersion==7 #pragma comment( lib, "granny2.7.0.30.lib" ) #elif GrannyProductMinorVersion==8 #pragma comment( lib, "granny2.8.49.0.lib" ) #elif GrannyProductMinorVersion==9 #pragma comment( lib, "granny2.9.12.0.lib" ) #else #error "unknown granny version" #endif or this line: #pragma comment( lib, "granny2.lib" ) Also delete this lines: //ifdef _DEBUG //#pragma comment( lib, "python2_d.lib" ) //#else #pragma comment( lib, "python2.lib" ) //#endif #pragma comment( lib, "devil.lib" ) #ifdef _DEBUG #pragma comment( lib, "jpegd.lib" ) #else #pragma comment( lib, "jpeg.lib" ) #endif #pragma comment( lib, "cryptopp-static.lib" ) #pragma comment( lib, "snappy.lib" ) #pragma comment( lib, "lzo2.lib" ) Delete snappy inclusion if you don't use Type6. If you have ENABLE_PY_CHECK, add before #elif PY_VERSION_HEX==0x020712F0 this one: #elif PY_VERSION_HEX==0x020712F0 { PYFOLD"/abc.pyc", 6594, 0xb23cfe1aL}, { PYFOLD"/bisect.pyc", 3276, 0x306b93d2L}, { PYFOLD"/codecs.pyc", 40678, 0xe2dc76f0L}, { PYFOLD"/collections.pyc", 28295, 0xaffface7L}, { PYFOLD"/copy.pyc", 13236, 0xaf4763dcL}, { PYFOLD"/copy_reg.pyc", 5618, 0x8612d494L}, { PYFOLD"/fnmatch.pyc", 3860, 0xdad2ad3aL}, { PYFOLD"/functools.pyc", 7859, 0x110b58e2L}, { PYFOLD"/genericpath.pyc", 3968, 0x3005313bL}, { PYFOLD"/hashlib.pyc", 7278, 0x709b3325L}, { PYFOLD"/heapq.pyc", 15302, 0x763f87a6L}, { PYFOLD"/keyword.pyc", 2187, 0x6e8bf638L}, { PYFOLD"/linecache.pyc", 3518, 0xe414a862L}, { PYFOLD"/locale.pyc", 57922, 0xe5bae851L}, { PYFOLD"/ntpath.pyc", 14031, 0x2170738fL}, { PYFOLD"/os.pyc", 27862, 0xd1b87519L}, { PYFOLD"/random.pyc", 27469, 0x626a1305L}, { PYFOLD"/re.pyc", 14274, 0xf6615b33L}, { PYFOLD"/shutil.pyc", 20489, 0x5f86796aL}, { PYFOLD"/site.pyc", 20470, 0x1e5d86efL}, { PYFOLD"/sre_compile.pyc", 13175, 0xdb74e1f3L}, { PYFOLD"/sre_constants.pyc", 6400, 0x62725f60L}, { PYFOLD"/sre_parse.pyc", 22632, 0x73a3417bL}, { PYFOLD"/stat.pyc", 3161, 0x505339d3L}, { PYFOLD"/string.pyc", 22550, 0xa831251fL}, { PYFOLD"/struct.pyc", 280, 0x692620a3L}, { PYFOLD"/sysconfig.pyc", 18751, 0x1609c777L}, { PYFOLD"/traceback.pyc", 12499, 0xc55a9ea6L}, { PYFOLD"/types.pyc", 3012, 0xab7ff24eL}, { PYFOLD"/UserDict.pyc", 11556, 0xec08a651L}, { PYFOLD"/warnings.pyc", 14453, 0x191fb51bL}, { PYFOLD"/weakref.pyc", 18614, 0xc850b7d7L}, { PYFOLD"/_abcoll.pyc", 29948, 0x2e08dd19L}, { PYFOLD"/_generate.py", 368, 0xa1759794L}, { PYFOLD"/_weakrefset.pyc", 11646, 0x37eda60dL}, { PYFOLD"/__future__.pyc", 4469, 0xfc87d05aL}, { PYFOLD"/encodings/aliases.pyc", 8811, 0x1cc93e8dL}, { PYFOLD"/encodings/ascii.pyc", 2691, 0x675a99b1L}, { PYFOLD"/encodings/cp1250.pyc", 3347, 0xdbb8207eL}, { PYFOLD"/encodings/cp1251.pyc", 3344, 0x8ef98d98L}, { PYFOLD"/encodings/cp1252.pyc", 3347, 0x7b5e1208L}, { PYFOLD"/encodings/cp850.pyc", 8292, 0xa0865dd5L}, { PYFOLD"/encodings/cp949.pyc", 2065, 0x5f2c4c06L}, { PYFOLD"/encodings/utf_8.pyc", 2294, 0xbc12dc7fL}, { PYFOLD"/encodings/__init__.pyc", 4542, 0x861f2a28L}, Open EterPack/EterPack.cpp and delete this line: #include <cryptopp/cryptoppLibLink.h> Open UserInterface/GuildMarkUploader.cpp and delete: (ILvoid*) Open EterLib/JpegFile.cpp and make sure that jpeglib.h inclusion is like this: #include <jpeglib.h> then remove this line: #include <libjpeg-6b/jpegLibLink.h> "libjpeg-6b" may vary. Inside GrpDetector.h add this after #include <d3dx8.h> #include <string> Inside ScriptLib\StdAfx.h replace: #ifdef _DEBUG #undef _DEBUG #include "../../Extern/Python2/Python.h" #define _DEBUG #else #include "../../Extern/Python2/Python.h" #endif #include "../../Extern/Python2/node.h" #include "../../Extern/Python2/grammar.h" #include "../../Extern/Python2/token.h" #include "../../Extern/Python2/parsetok.h" #include "../../Extern/Python2/errcode.h" #include "../../Extern/Python2/compile.h" #include "../../Extern/Python2/symtable.h" #include "../../Extern/Python2/eval.h" #include "../../Extern/Python2/marshal.h" the Python inclusion may vary to #include <python2.7/Python.h> #include <python2.7/node.h> #include <python2.7/grammar.h> #include <python2.7/token.h> #include <python2.7/parsetok.h> #include <python2.7/errcode.h> #include <python2.7/compile.h> #include <python2.7/eval.h> #include <python2.7/marshal.h> #undef BYTE // Fixed python bitset.h build issue Inside scriptLib/PythonLauncher.h modify #include "../../Extern/Python2/frameobject.h" to: #include <python2.7/frameobject.h> Open scriptLib/PythonLauncher.cpp and modify: #include "../../Extern/Python2/frameobject.h" to: #include <python2.7/frameobject.h> Open scriptLib/PythonMarshal.cpp and modify: #include "../../Extern/Python2/longintrepr.h" to: #include <python2.7/longintrepr.h> Open ScriptLib/PythonUtils.cpp and if we don't have this #define PyLong_AsLong PyLong_AsLongLong #define PyLong_AsUnsignedLong PyLong_AsUnsignedLongLong add it after this: #include "PythonUtils.h" Open EterBase\StdAfx.h and modify: // Armadillo nanomite protection #ifndef NANOBEGIN #ifdef __BORLANDC__ #define NANOBEGIN __emit__ (0xEB,0x03,0xD6,0xD7,0x01) #define NANOEND __emit__ (0xEB,0x03,0xD6,0xD7,0x00) #else #define NANOBEGIN __asm _emit 0xEB __asm _emit 0x03 __asm _emit 0xD6 __asm _emit 0xD7 __asm _emit 0x01 #define NANOEND __asm _emit 0xEB __asm _emit 0x03 __asm _emit 0xD6 __asm _emit 0xD7 __asm _emit 0x00 #endif #endif in: // Armadillo nanomite protection #if !defined(NANOBEGIN) && !defined(NANOEND) #ifdef _DEBUG #define NANOBEGIN #define NANOEND #else #include <armadillo/SecuredSections.h> #endif #endif This change also needs to be applied to the following files: - EterGrnLib/StdAfx.h - MilesLib/StdAfx.h - SpeedTreeLib/StdAfx.h Open MilesLib/StdAfx.h and delete this line: #include <mss.h> Open the client sln file (like Metin2Client.sln or Metin2Client_VC90.sln) if it asks for any linked update press yes. Then go to UserInterface properties, select at the top of the window "Configuration: ", it might be Debug, Release, Distribute, and set it "All configurations": Modify /SAFESEH to /SAFESEH:NO. Select ALL projects, then right click the selection and click Properties. Inside the properties, click on the left "vcpkg" and make sure to select: "Static libraries: Yes" "Auto link: Yes" Go to C/C++, General, you will find "Extra inclusion directories" for something similar. Press the little arrow in the text, then modify. Add the following: $(SolutionDir)Extern\include. Then, select the UserInterface project. Go to Linker, General and find "Extra library directory". Press the little arrow in the text, then Modify. Add the following: $(SolutionDir)Extern\lib. If you used an old version of the tutorial, please remove this line: <VcpkgUserTriplet>x86-windows-static</VcpkgUserTriplet> You have to remove this line in all .vcxproj files. Client: Cleanup and update of binary files Open our clinet directory and delete all DLL with the exception of this (MAKE SURE TO NOT RMEOVE ANY EXTRA DLL YOU HAVE INSERTED) - SpeedTreeRT.dll - granny2.dll - MSS32.dll Delete the "lib" directory and replace with this directory This directory works for Python 2.7. Final notes and infos If you use Granny 2.9 open EterBase/ServiceDefs.h and add this: #define __GRANNY_VERSION__ 9 after #define _IMPROVED_PACKET_ENCRYPTION_ The vcpkg folder already contains MSL (required for marty sources), check in share/moduleinfo.txt and if there is a new commit, you can replace the inclusions, you can also upload the new updated package and I can update the tutorial. The Python libraries I have distributed works for 2.7.18, it includes some encoding modules, this is the last ever version of python 2.7.18 Expat and xml were removed as they don't work with static python (GIL stuff) Fell free to comment with the check code updated and I'll update the post. The Visual Studio and Makefile updates do not apply with CMake or Premake (pff anyone uses them unironically?) The tutorial might not be 100% accurate for all the sources, I have tested it with mainline_released, a marty internet released and some friends sources (Client side) FreeBSD currently does not work on vcpkg, I will look at fixing the issues when I have time. The log.c changes removed 1058340518920543890 logging messages on Windows. The armadillo change not only update the nanomites to the last version (while Armadillo is deprecated) but also fixes some debugging issues, sometimes, calling those asm routimes might corrupt the debugger, so it's better to disable them if they are not required (Debug builds) If you need newer version of granny2, modify granny2.h accordingly, you can also share the update if you wish to. Changes: - Updated to python 2.7.18 (v3) - Removed all x86-windows-static specification as the new vcpkg integration is easier to use. - Ability to customize the vcpkg root directory - Removed all infos about compiling the sources and so on as they were not required in this forum (I do really expect that people knows how to build in this forum) - Removed all the old manual linking as vcpkg supports automatic linking now I apologize for any grammar error or formatting error, I have not checked the post twice, you can freely report any error in the comments. Good luck in your experiments.
  10. Hello Friends You know that so many files are shared in the Forum that some of them are compiled with GCC8 - GCC9 or GCC10. There is also MySQL, of course, if it is on MySQL, MySQL 5.6 or MariaDB 10.3 is used. That's why I will share 2 different Freebsd VDIs on a single topic. 1 - Mysql 5.6 Version 2 - MariaDB 10.3 Version You can download and use these 2 VDIs according to your needs. ISO File Used - 2021-Apr-09 06:58 ; Freebsd Login ; User : root Password : dev Package Installation List for MySQL 5.6 ; gcc10: 10.3.0 gcc8: 8.5.0_1 gcc9: 9.4.0 gdb: 10.2_1 gmake: 4.3_2 makedepend: 1.0.6,1 mysql56-client: 5.6.51 mysql56-server: 5.6.51 python27-2.7.18_1 Package Installation List for MariaDB 10.3 ; gcc10: 10.3.0 gcc8: 8.5.0_1 gcc9: 9.4.0 gdb: 10.2_1 gmake: 4.3_2 makedepend: 1.0.6,1 mariadb103-client: 10.3.30 mariadb103-server: 10.3.30 python27-2.7.18_1 Download Link ; Google.Drive (Download) Dosya.Upload (Download) Virüs Scan ; Thank you for using it and for your nice comments. Enix Yazılım.
  11. This tutorial is going to teach you how to compile, run and configure a server on Windows. I needed something like this a few days ago and since it doesn't exist, I decided to make it There is no addition or modification in the source or client (except for small bonuses). 0. Beginning At the end of the topic there will be two links where you will need to download: Client + Server + Source MySQL The client is based on the Metin2 Client fur r40250, I just edited it to have the classic format. Regardless the Server and Client source. 1. The files We will need to download the following files: Visual Studio Community 2019 - In order to compile both the server and the binary, we're going to need this MySQL - Connect and create the database 2. Installing The installation is easy enough for me to consider that I don't need to spend much time on this, however I hope this two pictures will facilitate (more) on what you need to do: Visual Studio Community Note: You actually just need the MSVC v142, C++ CMake, C++ ATL, C++ MFC and C++/CLI for this to work MySQL: Warning: In this tutorial we're going to use Mysql Server 5.7.33 X64 but you can (must) upgrade it to 8.0 2.1 Installing Server / Client / Database Here you need to pay attention because there's a limitation: Warning: You must unzip the file "dev" on C:\ If you don't want, follow the Mali61's topic and you need to create manually the symlinks for each core on the server. This is how it should be. Client: There isn't much to say, in pack/ you already have root and locale_de unpacked but since this is going to be localhost only, you don't need to change nothing on the serverinfo.py Bonus: I translated the client to English, just because Database: 1) Windows Key + R and write services.msc 2) Search for MySQL57 (or the version you installed) and click on Stop Since Im portuguese, yeah 3) Go to directory C:\ProgramData\MySQL 4) In the folder MySQL Server 5.7 (or the version you installed) and in the folder Data, paste the files you previously downloaded and unziped from mysql_dev.rar 5) On services.msc, start the MySQL process Back it again with the portuguese Server: These images are referenced in each core's CONFIG and conf.txt, respectively where the location is on directory C:\dev\2. Server. Warning: Don't forget to change the MySQL's user password! You need to put the same password you had when installing the MySQL. 3. Compile Server / Client Source There is nothing introductory since it is something very simple that you will be able to. Server: It's quite simple, to build the server source, we just need to open the file dev_server.sln which is located in C:\dev\1. Svn\Server\build You can build all at once or separately. Bonus: I linked the files to go to the directory C:\dev\2. Server\share\ so you don't need to c&p multiple times. Client: Same as before, open the dev_solution.sln which is located in C:\dev\1. Svn\Client Since I have a good computer, I enabled the multi-processor compilation option. If your computer is very slow while you are compiling, I suggest you deactivate by going to Properties in all the builds. 4. Starting the Server On the main directory of the server (C:\dev\2. Server) you'll have 2 bat files: start.bat* - As the name says, it will start the server clear.bat - It will clear all the server's logs * I forgot the make it dynamic so if you don't want to have on the C:\dev, you'll need to change the directory. Execute start.bat and it will show up first the db.exe, then auth's game.exe and last channel1's game.exe And there you have it, your server is now online! 5. Debug You can debug by going to Debug -> Start New Instance Bonus: I linked everything so you don't have to worry about anything 5. Credits I like to say that I don't know anything about anything and as such, everything here has its credits. @Mali61 - Client/Server compilable with VS2019 (Server & Client) @Karbust - If it wasn't for him, I couldn't have done this ThatGuyPT - The base was from his Windows Serverfiles 6. FAQ Q: Why didn't you use xampp instead of MySQL? A: At the moment I use MySQL a lot even outside of Metin2, so it makes more sense to me that it be this way. However, it is exactly the same, especially on localhost. Q: Can I migrate the source to FreeBSD? A: Yes, you can! As long as you have cmake configured, you can distribute to FreeBSD and use it there. Q: What is the id and password to enter the game? (I put this question because I know there will be someone asking this) A: You can create an account in the database, but you can use id: admin pw: 123 7. Links dev - mysql_dev All In One (Internal) If you have any questions that I can answer, feel free to write a post here.
  12. There instructions have been tested on FreeBSD 12.1! First of all we want to install update the package repository: pkg update If you have never used pkg probably you will be asked to install it. Now run this command to install php7.4 and nginx pkg install php74 nginx We can install the most common php extensions running this command: pkg install php74-extensions php74-mysqli php74-mbstring php74-curl php74-gd The following extensions will be installed: php74-ctype: 7.4.14 php74-curl: 7.4.14 php74-dom: 7.4.14 php74-extensions: 1.0 php74-filter: 7.4.14 php74-gd: 7.4.14 php74-iconv: 7.4.14 php74-json: 7.4.14 php74-mbstring: 7.4.14 php74-mysqli: 7.4.14 php74-opcache: 7.4.14 php74-pdo: 7.4.14 php74-pdo_sqlite: 7.4.14 php74-phar: 7.4.14 php74-posix: 7.4.14 php74-session: 7.4.14 php74-simplexml: 7.4.14 php74-sqlite3: 7.4.14 php74-tokenizer: 7.4.14 php74-xml: 7.4.14 php74-xmlreader: 7.4.14 php74-xmlwriter: 7.4.14 Enable nginx and php service to automatically start sysrc nginx_enable=yes sysrc php_fpm_enable=YES We can start nginx and php for the first time by running service nginx start service php-fpm start You can open now your web browser and type the server ip in the address bar. You should see a page like this We are going to configure nginx now. Open the file nginx.conf located in /usr/local/etc/nginx and modify the section "server": server { listen 80; server_name _; root /var/www; index index.php index.html index.htm; location / { try_files $uri $uri/ =404; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $request_filename; include fastcgi_params; } location ~ /\.ht { deny all; } } Change mydomainname.com with your domain (or server ip). We can test now if php is working correctly. The files of your webiste will be hosted in the folder /var/www (you can change it in the configuration above). Create a new file inside that folder and call it index.php with this content: <?php phpinfo(); Now open again the browser and type the ip address/domain name and you should see something like this: The server is working properly and you can upload now your script You might need to install more php extentions according to the requirements of your script
  13. Hello community, About Firewall This firewall is ideal for those who have a national server and not an international server. It is based on GEO block, adding new countries or removing them is extremely easy. Currently I can easily say that it is the best pf that is published for free at the level of efficiency. I attacked the firewall several times, it was tested on online servers with 700/800 simultaneous players and it worked efficiently without problems. Warning The best protection that can be applied is at the network level. there is no point in having 1 firewall running on an operating system if the network does not support a large amount of traffic for the operating system's firewall to be able to act. This firewall in combination with a good VPS/dedicated VPS and good network protection on the company side can easily handle small to medium attacks. All the magic happens because of GEO BLOCK, without GEO BLOCK it is equivalent to the pf's shared here in the community. The code is not optimally organized and can be improved, remembering that it was my first version. Requirements -pftop (PF) -wget How To Setup [Hidden Content] Questions? Comment, as soon as possible I will answer. Best Regards, Papix
  14. Required level : Beginner Estimated time : 15 minutes This tutorial is to explain how to install a map on Metin2. Needed : A map, you can find many maps here. You will need access to your server, with WinSCP for example, as well as a packer like EterNexus or PackMakerLite for depack your client. I. Serveur side II. Client side A category Questions and Answers is available. If you have a problem or question, feel free to post a request!
  15. Hello community, I have seen many people selling "wiki equal to the official" for prices ranging from 300€ to 500€ or more, what is wrong with this? These "web developers" are selling only open source code. In a didactic way these "professionals" are selling something open source that is free. How do I know this is not speculation? For those who don't know there are several free tools to detect which engine a site is using. For example, open whatcms.org and put the link to the official wiki. And voila, we have discovered the secret of many super web developers The secret goes by the name of MediaWiki. Requirements Hardware Minimum 256MB RAM Minimum 85MB disk space Dependencies PHP 7.3.19+ or 7.4.3+ and respective dependencies MySQL 5.5.8+ or MariaDB 5.1+ If you want to see the full requirements see the documentation: [Hidden Content] You don't know how to configure a webhost? Don't worry, consult my topic about configuring a web server using CyberPanel + OpenLiteSpeed: MediaWiki Setup [Hidden Content] Questions? Comment, as soon as possible I will answer. Best Regards, Papix
  16. M2 Download Center Download Here ( Internal ) Features Written in c++, best performance with multithreading Beautiful GUI, inspired in Blizzard All errors and exceptions throw user friendly messages International Multilanguage Fast download files Method of Hash (CRC32) by default Banner in home (Promotion, Event, New Image, etc...) + News / Patch notes Initialize client with arguments Tool user friendly for generate serverlist hashs New config game for Metin built-in (Requirements: metin2client source) Languages Availables English (US) (Full language correctly) Espanhol (ES) (Translate by : Jesús ) Portuguese (BR) (Full language correctly) Romanian (RO) (Translate by: Abel (Tiger) ) German (DE) (Translate by : Core | Simplex2 ) Turkish (TR) (Need review) Czech (Translate by : Joseph Lochy ) If your language needs review, or you want to help translate the language of their country to add to the list, you can contact me by skype and help correct . Some Community version limitations - News/PatchNotes maximum 255 characters - Without support for client initialize with arguments - Without change Icon Application - Without change Logo (Metin2 logo default) - Without change Background - Without custom Layout - Without possibility of choose hash method - Receive updates direct in Topic/forum - Without support Skype - Binarie name default for initialize: metin2client.exe INSTALL PART SERVER Rev: 140816 : [Hidden Content] [ChangeLog 090816.1] - Fix Application crash when all files updated. [ChangeLog 090816.2] - Removed limit bandwith of Community Version: because failing to download multiple files/ buffer fail. [ChangeLog 140816] - Added Language Czech (translate by : Joseph Lochy ) - Update Language Spanish ( translate by : Jesús ) - Update Language Romanian ( translate by: Abel (Tiger) ) VirusScan : [Hidden Content] 1. In his HOST, upload the files from the "Host Files" 2. Open GameLauncher_Manager.exe select "TAB" Server Hash. 3. Select the client directory and generate the Hash 4. Put the new "serverlist.data" in /gamelauncher/conf/ Structure [patch.notes] = Here you put an text (news/patch notes) [serverlist.data] = Responsible for client hash [slide1.png] = Banner image. The extension must be .png and same size . 5. Put the client files in the folder "/gamelauncher/data/" (host) INSTALL PART CLIENT Structure [ldata] = Folder that stores settings (This folder is the one that does not create automatic. It must exist for GameLauncher work.) [gameconfig.ini] = Metin2 client config [LauncherConfig.ini] = Launcher Config [serverconf.data] = Host configs 1. Open GameLauncher_Manager.exe and select "TAB" Config for GameLauncher Fill in the fields Homepage: Here you put the link to your website. This link must be prefixed with http:// URL Hosting: Here you put the link from the main folder where you will create the folder "gamelauncher" e.g. metin2server.net (public_html) 2. Click in "Save". Will generate a file "serverconf.data".Place in the "ldata" folder within the client. INSTALL PART CLIENT SOURCE 1. In "PythonSystem.cpp", put in header //Boost #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ini_parser.hpp> #include <boost/lexical_cast.hpp> #define ENABLE_NEWGAMECONFIG 2. In "PythonSystem.cpp" search function "LoadConfig()" And replace with : bool CPythonSystem::LoadConfig() { #ifdef ENABLE_NEWGAMECONFIG boost::property_tree::ptree r_pt; /** * File exists? */ try { boost::property_tree::ini_parser::read_ini("ldata/gameconfig.ini", r_pt); //resolution m_Config.width = r_pt.get<DWORD>("Metin2.WIDTH"); m_Config.height = r_pt.get<DWORD>("Metin2.HEIGHT"); //frequency m_Config.frequency = r_pt.get<DWORD>("Metin2.FREQUENCY"); //gamma m_Config.gamma = r_pt.get<int>("Metin2.GAMMA"); //windowmode //fullscreen m_Config.bWindowed = r_pt.get<bool>("Metin2.WINDOW_MODE"); //software cursor m_Config.is_software_cursor = r_pt.get<bool>("Metin2.SOFTWARE_CURSOR"); //Sound Music m_Config.music_volume = r_pt.get<float>("Metin2.MUSIC", 0.0f); //Sound Effects m_Config.voice_volume = (char)r_pt.get<int>("Metin2.EFFECTS"); //fog m_Config.iDistance = r_pt.get<int>("Metin2.VISIBILITY"); //tln m_Config.bSoftwareTiling = (r_pt.get<std::string>("Metin2.SOFTWARE_TILING").length() > 1 ? 0 : r_pt.get<int>("Metin2.SOFTWARE_TILING")); //INT in byte //shadow m_Config.iShadowLevel = r_pt.get<int>("Metin2.SHADOW"); //IME GAME m_Config.bUseDefaultIME = r_pt.get<bool>("Metin2.GAME_IME"); //IME Extern m_Config.bUseDefaultIME = r_pt.get<bool>("Metin2.EXTERN_IME"); //Others m_Config.bDecompressDDS = r_pt.get<bool>("Metin2.DECOMPRESSED_TEXTURE"); m_Config.bViewChat = r_pt.get<bool>("Metin2.VIEW_CHAT"); m_Config.bAlwaysShowName = r_pt.get<bool>("Metin2.ALWAYS_VIEW_NAME"); m_Config.bShowDamage = r_pt.get<bool>("Metin2.SHOW_DAMAGE"); m_Config.bShowSalesText = r_pt.get<bool>("Metin2.SHOW_SALESTEXT"); m_Config.bNoSoundCard = false; if (m_Config.bWindowed) { unsigned screen_width = GetSystemMetrics(SM_CXFULLSCREEN); unsigned screen_height = GetSystemMetrics(SM_CYFULLSCREEN); if (m_Config.width >= screen_width) { m_Config.width = screen_width; } if (m_Config.height >= screen_height) { m_Config.height = screen_height; } } m_OldConfig = m_Config; } catch (boost::property_tree::ini_parser_error &e) { fprintf(stderr, "Game Config Error: %s\n", e.message().c_str()); } catch(const boost::property_tree::ptree_error &e) { fprintf(stderr, "Game Config Error: %s\n", e.what()); } return true; #else FILE * fp = NULL; if (NULL == (fp = fopen("metin2.cfg", "rt"))) return false; char buf[256]; char command[256]; char value[256]; while (fgets(buf, 256, fp)) { if (sscanf(buf, " %s %s\n", command, value) == EOF) break; if (!stricmp(command, "WIDTH")) m_Config.width = atoi(value); else if (!stricmp(command, "HEIGHT")) m_Config.height = atoi(value); else if (!stricmp(command, "BPP")) m_Config.bpp = atoi(value); else if (!stricmp(command, "FREQUENCY")) m_Config.frequency = atoi(value); else if (!stricmp(command, "SOFTWARE_CURSOR")) m_Config.is_software_cursor = atoi(value) ? true : false; else if (!stricmp(command, "OBJECT_CULLING")) m_Config.is_object_culling = atoi(value) ? true : false; else if (!stricmp(command, "VISIBILITY")) m_Config.iDistance = atoi(value); else if (!stricmp(command, "MUSIC_VOLUME")) { if(strchr(value, '.') == 0) { // Old compatiability m_Config.music_volume = pow(10.0f, (-1.0f + (((float) atoi(value)) / 5.0f))); if(atoi(value) == 0) m_Config.music_volume = 0.0f; } else m_Config.music_volume = atof(value); } else if (!stricmp(command, "VOICE_VOLUME")) m_Config.voice_volume = (char) atoi(value); else if (!stricmp(command, "GAMMA")) m_Config.gamma = atoi(value); else if (!stricmp(command, "IS_SAVE_ID")) m_Config.isSaveID = atoi(value); else if (!stricmp(command, "SAVE_ID")) strncpy(m_Config.SaveID, value, 20); else if (!stricmp(command, "PRE_LOADING_DELAY_TIME")) g_iLoadingDelayTime = atoi(value); else if (!stricmp(command, "WINDOWED")) { m_Config.bWindowed = atoi(value) == 1 ? true : false; } else if (!stricmp(command, "USE_DEFAULT_IME")) m_Config.bUseDefaultIME = atoi(value) == 1 ? true : false; else if (!stricmp(command, "SOFTWARE_TILING")) m_Config.bSoftwareTiling = atoi(value); else if (!stricmp(command, "SHADOW_LEVEL")) m_Config.iShadowLevel = atoi(value); else if (!stricmp(command, "DECOMPRESSED_TEXTURE")) m_Config.bDecompressDDS = atoi(value) == 1 ? true : false; else if (!stricmp(command, "NO_SOUND_CARD")) m_Config.bNoSoundCard = atoi(value) == 1 ? true : false; else if (!stricmp(command, "VIEW_CHAT")) m_Config.bViewChat = atoi(value) == 1 ? true : false; else if (!stricmp(command, "ALWAYS_VIEW_NAME")) m_Config.bAlwaysShowName = atoi(value) == 1 ? true : false; else if (!stricmp(command, "SHOW_DAMAGE")) m_Config.bShowDamage = atoi(value) == 1 ? true : false; else if (!stricmp(command, "SHOW_SALESTEXT")) m_Config.bShowSalesText = atoi(value) == 1 ? true : false; } if (m_Config.bWindowed) { unsigned screen_width = GetSystemMetrics(SM_CXFULLSCREEN); unsigned screen_height = GetSystemMetrics(SM_CYFULLSCREEN); if (m_Config.width >= screen_width) { m_Config.width = screen_width; } if (m_Config.height >= screen_height) { m_Config.height = screen_height; } } m_OldConfig = m_Config; fclose(fp); // Tracef("LoadConfig: Resolution: %dx%d %dBPP %dHZ Software Cursor: %d, Music/Voice Volume: %d/%d Gamma: %d\n", // m_Config.width, // m_Config.height, // m_Config.bpp, // m_Config.frequency, // m_Config.is_software_cursor, // m_Config.music_volume, // m_Config.voice_volume, // m_Config.gamma); return true; #endif // ENABLE_NEWGAMECONFIG } 3. Search function "SaveConfig()" and replace with: bool CPythonSystem::SaveConfig() { #ifdef ENABLE_NEWGAMECONFIG boost::property_tree::ptree pt; pt.put("Metin2.WIDTH", m_Config.width); pt.put("Metin2.HEIGHT", m_Config.height); pt.put("Metin2.FREQUENCY", m_Config.frequency); pt.put("Metin2.GAMMA", m_Config.gamma); pt.put("Metin2.WINDOW_MODE", (m_Config.bWindowed == 1 ? 1 : 0)); pt.put("Metin2.FULLSCREEN", (m_Config.bWindowed == 1 ? 0 : 1)); pt.put("Metin2.SOFTWARE_CURSOR", boost::lexical_cast<std::string>(m_Config.is_software_cursor)); pt.put("Metin2.MUSIC", m_Config.music_volume); pt.put("Metin2.EFFECTS", m_Config.voice_volume); pt.put("Metin2.VISIBILITY", m_Config.iDistance); pt.put("Metin2.SOFTWARE_TILING", m_Config.bSoftwareTiling); //tln pt.put("Metin2.SHADOW", m_Config.iShadowLevel); pt.put("Metin2.GAME_IME", (m_Config.bUseDefaultIME == true ? 0 : 1)); pt.put("Metin2.EXTERN_IME", (m_Config.bUseDefaultIME == true ? 1 : 0)); //Game Config Others pt.put("Metin2.DECOMPRESSED_TEXTURE", (m_Config.bDecompressDDS == true ? 1 : 0)); pt.put("Metin2.VIEW_CHAT", (m_Config.bViewChat == true ? 1 : 0)); pt.put("Metin2.ALWAYS_VIEW_NAME", (m_Config.bAlwaysShowName == true ? 1 : 0)); pt.put("Metin2.SHOW_DAMAGE", (m_Config.bShowDamage == true ? 1 : 0)); pt.put("Metin2.SHOW_SALESTEXT", (m_Config.bShowSalesText == true ? 1 : 0)); try { boost::property_tree::ini_parser::write_ini("ldata/gameconfig.ini", pt); } catch (boost::property_tree::ini_parser_error &e) { fprintf(stderr, "Game Config Error: %s\n", e.message().c_str()); } catch(const boost::property_tree::ptree_error &e) { fprintf(stderr, "Game Config Error: %s\n", e.what()); } return true; #else FILE *fp; if (NULL == (fp = fopen("metin2.cfg", "wt"))) return false; fprintf(fp, "WIDTH %d\n" "HEIGHT %d\n" "BPP %d\n" "FREQUENCY %d\n" "SOFTWARE_CURSOR %d\n" "OBJECT_CULLING %d\n" "VISIBILITY %d\n" "MUSIC_VOLUME %.3f\n" "VOICE_VOLUME %d\n" "GAMMA %d\n" "IS_SAVE_ID %d\n" "SAVE_ID %s\n" "PRE_LOADING_DELAY_TIME %d\n" "DECOMPRESSED_TEXTURE %d\n", m_Config.width, m_Config.height, m_Config.bpp, m_Config.frequency, m_Config.is_software_cursor, m_Config.is_object_culling, m_Config.iDistance, m_Config.music_volume, m_Config.voice_volume, m_Config.gamma, m_Config.isSaveID, m_Config.SaveID, g_iLoadingDelayTime, m_Config.bDecompressDDS); if (m_Config.bWindowed == 1) fprintf(fp, "WINDOWED %d\n", m_Config.bWindowed); if (m_Config.bViewChat == 0) fprintf(fp, "VIEW_CHAT %d\n", m_Config.bViewChat); if (m_Config.bAlwaysShowName != DEFAULT_VALUE_ALWAYS_SHOW_NAME) fprintf(fp, "ALWAYS_VIEW_NAME %d\n", m_Config.bAlwaysShowName); if (m_Config.bShowDamage == 0) fprintf(fp, "SHOW_DAMAGE %d\n", m_Config.bShowDamage); if (m_Config.bShowSalesText == 0) fprintf(fp, "SHOW_SALESTEXT %d\n", m_Config.bShowSalesText); fprintf(fp, "USE_DEFAULT_IME %d\n", m_Config.bUseDefaultIME); fprintf(fp, "SOFTWARE_TILING %d\n", m_Config.bSoftwareTiling); fprintf(fp, "SHADOW_LEVEL %d\n", m_Config.iShadowLevel); fprintf(fp, "\n"); fclose(fp); return true; #endif // ENABLE_NEWGAMECONFIG } Notes: Any doubt or mistake you can ask here on the topic. To more details about the PRO version, you can contact me by skype. Request in my profile or PM
  17. I will show you how to easily create animations. Do you know this animation? You must have seen her on some server. It would seem that someone went to the trouble to match her with metin, right? This is just the first impression. If you deal with models in metin then you will do such animation in a simple way. Here is a page with thousands of different animations. All you have to do is load your model in FBX format and the animations will adapt themselves. Then you save the file and later adjust it slightly in the program, because the animation does not include hair bones and various armor elements, such as leg or shoulders (although this is not necessary, because I saw that people sell these animations without any editing). [Hidden Content]
  18. M2 Download Center Internal Hi Everyone! Hello Community, I make this little tutorial, for all the people who wants to try modelling or modifying a Metin2 models This video only intoduce gr2 -> fbx with Noesis, after that import fbx files to 3ds max , and exporting model to gr2. I make 2 video about the process easy way, but keep eye on parameters, switchers in every program. For helping I upload to my mega folders some files 3ds Max 2018 granny export plugin whole noesis 4.44 with export/import plugin 2 video file about the process export settings to granny export in 3ds MAX I. Converting gr2 files to FBX exporting option with animation Exporting option without animation In the video you will see it I paired a model with animation, because sometime the model doesnt have it all the bones. I think its important. II. Importing FBX files With animation importing @.plechito' makes a really usefull tutorial in here Only few thing In my video I show you howto set 3ds MAX 2018 eg. UNITS setup Set all things into centimeter In exporting set all the things what i'm make if you DONT have animation in a fbx files, if you have it In animation import you haveto choose Noesis frames After all that click on OK , and your model finally imported to 3ds MAX 2018 III. Exporting from 3ds Max After you make your pretty modification in the model you have to exporting In my way. I select all things what i wanna export, In the export menu tick out animation if you didnt want to ( It usefull if you wanna make model with animation) Thick out Move Origins in Animation and Model submenu In Meshes submenu, deselect all the bones At the texture submenu thick out include, because it build it the texture into .gr2 I think thats all. If you have question send me a letter or find in discord, or leave a message somewhere Have a nice day. UPDATE I added some exporting setting to mega folder, you can use it exporting setup exporting model exporting animation exporting move animation exporting dead animation exporting static model Cheers. Update 25.03.2022 - added 64 bit support for Granny2 plugin, you should place granny2_x64.dll in noesis root folder. -following @.plechito' logic i've added path for looking for textures eg. If warrior_m.gr2 textures is missing is looking for d:\ymir work\pc\warrior . Update 24.03.2022 I'm updating Noesis Granny plugin now adding texture to the fbx material, so in your 3d application (Noesis, Blender, 3dsmax) you can see texture correctly. if you wanna download just a plugin you can find it here [Hidden Content] you have to copying the file to Noesis/plugin/python folder
  19. Required level : Beginner Estimated time : 20 min Hello everyone ! Needed : Have a Metin2 server with WinSCP access to your server. A tool to unpack your client like EterManager, Eternexus, or EterPack Archiver A TGA file for the item icon. You can also use an existing one. If you implement an item with a 3D Model (weapon, armor, costume, etc ..), you'll need : A file .gr2 which corresponds to the 3D model of your item and the tool Granny Viewer which allows you to view this type of model. One or multiple .dds / .tga files which correspond to the textures of the item. I. Client Side : II. Server Side : A category Questions and Answers is available. If you have a problem or a question, feel free to post a request!
  20. M2 Download Center Download Here ( Internal ) Well, such a thing is quite easy, and I know it since 2011~2012 (when the first 30k game cores were released to be more precise). You can find some diff patch files of that time for 30k game cores on the web that do this, but I will explain to you how to do it via source code editing. Before all, why the 40k game core files have a login more slower than usual? That's because ymir implemented an additional encryption security using cryptopp, and at login, they added some key agreement checks, which ended up the login to be more slower. You can actually disable such a feature, so that you can have the login fast like the old game revisions had. Disabling it will actually remove the "key agreement" issue, and will encrypt the connection with xtea instead of some cryptopp algorithms. (no big security issues, so it's safe to do) The edit is quite simple: Via server, you have to go and edit ^/Srcs/Server/common/service.h commenting #define _IMPROVED_PACKET_ENCRYPTION_ to // #define _IMPROVED_PACKET_ENCRYPTION_ Via client, same story for ^\Srcs\Client\EterBase\ServiceDefs.h commenting #define _IMPROVED_PACKET_ENCRYPTION_ to // #define _IMPROVED_PACKET_ENCRYPTION_ Additionally, in the client, you must also edit the archaic pong from ^\Srcs\Client\UserInterface\Locale.cpp like this: find #define LSS_SECURITY_KEY "testtesttesttest" change it to #define LSS_SECURITY_KEY "1234abcd5678efgh" Video of a quick login: (using a second rate connection, and with 4810 programs opened simultaneously) [Hidden Content] Here how the code should be at the end: Enjoy your abione.
  21. Hello community, Today i will show you how to change keys Open your binary with HxD and replace those keys with yours keys. Standart Lzo Key : B99EB0026F69810563989B2879181A00 Standart Χtea Key : 22B8B40464B26E1FAEEA1800A6F6FB1C StandartI tem_Proto Key: A1A40200AA155404E78B5A18ABD6AA01 Standart Mob_Proto Key: 467449000B4A0000B76E08009D186800 Item_Proto: 4D495058 Mob_Proto: 4D495054 EIX: 4D434F5A EPK: 45504B44 After changing those keys extract all your archives and then open your archiver and change the keys there too (the keys must be same client/archiver) and then pack it and you are done! Regards.
  22. I've been asked quite a few times about making and installing a map so I made a video showcasing the whole process from start to end. Please note that it is for beginners.
  23. Hello there, Surfing on the internet i was looking for a guide on how change the % of refining for the classes of the alchemy stones (rough, cut, rare, antique, legendary) for the clarity (Matt, Clear, Flawless, Brilliant, Excellent) and for the upp. Wasnt getting results so i did learn by myself how to do the trick. First of all, go in your locale directory and open the file "dragon_soul_table.txt". Inside it you will have at the line no. 140: Group RefineGradeTables { codice} RefineGradeTables it has the function to upgrade the CLASS (rough,cut....) Group RefineStepTables { codice} RefineStepTables it has the function to upgrade the CLARITY (Matt, clear....) Group RefineStrengthTables { codice} RefineStrengthTables it has the function to upgrade the LEVEL of the stone (+0, +1 etc) Those listed above are the function now lets get into it in a more detailled manner: RefineGradeTables: #--# NEED_COUNT FEE GRADE_NORMAL GRADE_BRILLIANT GRADE_RARE GRADE_ANCIENT GRADE_LEGENDARY GRADE_NORMAL 15 10000 5 95 0 0 0 Those are my settings by the way and i did modify them to let you understand better what im talking about, so we will have: GRADE_NORMAL STANDS FOR ROUGH GRADE_BRILLIANT STANDS FOR CUT GRADE_RARE STANDS FOR RARE GRADE_ANCIENT STANDS FOR ANTIQUE GRADE_LEGENDARY STANDS FOR LEGENDARY NEED_COUNT ITS EQUAL TO HOW MANY STONES ARE NEEDED FOR THAT UPGRADE FEE ITS EQUAL TO THE COST OF THE UPGRADE Now you may ask, where are the % of refining? Right here: GRADE_NORMAL 15 10000 5 95 0 0 0 You will have 8 values, GRADE_NORMAL stands for rough The value '15' identifies the no. of stones required for the upgrade The value '10000' identifies the no. of yang needed (10k) Now lets focus on the other 5 values: 5 95 0 0 0 For what these values stands for? And how do i modify them in the way i want? Read more and you will find out. The first value '5' identifies the % of the stone remaining in the first class (Rough) and the '95' is the % of success for the stone to become more powerful (Cut). Example, i want to create an upgrade of class that requires: 15 Stones, 10k Gold, and has these percentages: 90% of success, 5% of failure, 5% of lucky success to get from a Rough a Rare stone. Like this: GRADE_NORMAL 15 10000 5 90 5 0 0 Remember to have a total of 100% so 90 + 5 + 5 = 100 /------------------------------------------------------------------------------------------------------------------------------------------------------------------/ Now lets face the clarity field #--# NEED_COUNT FEE STEP_LOWEST STEP_LOW STEP_MID STEP_HIGH STEP_HIGHEST STEP_LOWEST 4 20000 30 70 0 0 0 In this case we will not face against the "GRADE_NORMAL" but we will have "STEP_*" (Where * is Lowest, low, mid, high, highest). NEED_COUNT Indicates the quantity of stones needed FEE Indicates the cost of the refining STEP_LOWEST STEP_LOW STEP_MID STEP_HIGH STEP_HIGHEST Those are the steps, STEP_LOWEST = Matt STEP_LOW = Clear STEP_MID = Flawless STEP_HIGH = Brilliant STE_HIGHEST = Excellent ---------------------------------------------------------------------------------------------- STEP_LOWEST 4 20000 30 70 0 0 0 What does this string means? Well for the refining % we always have to check the last 5 values 30 70 0 0 0 Here 30 indicates the % for the stone to remain matt (Failure %) and 70 indicates the percentage for your stone to become clear (Success %) What if i want to make the refining success 95% for the step_lowest? 5 95 0 0 0 What if i want to make the refining success 10% for the step_highest? 0 0 0 90 10 What if i want to make the refining success 10% for the step_highest but i even want to have the 5 % of the stone returning to the Flawless(STEP_MID) point? 0 0 5 85 10 Hope this will get you the idea on how to understand and modify those things, i posted this in few forums, if u want to share it i got no problems, if u want to give me credits would be better. Im sorry for my english if i made any kind of mistakes, thanks. Update You have to modify even the client part (sorry i forgot to mention this) On dragon_soul_refine_settings.py import item default_grade_need_count = [15, 10, 5, 3] default_grade_fee = [30000, 50000, 70000, 100000] default_step_need_count = [4, 3, 2, 1] default_step_fee = [20000, 30000, 40000, 50000] default_grade_need_count is for the N of stones needed (rough, cut etc) default_step_need_count is for the N of stones needed on the clarity refine fee are the costs for both of them.
  24. How to Update Client src c++17 Step 1: First, we open the client source with visual studio 2019/2022. Select all projects and right click on them -> properties -> General -> c++ language standard -> Here we choose c++17. [Hidden Content] Step 2: In each file (ctrl + shift +f) , we search for the following & delete them: using namespace std; Step 3: Now the following details need to be modified (every file): string pair make_pair vector to std::string std::pair std::make_pair std::vector Step 4: Open Eterbase project stl.h file and delete this code: namespace std { template <class _Ty> class void_mem_fun_t : public unary_function<_Ty *, void> { public: explicit void_mem_fun_t(void (_Ty::*_Pm)()) : _Ptr(_Pm) {} void operator()(_Ty *_P) const {((_P->*_Ptr)()); } private: void (_Ty::*_Ptr)(); }; template<class _Ty> inline void_mem_fun_t<_Ty> void_mem_fun(void (_Ty::*_Pm)()) { return (void_mem_fun_t<_Ty>(_Pm)); } template<class _Ty> class void_mem_fun_ref_t : public unary_function<_Ty, void> { public: explicit void_mem_fun_ref_t(void (_Ty::*_Pm)()) : _Ptr(_Pm) {} void operator()(_Ty& _X) const { return ((_X.*_Ptr)()); } private: void (_Ty::*_Ptr)(); }; template<class _Ty> inline void_mem_fun_ref_t<_Ty> void_mem_fun_ref(void (_Ty::*_Pm)()) { return (void_mem_fun_ref_t< _Ty>(_Pm)); } // TEMPLATE CLASS mem_fun1_t template<class _R, class _Ty, class _A> class void_mem_fun1_t : public binary_function<_Ty *, _A, _R> { public: explicit void_mem_fun1_t(_R (_Ty::*_Pm)(_A)) : _Ptr(_Pm) {} _R operator()(_Ty *_P, _A _Arg) const { return ((_P->*_Ptr)(_Arg)); } private: _R (_Ty::*_Ptr)(_A); }; // TEMPLATE FUNCTION mem_fun1 template<class _R, class _Ty, class _A> inline void_mem_fun1_t<_R, _Ty, _A> void_mem_fun1(_R (_Ty::*_Pm)(_A)) { return (void_mem_fun1_t<_R, _Ty, _A>(_Pm)); } } Step 5: In each file (ctrl + shift +f) , we search for the following & modify them: std::void_mem_fun to std::mem_fn this std::auto_ptr to: std::unique_ptr Step 6: Open userinterface/userinterface.cpp Search this: extern "C" { extern int _fltused; volatile int _AVOID_FLOATING_POINT_LIBRARY_BUG = _fltused; }; add under this: extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; } Step 7: Open Speherelib/spherepack.h modify this: inline void LostChild(SpherePack *pack); to: void LostChild(SpherePack *pack); Step 8: open etherBase/cipher.h search this: #include <cryptopp/cryptlib.h> #pragma warning(pop) add under this: using CryptoPP::byte; Step 9 Open etherpack/Eterpack.h #include <boost/unordered_map.hpp> #include <boost/shared_array.hpp> modify to: #include <unordered_map> Replace all file this: boost:: to: std:: Open etherpack/EterpackManager.h & EterPackPolicy_CSHybridCrypt.h modify this: #include <boost/unordered_map.hpp> to #include <unordered_map> Open GameLib/Area.cpp remove this: #include <boost/algorithm/string.hpp> and this: boost::algorithm::to_lower(attrFileName); modify to: std::transform(attrFileName.begin(), attrFileName.end(), attrFileName.begin(), [](unsigned char c) { return std::tolower(c); }); You can also delete the boost in the include folder. If you don't understand something, you can ask your questions here. thanks for @ Mali and @xXIntelXx some detail.
  25. M2 Download Center Download Here ( Internal ) Hi everyone, In this thread I will explain all console commands in the client: exit - Force close the client mvol - Change the music volume between 0 - 10 svol - Change the sound volume between 0 - 10 snds - Change the volume of the interactive sounds (mobs, hit etc...) between 0 - 200 asnds - Change the volume of the ambience between 0 - 200 mspd - Change the moving speed between 0 - 1100 pwd - Show the current folder path (D:ymir work) ls - Show the folders in D:ymir work shadow - Change the shadow level between 0 - 5 splat - Set the rendering distance between the character and the ambience 0 - ∞ distance - Change the view distance between 0 - 4 bgloading - This function is disabled in the binary! transtree - Enable or disable the transparency of the trees 0 - 1 cd - CD to directory, like in bat files (cd ..) up - Go up in the folder structure (D:ymir work) lsd - List all directories on the partition lsf - List all files on the partition lse - Show the path of the effects (D:ymir workeffect) collision - highlight the objects, monsters, npcs, players 0 - 1 colli - Equal with the collision function without argument, turn on/off the highlighting disconnect - Unfinished function! autorot - Set the rotation moving speed of the camera while pressing the A/D keys 0 - ∞ pickc - Show char informations, isRegistered, isAlive, isDead infoc - Information of another thing, usage: infoc <VID> regchre - Register new effect on the char, usage: regchre <effectType> and <effectFilename> setchra - Set character effect, usage: setchra <effectType> <Visibility 0 or 1> emoticon - Set emotion effect over the character, usage: emotion <number of the emoticon> perfinfo - Show performance informations (?) reload_locale - Reload the locale re - Reload uiGuild and uiInventory cooltime - Disable or enable the skills cooltime hidecursor - Hide the cursor showcursor - Show the cursor warp - Warp to x-y position web - Pop out a web browser hideui - Hide the user interface showui - Show the user interface setcspd - Change the camera moving speed 0 - ∞ savecmr - Export the current camera settings to the client folder, usage: savecmr <filename> loadcmr - Import camera settings, usage: loadcmr <filename in the client folder> setdefcmr - Set to default the camera settings setsight - Change the sight 0 - 100 setcombotype - Set the level of the combo, the server will kick you after few seconds because of combo hack check trans - Change the empire language converting, you can understand all empires without a ring 0 - 1 Rendering options: setrmadd setrmmod setrmaddrgb setrmmodrgb setspec restorerm If you have any question or suggestion please just reply to this topic. Kind Regards, Sanchez
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.