[C++] Renewal For GM's /full_set Commands

  • Konbuyu başlatan Konbuyu başlatan Admin
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 0
  • Görüntüleme Görüntüleme 56

Admin

Metin2Lobby
Yönetici
Founder
Katılım
6 Mayıs 2022
Mesajlar
52,647
Ticaret : 1 / 0 / 0
GÜNCELLEME - 25/04/2024

Metin2.dev'den bir kullanıcının isteği üzerine GM karakterin, kuşanılacak itemlerin seviyesinden düşük olmaması için kontrol eklendi, eğer karakter 90 Lvl'den düşükse, fonksiyon hata verip devam etmeyecektir. Bunun dışında item_attr bölümünde her item için ayrı bilgilendirme mesajı eklendi.

ÖNEMLİ BİLGİ

Bu düzenlemeyi sadece C++11 standartlarında bir derleyici kullananlar uygulayabilir !
(C++11 Destekleyen Derleyiciler; GCC 4.8 ve üstü, Clang 3.3 ve üstü, VS 12 ve üstü)

GİRİŞ & PROBLEM

Merhaba, bugün GM komutlarından /full_set komutunu ve ona bağlı olan diğer komutları (do_all_skill_master, do_item_full_set, do_attr_full_set) komple yenileyeceğiz. Kullanışsız itemleri ve efsunlarını değiştirip, fonksiyon içeriğini daha modern hale getirip, (C++11 ve üstü) bilgilendirme mesajları ekleyeceğiz. Taş ve cevher eklemeye gerek duymadım, zaten itemler ve efsunları yeterince op oldu. :D

NEDEN ?

Test aşamasında veya live sunucuda GM karakter ile deneme yapmak isteyen veya sadece karakterin, güçlendirmek isteyen herkes DB üzerinden kendine özel item üretmekle uğraşmıştır (veya çeşitli yardımcı scriptler ile), oyunun orijinalinde var olan bu kod ile ekstra bir zahmetten kurtulmayı hedefliyoruz.

ÖNERİ

Buradan edinilecek eşyalar çok yüksek oranlı olacağı için, bu kodları sadece GM_IMPLEMENTOR yetkisine sahip kişilerin kullanımına açmanızı tavsiye ederim.

ÇÖZÜM

Öncelikle "cmd_gm.cpp" dosyası açılır.

