Jump to content
Forgot your email address? ×
  • Create New...


  • Posts

  • Joined

  • Days Won


msnas last won the day on July 27 2017

msnas had the most liked content!

1 Follower

About msnas


  • Gender

Recent Profile Visitors

2045 profile views

msnas's Achievements


Enthusiast (6/14)

  • Very Popular Rare
  • Reacting Well
  • Dedicated Rare
  • First Post
  • Collaborator

Recent Badges



  1. This guy is brilliant at what he does! Talking to him, it doesn't even feel like work, he is someone who knows what he is doing and enjoys interacting with customers. Definitely someone I recommend!
  2. Total [1] 0 / 0 / 1 (this server 1) -> /w command Testing Total 1 -> /u command Requesting total users. PLAYERS: 3 -> /reload y command, this is what the system is all about.
  3. The system isn't requesting anything new, the GetUserCount() already exists and only returns the size from m_map_kLogonAccount, so I think it will be fine even if you have 30.000 players online.
  4. This is a simple (and probably unwanted) tutorial that will give you all the online players for each core, including the auth We might argue about whether being in the select character phase is really in-game, but we will include it anyway Why? The /u and /w commands only provide data from the channel where the player is Since I needed absolute values - as soon as someone logs in - I went searching in the source and came across the GetUserCount() function I had 3 players in separated channels (auth, 1 and 99 respectively) I used the commands above and lastly the new one we are going to create Explanation? The function returns (in type DWORD) the size of m_map_kLogonAccount It is a map that stores the login values with class CLoginData It is also responsible for storing the values of the players online every 1h in usage.txt It is given an insert in the map every login a player makes through the boolean function InsertLogonAccount() What we're going to do is requesting the data from GetUserCount() and show it. Installing DB ClientManager.cpp: Search for: //RELOAD_ADMIN case HEADER_GD_RELOAD_ADMIN: ReloadAdmin(peer, (TPacketReloadAdmin*)data); break; //END_RELOAD_ADMIN Add below: case HEADER_GD_REQ_USER_COUNT: RequestUserCount(dwHandle); break; Search for: void CClientManager::ReloadAdmin(CPeer*, TPacketReloadAdmin* p) { .... } Add below: void CClientManager::RequestUserCount(DWORD dwHandle) { for (TPeerList::const_iterator it = m_peerList.begin(); it != m_peerList.end(); ++it) { CPeer* peer = *it; if (!peer->GetChannel()) continue; TPacketDGUserCount Info; peer->EncodeHeader(HEADER_DG_RET_USER_COUNT, dwHandle, sizeof(TPacketDGUserCount)); Info.user = GetUserCount(); peer->Encode(&Info, sizeof(TPacketDGUserCount)); } } ClientManager.h: Search for: //RELOAD_ADMIN void ReloadAdmin(CPeer * peer, TPacketReloadAdmin * p); //END_RELOAD_ADMIN Add below: void RequestUserCount(DWORD dwHandle); Common tables.h: Search for: HEADER_DG_RESPOND_CHANNELSTATUS = 181, Add below: HEADER_GD_REQ_USER_COUNT = 141, HEADER_DG_RET_USER_COUNT = 182, Search for: typedef struct SPacketDGChangeEmpirePriv { BYTE type; int value; BYTE empire; BYTE bLog; time_t end_time_sec; } TPacketDGChangeEmpirePriv; Add below: typedef struct SPacketDGUserCount { DWORD user; } TPacketDGUserCount; Game cmd_gm.cpp: Search for: //RELOAD_ADMIN case 'a': ch->ChatPacket(CHAT_TYPE_INFO, "Reloading Admin infomation."); db_clientdesc->DBPacket(HEADER_GD_RELOAD_ADMIN, 0, NULL, 0); sys_log(0, "Reloading admin infomation."); break; //END_RELOAD_ADMIN Add below: case 'y': ch->ChatPacket(CHAT_TYPE_INFO, "Requesting total users."); db_clientdesc->DBPacket(HEADER_GD_REQ_USER_COUNT, ch->GetDesc()->GetHandle(), NULL, 0); break; input.cpp: Search for: case 'a': db_clientdesc->DBPacket(HEADER_GD_RELOAD_ADMIN, 0, NULL, 0); sys_log(0, "Reloading admin infomation."); break; Add below: case 'y': db_clientdesc->DBPacket(HEADER_GD_REQ_USER_COUNT, d->GetHandle(), NULL, 0); sys_log(0, "Reloading user count."); break; input.h: Search for: //RELOAD_ADMIN void ReloadAdmin( const char * c_pData ); //END_RELOAD_ADMIN Add below: void UserCount(LPDESC d, const TPacketDGUserCount* p); input_db.cpp: Search for: // MYSHOP_PRICE_LIST void CInputDB::MyshopPricelistRes(LPDESC d, const TPacketMyshopPricelistHeader* p ) { LPCHARACTER ch; if (!d || !(ch = d->GetCharacter()) ) return; sys_log(0, "RecvMyshopPricelistRes name[%s]", ch->GetName()); ch->UseSilkBotaryReal(p ); } // END_OF_MYSHOP_PRICE_LIST Add below: void CInputDB::UserCount(LPDESC d, const TPacketDGUserCount* p) { sys_err("ADMIN WHISPER: USERCOUNT"); LPCHARACTER ch; if (!d || !(ch = d->GetCharacter())) return; ch->ChatPacket(CHAT_TYPE_PARTY, "PLAYERS: %d", p->user); } Search for: // RELOAD_ADMIN case HEADER_DG_RELOAD_ADMIN: ReloadAdmin(c_pData ); break; //END_RELOAD_ADMIN Add below: case HEADER_DG_RET_USER_COUNT: UserCount(DESC_MANAGER::instance().FindByHandle(m_dwHandle), (TPacketDGUserCount*)c_pData); break; Usage The usage is /reload y
  5. 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.
  6. It's not that hard. if (!PERF_CHECKER_RENDER_GAME) { m_kRenderTargetManager.RenderBackgrounds(); float fAspect=m_kWndMgr.GetAspect(); float fFarClip=m_pyBackground.GetFarClip(); m_pyGraphic.SetPerspective(30.0f, fAspect, 100.0, fFarClip); CCullingManager::Instance().Process(); m_kChrMgr.Deform(); m_kEftMgr.Update(); m_kRenderTargetManager.DeformModels();
  7. You can't put pc functions inside of global functions, it will crash the server.
  8. // char_battle.cpp #define MAP_INDEX 3 void CHARACTER::SetPKMode(BYTE bPKMode) { if (bPKMode >= PK_MODE_MAX_NUM) return; if (GetMapIndex() == MAP_INDEX) { if (IsGM()) bPKMode = PK_MODE_PROTECT; else bPKMode = PK_MODE_FREE; }
  9. when kill with npc.is_pc() begin syschat'1 player killed' end
  10. Probably he has this set to false, we don't know. But the reason is because he has the martysama source and doesn't know how to manage it.
  11. Go to packet_info.cpp and set true the packet AnswerMakeGuild: Set(HEADER_CG_ANSWER_MAKE_GUILD, sizeof(TPacketCGAnswerMakeGuild), "AnswerMakeGuild", true);

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.