Server Files [C# / .NET 10] Martysama v5.8 ServerFiles Tam C# Portu | Windows • Linux • 64 Bit

  • Konuyu açan Konuyu açan OnurKAH
  • Açılış Tarihi Açılış Tarihi
  • Yanıt Yanıt 0
  • Gösterim Gösterim 117

OnurKAH

Üye
Üye
Mesaj
9
Beğeni
10
Puan
405
Ticaret Puanı
0
Metin2 C# Sunucusu: Martysama v5.8 (UPDATE-2.5) C++ → C# / .NET 10 portu

Merhaba, Martysama v5.8 (Ikarus offline shop v2, çoklu dil) sunucu kaynağının C# / .NET 10'a çevrilmiş hâlini paylaşıyorum. Hazır çalışan Windows derlemesi ve kaynak kodu birlikte.

Kısaca: aynı oyun, aynı istemci, aynı veritabanı. FreeBSD ya da sanal makine gerekmez; Windows, Linux ve ARM64'te aynı koddan çalışır. Kod satır satır çevrildi: fonksiyon, sınıf ve değişken adları C++ ile aynı, C++ kaynağındaki bir yeri C# tarafında aynı adla bulursunuz.

Uyumluluk

  • İstemci: paket yapıları, şifreleme (DH2 anahtar değişimi ve 13 farklı şifre) ve LZO sıkıştırma orijinalle bayt bayt aynı. UPDATE-2.5 istemcisi hiçbir değişiklik yapılmadan bağlanır. Bir önceki temel istemci (02.11.2024, 32 bit yang) için ayrı bir uyumluluk profili var.
  • Veritabanı: aynı MySQL / MariaDB şeması ve BLOB düzenleri; mevcut veritabanı olduğu gibi çalışır. İsteğe bağlı tek bir geçiş betiği beş sütunu genişletir (aşağıda).
  • Sunucu dosyaları: aynı CONFIG, conf.txt, locale, quest'ler (Lua 5.0 lehçesi), haritalar ve protolar.
  • Oyun döngüsü orijinaldeki gibi tek iş parçacıklı, saniyede 25 pulse.

Nasıl doğrulandı:

  • Ortak yapılar: 196 struct, 793 alan, 1087 sabit; g++ -m32 çıktısıyla karşılaştırma → birebir
  • Ağ paketleri: 263 paket yapısı, 863 alan, 450 sabit → birebir
  • Şifreleme: orijinal cipher.cpp ile 480 canlı el sıkışma ve 15 360 paketlik oturumlar → bit düzeyinde aynı
  • Lua: 2952 quest parçasının hepsi derleniyor; ~420 davranış orijinal Lua 5.0 ile karşılaştırıldı → aynı
  • Birim testleri: 620 test (SQL dahil), Windows ve Linux → hepsi geçiyor
  • Oyun sistemleri: gerçek istemcinin protokolüyle oynayan QA botları: sosyal 36 senaryo, eşya 73 ve savaş 82 adım → hepsi geçiyor

Neden C#? Somut avantajlar

Aşağıdakilerin her biri bu portta ölçüldü ya da gerçekten yaşandı.

1. Bellek güvenliği: sunucu çökmez, yalnız o bağlantı kapanır

C++'ta bir dizinin dışına yazmak sessizce başka bir verinin üzerine yazar; sunucu ya o an çöker ya da çok sonra, ilgisiz görünen bir yerde. C#'ta her dizi ve Span erişimi sınır denetimlidir; işaretçi aritmetiği, serbest bırakılmış belleğe erişim ve başlatılmamış değişken yoktur.

  • Port tamamen "safe" C#: hiçbir yerde unsafe, işaretçi ya da fixed yok (başlangıçta 9018 yerdeydi). Bunu derleyiciye bağlı bir kural denetliyor; biri yeniden unsafe yazarsa derleme durur.
  • Bir paket işlenirken hata olursa yalnızca o bağlantı kapanır ve hata, dosya ve satır numarasıyla syserr'e yazılır; sunucu ve diğer oyuncular etkilenmez. C++'ta aynı hata bütün çekirdeği düşürürdü.
  • Sınır denetimi, orijinal kodda sessizce bellek bozan üç hatayı ortaya çıkardı: kişisel dükkânın 40–44. vitrin yuvaları vektörün dışına yazıyordu; küçülen depodan taşan eşya heap bloğunun dışına yazıyordu; 65 528 bayttan uzun şifre anahtarı akışı ayrılan bloğun dışına kopyalanıyordu. Üçü de düzeltildi.
  • Silinmiş bir karaktere kalan referans, C++'taki gibi serbest bırakılmış belleğe erişip çökme yapmaz.

2. Ekonomide taşmaya karşı denetimli aritmetik

C++'ta tamsayı taşması sessizdir: 2 147 483 647'ye 1 eklemek eksi sayı verir. Metin2'deki yang yaratma açıklarının çoğu buradan doğar. C#'ta checked aritmetik, 128 bitlik ara sonuç (Int128) ve doyan çarpım dilin standart araçları. Ekonomi kodu bunlarla yazıldı: taşan bir işlem, hiçbir değer değişmeden reddedilir. Boss yang'ı, dükkân vergisi, üç kat dükkân fiyatı ve won ticaretindeki açıklar böyle kapandı (aşağıda "C++ sürümünden farklar").

3. Her platformda aynı tür boyutları, aynı paket baytları

C++'ta long Windows'ta 32, Linux'ta 64 bittir; orijinal sunucu 32 bit FreeBSD için derlendi ve time_t 32 bitti. C#'ta int her yerde 32, long her yerde 64 bit; yapıların bellekteki düzeni açıkça yazılır.

  • Paket ve BLOB baytları Windows'ta, Linux'ta ve ARM64'te aynı. 196 struct, 793 alan ve 1087 sabit g++ -m32 çıktısıyla bayt bayt doğrulandı.
  • Paketler Span<byte> ve BinaryPrimitives ile kopyalanmadan ve işaretçi kullanılmadan okunuyor. Şifreleme bu yolla işaretçisiz yazılınca maliyeti yalnızca %1,3 arttı.
  • Zaman ve para içeride 64 bit tutuldu; 2038 sorunu kalktı.

4. Windows, Linux ve ARM64: tek kod, tek komut

  • FreeBSD ya da sanal makine gerekmez; sunucu Windows'ta doğrudan çalışır.
  • Windows'ta tek komut Linux paketini de üretir (tools\publish.ps1 -Runtime linux-x64). Self-contained paketler hedef makinede .NET kurulumu istemez.
  • Platforma özgü kod birkaç küçük yerde (kapanma sinyali, Windows hizmeti, Linux'ta poll(2)) ve hepsi aynı kod tabanında. Kaynaktaki CI tanımı (GitHub Actions) her commit'i Windows x64, Linux x64 ve ARM64'te derleyip test eder.

5. Çok çekirdekli işlemciyi kullanmak: asenkron soketler

  • .NET'in asenkron soket API'si Windows'ta IOCP, Linux'ta epoll kullanır; kod aynıdır. Şifreleme ve gönderim diğer işlemci çekirdeklerine taşındı. Oyun mantığı orijinaldeki gibi tek iş parçacığında kaldı; her oyuncunun paketleri yine sırayla gider.
  • Köy testinde bir oyun çekirdeği 900 yerine 2000 oyuncuyu lagsız taşıdı; 2000 oyuncuda ana döngü yalnızca %35 dolu. C++'ta aynısı için her platforma ayrı bir IOCP / epoll katmanı yazmak gerekirdi.
  • Günlük yazımı da ayrı bir iş parçacığına taşındı (SQL sorguları orijinalde de ayrıydı); oyun döngüsü diske beklemez.

6. Tek süreçte birden çok oyun çekirdeği

Orijinal kod global değişkenlerle dolu; C++'ta iki çekirdeği aynı süreçte çalıştırmak büyük bir yeniden yazım ister. .NET'te oyun kodu her çekirdek için ayrı bir AssemblyLoadContext'e yüklenir: her çekirdeğin global durumu kendine ait; .NET çalışma zamanı, kütüphaneleri ve bellek yönetimi ortak. Oyun kodunu değiştirmeden 3 çekirdekte boşta 175 MB, 100 oyuncuda 224 MB bellek tasarrufu.

7. Hata ayıklama ve teşhis

  • Sunucuyu Visual Studio, VS Code ya da Rider ile Windows'ta doğrudan çalıştırıp durma noktası koyabilirsiniz; uzak gdb ve FreeBSD sanal makinesi gerekmez.
  • Her istisna yığın iziyle günlüğe düşer; pakette sembol dosyaları (PDB) var. Core dump çözümlemeye gerek kalmaz.
  • Çalışma zamanının olayları (GC, bellek ayırma, iş parçacıkları) programın içinden okunur. Portun ayırma örnekleyicisi (M2_ALLOC_SAMPLE=1), Linux'taki fazla GC'nin kaynağını tek koşuda buldu: .NET'in Socket.Select'inin her çağrıda ayırdığı dizi. Yerine poll(2) konunca ayırma %31 azaldı.
  • Yerleşik ölçüm API'si (System.Diagnostics.Metrics): pulse, döngü süresi, oyuncu, paket ve GC sayaçları M2Manager'ın Performans sekmesine, isteğe bağlı olarak da Prometheus ve OpenTelemetry'ye gidiyor. dotnet-counters, dotnet-trace ve dotnet-dump çalışan sunucuya bağlanır.

8. Kurallar derleyicide, davranış testlerde

  • Roslyn analizörleri proje kurallarını derleme hatası yapıyor: unsafe kod, 32 bit zaman API'leri ve printf biçim hataları (ör. 64 bit bir değerin %u ile yazılması) derlemeyi durdurur.
  • 620 birim testi (xUnit), gerçek MariaDB'ye karşı SQL testleri, QA botları ve yük testi aynı dilde, aynı çözümde. Bir değişikliğin etkisi dakikalar içinde görülür.

9. Okunur ve bakımı kolay kod

  • Fonksiyon, sınıf ve değişken adları C++ ile aynı: C++ kaynağını bilen biri karşılığını hemen bulur. Quest'ler (Lua 5.0) değişmeden çalışır.
  • Davranışı değiştiren her yenilik bir ortam değişkeniyle kapatılabiliyor; sorun çıkarsa yeniden derlemeden eski davranışa dönülür.
  • Koleksiyonlar, metin, 64 bit zaman, dosya, ağ ve şifreleme (BouncyCastle) için standart kütüphane hazır; libthecore'un elle yazılmış birçok parçası .NET'in karşılıklarına dayanıyor.

Bedeli

  • Bellek: 100 oyunculu bir çekirdek ~450 MB; 32 bit orijinalden fazla (64 bit işaretçiler ve .NET nesne başlıkları).
  • GC: duraklama var ama küçük: 100 oyuncuda 108 saniyede ~10 ms, bütün ölçümlerde 0 geciken pulse. C++'taki gibi bir tampon havuzu gereksiz ayırmayı önlüyor.
  • Boyut: self-contained derlemede her süreç .NET çalışma zamanını taşır (~80 MB). .NET kuruluysa çalışma zamanına bağlı derleme toplam ~15 MB.
  • Ham işlemci hızı: C++ önde olabilir, ama burada darboğaz değil: ana döngü 100 oyuncuda pulse başına 0,14 ms.

Ölçülen iyileştirmeler

Aynı makinede, aynı veritabanı kopyasıyla, 100 botun aynı anda oynadığı yük testi (tools\bench.ps1). "İlk port", orijinalin G/Ç düzenini birebir taşıyan sürüm: yanıtlar bir sonraki pulse'ı bekliyordu, tıpkı C++'ta olduğu gibi.

İlk port (C++ düzeni) → Şimdi
  • Sohbet tepki süresi, 100 oyuncu (p50 / p95): 33,3 / 78,5 ms → 0,42 / 1,35 ms
  • Sohbet tepki süresi, tek oyuncu (p50): 63,0 ms → 0,26 ms
  • Oyuna giriş, 100 oyuncu (ortalama): 3,07 sn → 0,83 sn
  • Oyun çekirdeğinin açılışı: 21,3 sn → ~5 sn
  • /reload q sırasında çekirdeğin donması: 18,1 sn → 1,9 sn

  • Çöp toplayıcı (GC) lag yapıyor mu? 100 oyuncuda 108 saniyede toplam ~10 ms duraklama; bütün ölçümlerde saniyede 25,00 pulse ve 0 geciken pulse.
  • Ana döngü: 100 oyuncuda pulse başına ortalama 0,14 ms (40 ms'lik bütçenin %0,4'ü).
  • Kapasite (köy testi): 1000'e kadar bot aynı köyde, herkes herkesi görerek dolaşıyor. Bir çekirdek, şifreleme ve gönderim ana döngüdeyken (C++'taki gibi) 900, diğer işlemci çekirdeklerine taşınınca 1000 oyuncuyu lagsız taşıyor (test edilen en büyük sayı). 1000 oyuncuda ana döngü dörtte bir dolu.
  • Bellek: birden çok oyun çekirdeği tek süreçte çalıştırılabilir; 3 çekirdekte boşta 175 MB, 100 oyuncuda 224 MB daha az bellek. Tampon havuzuyla (C++'taki buffer_new / buffer_delete serbest listesi gibi) bellek ayırma saniyede 6,8 MB'tan 0,47 MB'a indi.

C++ sürümünden farklar: kapatılan açıklar ve düzeltilen hatalar

Çeviri, hatalar dahil orijinal davranışı korur; yalnız aşağıdakiler bilinçli olarak farklı. Davranışı değiştiren her düzeltme bir ortam değişkeniyle kapatılıp orijinal davranışa dönülebilir. Tam liste docs\PORT_FIXES.md'de.

Ekonomi açıkları (para yaratma / kaybetme)

  • Ticarette ve dükkânda won (cheque) çoğaltma: won miktarı işaretsiz sayı olarak eksiye çevrildiği için hiç düşülmüyordu.
  • Kişisel dükkânda 2³¹–2³² arası fiyatla yang yaratma: alıcının yang'ı eksiye düşüyor, satıcı fiyatı alıyordu.
  • Yang etkinliğinde boss yang'ı taşıp eksi parçalar 4 milyarlık düşüşlere dönüşüyordu.
  • /won_exchange: 999 won'da "1 al" 100 milyon yang'ı alıp won'u vermiyordu.
  • Ticarette won teklifinin 32 bit taşmasıyla üst sınır denetimini atlama; Ikarus açık artırmasında eksi teklifle yang ↔ won dönüşümü.
  • Alıcı üst sınırdayken ödeyenin parası yok oluyordu (takas, özel dükkân, yerden yang).
  • Sekmeli NPC dükkânı (CShopEx): disable_shop_price_3x açıkken başka imparatorluktan gelen müşteriye tek fiyat gösterip üç kat kesiyordu.

Eşya kaybı

  • Depoda yığın birleştirirken artan eşya sahipsiz kalıp veritabanından siliniyordu.
  • Kemer envanterinin 255–257. hücrelerinde eşyanın üzerine yazılıyordu.

Kararlılık ve güvenlik

  • Bozuk paketler artık yalnızca o bağlantıyı kapatır; orijinalde tanımsız davranış (olası çökme).
  • Kod tamamen "safe" C#: hiçbir yerde işaretçi ya da unsafe yok (başlangıçta 9018 yerdeydi). Taşan bir paket sunucunun belleğini bozamaz.
  • GM'in /mob komutuna yanlışlıkla eksi sayı yazması çekirdeği kilitliyordu.
  • Çekirdekler arası yarışta silinen partinin "hayalet parti" olarak geri gelmesi.
  • 65 528 bayttan uzun şifre anahtarı akışında bellek (heap) taşması.

İstemciyle uyum

  • Yükseltme (refine) penceresi: orijinal sunucu 64 baytlık paket gönderiyordu, bu sürümün istemcisi 60 bayt bekliyor. Sonuç: yanlış başarı oranı ve malzemeler, ardından paket akışının kayması.
  • Canavar konum eşzamanlaması 0–64 sn gecikiyordu: sunucu saati float tutulduğu için 2020'den beri yalnızca 64 saniyede bir ilerliyordu.
  • Karakter oluşturma hata paketi, lonca yetenek paketinin boyutu ve bilinmeyen Ikarus alt başlığı (bağlantıyı donduruyordu) düzeltildi.

64 bit ve 2038

  • Zaman, para, adet ve süreler sunucunun içinde 64 bit; istemci paketlerine ve veritabanına eskisi gibi 32 bit alanlara sığdırılarak yazılır (baytlar aynı).
  • Orijinalde 2038-01-19'dan sonra: süreli eşyalar silinir, premium bozulur, oyun çekirdekleri veritabanına hiç bağlanamaz.
  • Uzun açık kalmada: milisaniye saati 49,7 günde taşar (yetenek bekleme süreleri bozulur), Mavi Ejderha 24,86. günden 49,7. güne kadar yetenek kullanmaz, pulse sayacı 994. günde taşar.

Veritabanı

  • Seviye 100 üstü karakterin exp'i (2,15–2,5 milyar) int(11) sütuna sığmıyordu. MariaDB'nin varsayılan katı modunda karakterin bütün kaydı (seviye, konum, yang) düşüyordu.
  • Özel dükkân fiyat listesi 4,29 milyar üstü fiyatları yanlış öneriyordu (5 milyar → 705 milyon).
  • Para günlüğünün 2³¹ üstü saatlik toplamları ve 2038 sonrası kuşak günlüğü yazılamıyordu.
  • Bu dördü için isteğe bağlı geçiş betiği var (tools\migrate_db.ps1; yalnız sütun türleri değişir). Geçiş yapılmazsa sunucu eskisi gibi çalışır ve açılışta uyarı yazar.
  • Para günlüğü MariaDB'de hiç yazılmıyordu (INSERT DELAYED, InnoDB tablosunda reddediliyor); kodda düzeltildi.

Yönetim ve altyapı

  • M2Manager (Windows): başlat / durdur, günlükler, GM listesi, hesaplar (yeni hesap, şifre, engelleme, oyundan atma), duyuru, oyuncu sayısı, performans sekmesi.
  • Çöken süreci otomatik yeniden başlatma, Windows hizmeti, Linux için systemd birimleri.
  • Zamanlanmış veritabanı yedeği (mariadb-dump, sıkıştırılmış, eskilerini siler).
  • Çok kanallı ve çok makineli kurulum: kanal, çekirdek ve harita dağılımını verirsiniz; çalışma klasörünü ve port planını betik üretir.
  • Metrikler: Prometheus ve OpenTelemetry'ye aktarma (isteğe bağlı).
  • Test araçları: yük testi botu, gerçek istemci gibi kare hızında okuyan bot testi, üç QA paketi.
  • .NET kurulumu gerektirmeyen Windows ve Linux (x64, ARM64) paketleri.

İndirme ve kurulum

İndirme:
VirusTotal:

  • Build: Hazır çalışan sunucu (Windows 10/11 x64, .NET kurulumu gerekmez): çalışma klasörü, M2Manager, taşınabilir MariaDB ve veritabanı
  • Source: Kaynak kodu (.NET 10 SDK ile derlenir): sunucu, M2Manager, testler, QA botları, kurulum araçları, belgeler

  1. Build\README.md → "İlk açılıştan önce: veritabanı geçişi" (bir kez, üç komut).
  2. Build\sunucu\M2Manager.exe → Başlat.
  3. M2Manager → Hesaplar → Yeni hesap. İstemcinin serverinfo.py dosyasında IP 127.0.0.1, auth portu 30001, kanal 1 portu 30003.

Linux kurulumu ve diğer ayrıntılar: Source\README.md. Ölçümler ve onları kapatan anahtarlar: Source\docs\FAZ2.md.

Client olarak Martysama v5.8 Clientini 2.5 update'i ile kullanabilirsiniz

Bilinen notlar

  • Quest bayrakları henüz 32 bit; 2038 sonrası bir zamanı saklayan quest bayrağı bozulur (sıradaki iş).
  • Sunucu dosyalarındaki orijinal veri hataları olduğu gibi duruyor (eksik canavar hareket dosyaları, guild_manage.quest, bazı çevirisiz Korece metinler); bunlar koddan değil, orijinal sunucu da aynılarını yazar.

Screenshot_1.webp
Screenshot_2.webp
Screenshot_3.webp
 
Geri
Üst