Kod:
// FIND THIS FUNCS; ACMD (do_full_set) {     extern void do_all_skill_master (LPCHARACTER ch, const char* argument, int cmd, int subcmd);     do_all_skill_master (ch, NULL, 0, 0); // etc. } ACMD (do_all_skill_master) {     ch->SetHorseLevel (SKILL_MAX_LEVEL); // etc. } ACMD (do_item_full_set) {     BYTE job = ch->GetJob();     LPITEM item; // etc. } ACMD (do_attr_full_set) {     BYTE job = ch->GetJob();     LPITEM item;     // etc. } // CHANGE ALL; // FULL_SET_COMMAND_RENEWAL ACMD (do_all_skill_master)   // DevFix 54 (Thanks to MartySaMa for do_all_skill_master renewal) {     ch->SetHorseLevel (HORSE_MAX_LEVEL);     for (int i = 0; i < SKILL_MAX_NUM; i++)     {         if (ch->CanUseSkill (i))         {             switch (i)             {                 case SKILL_COMBO:                     ch->SetSkillLevel (i, 2);                     break;                 case SKILL_LANGUAGE1:                 case SKILL_LANGUAGE2:                 case SKILL_LANGUAGE3:                     ch->SetSkillLevel (i, 20);                     break;                 case SKILL_HORSE_SUMMON:                     ch->SetSkillLevel (i, 10);                     break;                 case SKILL_HORSE:                     ch->SetSkillLevel (i, 21);                     break;                 case SKILL_HORSE_WILDATTACK:                 case SKILL_HORSE_CHARGE:                 case SKILL_HORSE_ESCAPE:                 case SKILL_HORSE_WILDATTACK_RANGE:                     ch->SetSkillLevel (i, 20);                     break;                 default:                     ch->SetSkillLevel (i, SKILL_MAX_LEVEL);                     break;             }             ch->ChatPacket (CHAT_TYPE_INFO, "<GM> All of your skills are being upgraded to master...");         }         else         {             switch (i)             {                 case SKILL_HORSE_WILDATTACK:                 case SKILL_HORSE_CHARGE:                 case SKILL_HORSE_ESCAPE:                 case SKILL_HORSE_WILDATTACK_RANGE:                     ch->SetSkillLevel (i, 20);                     break;             }             ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Your horse skills are being upgraded to master...");         }     }     ch->SetHorseLevel (21); // Soldier Horse     ch->ComputePoints();     ch->SkillLevelPacket();     ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Upgrade is done successfully!"); } ACMD (do_item_full_set)   // DevFix 54 {     if (ch->GetLevel() < 90) // 90 Lvl is enough for items that i use in this function, if you change vnums in the future, make sure change this level limit! - [MT2Dev Note] - 25/04/2024     {         ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You need to at least 90 Level for use this command!");         return;     }     struct GMItemInfo // General structure for all job types. - [MT2Dev Note] - 23/04/2024     {         DWORD vnum;         BYTE count;     };     LPITEM pItem; // We need to look all equipments and unequip all of them (Thanks to Ken for the idea). - [MT2Dev Note] - 23/04/2024     for (BYTE i = 0; i < WEAR_MAX_NUM; i++) // Using WEAR_MAX_NUM instead of hard-coded value. - [MT2Dev Note] - 23/04/2024     {         if (! (pItem = ch->GetWear (i)))         {             continue;         }         ch->UnequipItem (pItem);     }     BYTE job = ch->GetJob();     switch (job)   // Ps; Item & Bonus names were written with reference to the Turkish locale.. - [MT2Dev Note] - 23/05/2023     {         case JOB_SURA:         {             const std::vector<GMItemInfo> Suraitems =             {                 {11973, 1}, // Sura Kraliyet Zirhi      (WEAR_BODY)                 {13149, 1}, // Titan Kalkan +9          (WEAR_SHIELD)                 {15419, 1}, // Kirin Ayakkabi +9        (WEAR_FOOTS)                 {239,   1}, // Ilahi Kilic +9           (WEAR_WEAPON)                 {12539, 1}, // Boynuzlu Kask +9         (WEAR_HEAD)                 {14209, 1}, // Cennetin Gozu Bilezik +9 (WEAR_WRIST)                 {17209, 1}, // Cennetin Gozu Kupe +9    (WEAR_EAR)                 {16209, 1}, // Cennetin Gozu Kolye +9   (WEAR_NECK)                 {71143, 1}, // Mutluluk Yuzugu          (WEAR_UNIQUE1)                 {72702, 1}, // Ruzgarin Ayakkabilari    (WEAR_UNIQUE2)             };             for (const auto& GMItemInfo: Suraitems)             {                 auto item = ITEM_MANAGER::instance().CreateItem (GMItemInfo.vnum);                 if (!item || !item->EquipTo (ch, item->FindEquipCell (ch)))                 {                     M2_DESTROY_ITEM (item);                 }             }         }         break;         case JOB_WARRIOR:         {             const std::vector<GMItemInfo> Warrioritems =             {                 {11971, 1}, // Savasci Kraliyet Zirhi   (WEAR_BODY)                 {13149, 1}, // Titan Kalkan +9          (WEAR_SHIELD)                 {15419, 1}, // Kirin Ayakkabi +9        (WEAR_FOOTS)                 {229,   1}, // Ay Kilici +9             (WEAR_WEAPON)                 {12269, 1}, // Korku Maskesi +9         (WEAR_HEAD)                 {14209, 1}, // Cennetin Gozu Bilezik +9 (WEAR_WRIST)                 {17109, 1}, // Abanoz Kupe +9           (WEAR_EAR)                 {16209, 1}, // Cennetin Gozu Kolye +9   (WEAR_NECK)                 {71143, 1}, // Mutluluk Yuzugu          (WEAR_UNIQUE1)                 {72702, 1}, // Ruzgarin Ayakkabilari    (WEAR_UNIQUE2)             };             for (const auto& GMItemInfo: Warrioritems)             {                 auto item = ITEM_MANAGER::instance().CreateItem (GMItemInfo.vnum);                 if (!item || !item->EquipTo (ch, item->FindEquipCell (ch)))                 {                     M2_DESTROY_ITEM (item);                 }             }         }         break;         case JOB_SHAMAN:         {             const std::vector<GMItemInfo> Shamanitems =             {                 {11974, 1}, // Saman Kraliyet Zirhi     (WEAR_BODY)                 {13149, 1}, // Titan Kalkan +9          (WEAR_SHIELD)                 {15419, 1}, // Kirin Ayakkabi +9        (WEAR_FOOTS)                 {7159,  1}, // Mutluluk Yelpazesi +9    (WEAR_WEAPON)                 {12679, 1}, // Kardinal Sapka +9        (WEAR_HEAD)                 {14209, 1}, // Cennetin Gozu Bilezik +9 (WEAR_WRIST)                 {17209, 1}, // Cennetin Gozu Kupe +9    (WEAR_EAR)                 {16209, 1}, // Cennetin Gozu Kolye +9   (WEAR_NECK)                 {71143, 1}, // Mutluluk Yuzugu          (WEAR_UNIQUE1)                 {72702, 1}, // Ruzgarin Ayakkabilari    (WEAR_UNIQUE2)             };             for (const auto& GMItemInfo: Shamanitems)             {                 auto item = ITEM_MANAGER::instance().CreateItem (GMItemInfo.vnum);                 if (!item || !item->EquipTo (ch, item->FindEquipCell (ch)))                 {                     M2_DESTROY_ITEM (item);                 }             }         }         break;         case JOB_ASSASSIN:         {             const std::vector<GMItemInfo> Assassinitems =             {                 {11972, 1}, // Ninja Kraliyet Zirhi     (WEAR_BODY)                 {13149, 1}, // Titan Kalkan +9          (WEAR_SHIELD)                 {15419, 1}, // Kirin Ayakkabi +9        (WEAR_FOOTS)                 {4019,  1}, // Dokuz Pala +9            (WEAR_WEAPON)                 {12399, 1}, // Ork Kapsonu +9           (WEAR_HEAD)                 {14209, 1}, // Cennetin Gozu Bilezik +9 (WEAR_WRIST)                 {17169, 1}, // Kristal Kupe +9          (WEAR_EAR)                 {16209, 1}, // Cennetin Gozu Kolye +9   (WEAR_NECK)                 {71143, 1}, // Mutluluk Yuzugu          (WEAR_UNIQUE1)                 {72702, 1}, // Ruzgarin Ayakkabilari    (WEAR_UNIQUE2)             };             for (const auto& GMItemInfo: Assassinitems)             {                 auto item = ITEM_MANAGER::instance().CreateItem (GMItemInfo.vnum);                 if (!item || !item->EquipTo (ch, item->FindEquipCell (ch)))                 {                     M2_DESTROY_ITEM (item);                 }             }         }         break;     }     ch->ChatPacket (CHAT_TYPE_INFO, "<GM> New items created and equipped successfully!");     ch->ComputePoints(); } ACMD (do_attr_full_set)   // DevFix 54 {     BYTE job = ch->GetJob();     switch (job) // I know SetForceAttribute use UpdatePacket for each time but this command only useable by few GM's so that's not a big deal.. - [MT2Dev Note] - 23/04/2024     {         case JOB_WARRIOR:         case JOB_ASSASSIN:         case JOB_SURA:         case JOB_SHAMAN:         {             LPITEM item;             item = ch->GetWear (WEAR_HEAD);             if (item)             {                 item->ClearAttribute();                 item->SetForceAttribute (0, APPLY_MAX_HP, 32000); // Maks. HP                 item->SetForceAttribute (1, APPLY_CON,      100); // Canlilik                 item->SetForceAttribute (2, APPLY_INT,      100); // Zeka                 item->SetForceAttribute (3, APPLY_STR,      100); // Guc                 item->SetForceAttribute (4, APPLY_DEX,      100); // Ceviklik                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Helmet attr is changed successfully..");             }             else             {                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You're not wear any helmet!");             }             item = ch->GetWear (WEAR_WEAPON);             if (item)             {                 item->ClearAttribute();                 item->SetForceAttribute (0, APPLY_ATT_SPEED,                100); // Saldiri Hizi                 item->SetForceAttribute (1, APPLY_MOV_SPEED,                100); // Hareket Hizi                 item->SetForceAttribute (2, APPLY_CAST_SPEED,              1000); // Buyu Hizi                 item->SetForceAttribute (3, APPLY_SKILL_DAMAGE_BONUS,      1000); // Beceri Hasari                 item->SetForceAttribute (4, APPLY_NORMAL_HIT_DAMAGE_BONUS, 1000); // Ortalama Zarar                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Weapon attr is changed successfully..");             }             else             {                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You're not wear any weapon!");             }             item = ch->GetWear (WEAR_SHIELD);             if (item)             {                 item->ClearAttribute();                 item->SetForceAttribute (0, APPLY_CRITICAL_PCT,    100); // Kritik Vurus Sansi                 item->SetForceAttribute (1, APPLY_PENETRATE_PCT,   100); // Delici Vurus Sansi                 item->SetForceAttribute (2, APPLY_IMMUNE_STUN,       1); // Sersemlik Bagisikliligi                 item->SetForceAttribute (3, APPLY_IMMUNE_SLOW,       1); // Yavaslama Bagisikliligi                 item->SetForceAttribute (4, APPLY_IMMUNE_FALL,       1); // Dusmeye Karsi Bagisiklilik                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Shield attr is changed successfully..");             }             else             {                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You're not wear any shield!");             }             item = ch->GetWear (WEAR_BODY);             if (item)             {                 item->ClearAttribute();                 item->SetForceAttribute (0, APPLY_MAX_HP,       32000); // Maks HP                 item->SetForceAttribute (1, APPLY_BLOCK,          100); // Bloklama                 item->SetForceAttribute (2, APPLY_RESIST_SWORD,   100); // Kilic Sav.                 item->SetForceAttribute (3, APPLY_RESIST_TWOHAND, 100); // Cift-el Sav.                 item->SetForceAttribute (4, APPLY_RESIST_DAGGER,  100); // Bicak Sav.                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Armor attr is changed successfully..");             }             else             {                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You're not wear any armor!");             }             item = ch->GetWear (WEAR_FOOTS);             if (item)             {                 item->ClearAttribute();                 item->SetForceAttribute (0, APPLY_MAX_HP,    32000); // Maks HP                 item->SetForceAttribute (1, APPLY_MAX_SP,    32000); // Maks SP                 item->SetForceAttribute (2, APPLY_RESIST_BELL, 100); // Can Sav.                 item->SetForceAttribute (3, APPLY_RESIST_FAN,  100); // Yelpaze Sav.                 item->SetForceAttribute (4, APPLY_RESIST_BOW,  100); // Ok Sav.                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Shoes attr is changed successfully..");             }             else             {                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You're not wear any shoes!");             }             item = ch->GetWear (WEAR_WRIST);             if (item)             {                 item->ClearAttribute();                 item->SetForceAttribute (0, APPLY_POISON_REDUCE,           100); // Zehre Karsi Koyma                 item->SetForceAttribute (1, APPLY_ATT_GRADE_BONUS,        1000); // Saldiri Degeri                 item->SetForceAttribute (2, APPLY_MAGIC_DEF_GRADE,         100); // Buyu Sav.                 item->SetForceAttribute (3, APPLY_NORMAL_HIT_DEFEND_BONUS, 100); // Ortalama Zarara Karsi Koyma                 item->SetForceAttribute (4, APPLY_ATTBONUS_MONSTER,        100); // Canavarlara Karsi Guc                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Wrist attr is changed successfully..");             }             else             {                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You're not wear any wrist!");             }             item = ch->GetWear (WEAR_NECK);             if (item)             {                 item->ClearAttribute();                 item->SetForceAttribute (0, APPLY_ATTBONUS_WARRIOR,    100); // Savascilara Karsi Guclu                 item->SetForceAttribute (1, APPLY_ATTBONUS_ASSASSIN,   100); // Ninjalara Karsi Guclu                 item->SetForceAttribute (2, APPLY_ATTBONUS_SURA,       100); // Suralara Karsi Guclu                 item->SetForceAttribute (3, APPLY_ATTBONUS_SHAMAN,     100); // Samanlara Karsi Guclu                 item->SetForceAttribute (4, APPLY_SKILL_DEFEND_BONUS,  100); // Beceri Hasarina Karsi Koyma                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Necklace attr is changed successfully..");             }             else             {                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You're not wear any necklace!");             }             item = ch->GetWear (WEAR_EAR);             if (item)             {                 item->ClearAttribute();                 item->SetForceAttribute (0, APPLY_ATTBONUS_HUMAN,   100); // Yari Insanlara Karsi Guclu                 item->SetForceAttribute (1, APPLY_ATTBONUS_ANIMAL,  100); // Hayvanlara Karsi Guclu                 item->SetForceAttribute (2, APPLY_ATTBONUS_ORC,     100); // Orklara Karsi Guclu                 item->SetForceAttribute (3, APPLY_ATTBONUS_DEVIL,   100); // Seytanlara Karsi Guclu                 item->SetForceAttribute (4, APPLY_ATTBONUS_UNDEAD,  100); // Olumsuzlere Karsi Guclu                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM> Earring attr is changed successfully..");             }             else             {                 ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You're not wear any earring!");             }         }         break;     }     ch->ComputePoints(); } ACMD (do_full_set)   // DevFix 54 {     if (ch->GetLevel() < 90) // 90 Lvl is enough for items that i use in the function, if you change vnums in the future, make sure change this level limit! - [MT2Dev Note] - 25/04/2024     {         ch->ChatPacket (CHAT_TYPE_INFO, "<GM Control> You need to at least 90 Level for use this command!");         return;     }     do_all_skill_master (ch, NULL, 0, 0);     do_item_full_set (ch, NULL, 0, 0);     do_attr_full_set (ch, NULL, 0, 0);     ch->ChatPacket (CHAT_TYPE_INFO, "<GM> All is DONE!"); } // END_OF_FULL_SET_COMMAND_RENEWAL

Metin2 Sunucularında GM Yetkililer İçin Yeni Renkli Komut Sistemi: [C++] Renewal For GM's /full_set

Metin2 özel sunucularında yetkililerin deneyimini zenginleştirmek ve oyun içi yönetim kolaylığı sağlamak amacıyla geliştirilen /full_set komutu, GM (Game Master) kullanıcılar için yenilikçi bir araçtır. Bu komut sayesinde yetkililer, hızlıca tam ekipman seti alabilir, test yapabilir ve oyun içi dengeleri gözlemleyebilirler. Bu yazıda, [C++] Renewal For GM's /full_set komutunun nasıl geliştirildiğini, hangi dosyalarda değişiklik yapıldığını ve nasıl entegre edileceğini detaylı şekilde ele alacağız.

Neden /full_set Komutu?
Metin2 özel sunucularında test süreçleri oldukça önemlidir. Özellikle PvP odaklı sunucularda, farklı ekipman setlerinin performans karşılaştırması gibi durumlarda yetkililerin tüm itemleri teker teker eklemesi çok zaman alır. Bu noktada [/B]/full_set komutu, belirli bir setin tüm parçalarını bir anda vererek hem zaman kazandırır hem de test sürecini hızlandırır.

Komutun Kullanımı
Yetkili oyuncu oyun içine girip '/full_set' komutunu yazdığında, önceden tanımlanmış olan bir ekipman seti karakterine otomatik olarak eklenir. Bu set, hem silah hem zırh hem de aksesuarları içerebilir. Bu işlem, Python tarafında bir arayüz ile veya doğrudan C++ tarafında oyun motoruna entegre edilerek yapılabilir.

C++ Tarafında Entegrasyon
Komutun çalışabilmesi için game/src/char/char.cpp veya benzeri bir dosyada komut tanımı yapılmalıdır. Burada
/full_set komutu yakalanır ve gerekli item listesi karaktere eklenir. Örnek olarak:

- Karakter kontrolü yapılır
- Yetki seviyesi kontrol edilir
- Ekipman seti tanımı yapılır (örneğin: Dragon Set, Phoenix Set)
- Her item DB üzerinden karaktere verilir

Önemli Not: Yetkisiz kullanıcıların bu komuta erişememesi için mutlaka bir yetki kontrolü yapılmalıdır. Aksi halde güvenlik açıkları oluşabilir.

Python Tarafında GUI Entegrasyonu
Eğer daha kullanıcı dostu bir arayüz istiyorsanız, PyUI veya UIScript kullanarak bir pencere tasarlayabilirsiniz. Bu pencere sayesinde yetkililer, hangi seti almak istediklerini seçebilir ve komutu bu şekilde tetikleyebilirler. Bu işlem py_root klasörü altında geliştirilir.

Test ve Hata Ayıklama
Sistem entegrasyonundan sonra mutlaka test edilmelidir. Bu aşamada auth server ve game server senkronize çalışmalı, item ID'ler doğru olmalı ve karakterin envanteri yeterli boşluğa sahip olmalıdır. Hata ayıklama işlemleri sırasında log sisteminden faydalanmak büyük avantaj sağlar.

Sonuç
Metin2 özel sunucularında yetkililerin işini kolaylaştıran
/full_set komutu, C++ ve Python tabanlı geliştirme yetenekleriyle kolayca entegre edilebilir. Bu tür sistemler, hem geliştirme hem de test süreçlerini hızlandırarak sunucu kalitesini artırır. Doğru yapılandırılmış bir core yapısı sayesinde yetkili komutları güvenli ve esnek bir şekilde yönetilebilir.


New Colored Command System for GMs on Metin2 Servers: [C++] Renewal For GM's /full_set

The /full_set command, developed to enhance the experience of administrators and provide ease of management within the game on Metin2 private servers, is an innovative tool for GM (Game Master) users. With this command, administrators can quickly obtain full equipment sets, conduct tests, and observe in-game balances. In this article, we will examine in detail how the [C++] Renewal For GM's /full_set command was developed, which files were modified, and how it is integrated.

Why the /full_set Command?
Testing processes are highly important on Metin2 private servers. Especially on PvP-focused servers, comparing the performance of different equipment sets can be time-consuming if administrators have to add items one by one. At this point, the

/full_set command saves time and accelerates the testing process by automatically adding all pieces of a predefined set to the character.

Usage of the Command
When an authorized player enters the command '/full_set' inside the game, a pre-defined equipment set is automatically added to their character. This set can include weapons, armor, and accessories. This process can be implemented via a UI in Python or directly integrated into the game engine on the C++ side.

Integration on the C++ Side
To make the command functional, a command definition must be made in a file such as game/src/char/char.cpp. Here, the
/full_set command is intercepted, and the necessary item list is added to the character. Example steps include:

- Checking the character
- Verifying permission level
- Defining the equipment set (e.g., Dragon Set, Phoenix Set)
- Adding each item to the character via the database

Important Note: A permission check must be performed to ensure unauthorized users cannot access this command. Otherwise, security vulnerabilities may arise.

GUI Integration on the Python Side
If you want a more user-friendly interface, you can design a window using PyUI or UIScript. With this window, administrators can select which set they want to receive and trigger the command accordingly. This implementation is developed under the py_root folder.

Testing and Debugging
After the system integration, thorough testing must be conducted. During this phase, the auth server and game server should work synchronously, item IDs must be correct, and the character's inventory must have enough free space. Utilizing the logging system during debugging offers significant advantages.

Conclusion
The
/full_set command, which facilitates the work of administrators on Metin2 private servers, can be easily integrated with C++ and Python development capabilities. Such systems accelerate both development and testing processes, thereby improving server quality. With a well-structured core architecture, admin commands can be managed securely and flexibly.
 

Şuan Bu Konuyu Görüntüleyen Kullanıcılar (Toplam : 0, Üye : 0, Misafir : 0)

Benzer konular

Geri
Üst Alt