Egy MMORPG-t az különböztet meg az egyjátékos játékoktól, hogy minden mögött, amit látsz, ott a szerver. Az Ancient Online: Remastered devlogjának első részében elmagyarázzuk, hogyan építünk MMORPG-t Unreal Engine 5.8-cal: hogyan oszlik meg a munka a kliens és a szerver között, hogyan szimuláljuk a világot, és hogyan tudunk élőben szerkeszteni egyetlen, egységes világot.
Két darab: kliens és szerver
Az Ancient Online két különálló szoftverből áll:
- A kliens C++ nyelven, Unreal Engine 5.8-on készül. A megjelenítés, a hang, a felület és a bevitel a dolga.
- A szerver független az Unrealtől: a nulláról írtuk meg C++23-ban. A játék összes szabálya itt fut.
Miért írtuk meg a szervert külön, és nem a motoron belül? Mert egy MMORPG-szervernek másra van szüksége, mint egy játékmotornak: grafika nincs, viszont van több ezer entitás, tartósan tárolt adat, hosszú üzemidő és kiszámítható teljesítmény iránti igény. A különálló szerverrel csak azt a munkát végezzük el, amelyre szükség van, és minden bájtot kézben tartunk.
Az autoritatív szerver: nem bízunk a kliensben
Az alapszabályunk: a kliens állít valamit, a szerver dönt.
- Mozgás: A játékos bevitele a szerverhez megy, és a szerver számolja ki a pozíciót. A kliens a szerver által közölt pozíciót jeleníti meg, sima interpolációval. Így az a kliens, amely „elteleportálja magát” vagy átsétál a falon, a szerver szemében nem jut sehová.
- Harc: Az olyan kimeneteleket, mint a sebzés, a találat és a hátralökés, a szerver számolja ki. A szerver dönti el például, hogy egy kombótalálat ledönti-e az ellenfelet, és az eredményt mindenkinek egyszerre közli.
- Gazdaság: A tárgyak, a pénz és a bolti tranzakciók csak a szerveren és az adatbázisban változnak.
Ennek a megközelítésnek az az ára, hogy jól kell kezelnünk a hálózati késleltetést; a nyeresége pedig egy olyan világ, ahol sokkal nehezebb csalni, és mindenkire ugyanazok a szabályok vonatkoznak.
25 Hz-es világciklus
A szerver másodpercenként 25-ször frissíti a világot. Minden lépésben előrehalad a szörnyek MI-je, a mozgás, a harc, a képességek hatásai és az időjárás. A rögzített lépésköz kiszámíthatóvá teszi a szimulációt, és biztosítja, hogy ugyanaz a bevitel mindig ugyanazt az eredményt adja.
Nem kell minden játékosnak mindenről tudnia. A szerver rácsra osztja a világot, és minden játékosnak csak az érdeklődési területén belüli (vagyis a közeli) entitásokról küld frissítést. Így a hálózati forgalom még egy zsúfolt városban is észszerű marad.
Könnyű bináris protokoll
A kliens és a szerver között nincs JSON és nincs REST; minden üzenet rögzített felépítésű bináris csomag. Ezzel sávszélességet és processzoridőt is megtakarítunk. Amikor új rendszert vezetünk be (például az időjárást, a mozgást vagy a játékon belüli fiókszolgáltatást), külön, verziózott csomagszerződést írunk hozzá, és közös fejlécfájlokkal garantáljuk, hogy a kliens és a szerver ugyanabból a szerződésből dolgozik.
Perzisztencia: mentés úgy, hogy a játéknak ne kelljen várnia
A játékosok adatai PostgreSQL adatbázisban élnek. Az viszont nem elfogadható, hogy a világciklus minden lépésben az adatbázisra várjon. Ezért a mentés a háttérben történik, késleltetett (write-behind) mentésként: a világszerver sorba (Redis) teszi a változásokat, egy háttéríró pedig kötegekben írja be őket az adatbázisba. A játék menete nem függ az adatbázis sebességétől.
Csatornák és a bejelentkezési szerver
A játékosok először egy bejelentkezési szerverhez csatlakoznak, amely a fiók ellenőrzését, a karakterválasztást és a munkamenetet kezeli. Ezután a négy játékcsatorna (CH1–CH4) egyikére lépnek tovább. A csatornák élő állapotát a játék weboldalának Szerverállapot oldala mutatja.
Egyetlen egységes világ, élőben szerkesztve
Az Ancient Online világát egyetlen, egységes világnak terveztük, nem töltőképernyőkkel elválasztott különálló térképeknek; cellákra van osztva, és streameléssel töltődik be.
A világ tartalmát (a terepet, az objektumokat, a szörnyek elhelyezését) nem a kódban, hanem az adatbázisban tároljuk, verziózva. Ezt a világot egy játékon belüli világszerkesztővel tudjuk szerkeszteni: a változtatások először vázlatba kerülnek, ellenőrzésen esnek át, és csak ezután publikáljuk őket. Minden publikálás új verziót hoz létre, a kliens pedig egy kivonat (digest) összevetésével ellenőrzi, hogy a nála lévő világadatok megegyeznek-e a szerverével. Eltérés esetén megvárja az adatok frissítését, és csak utána lép tovább a játékba.
Küldetések: Lua mint szkriptnyelv
A küldetéseket és az NPC-párbeszédeket a szerveren, Lua 5.4-ben írjuk. Így a szerver újrafordítása nélkül fejleszthetjük és tesztelhetjük a küldetéseket.
A játékosok üzenetei soha nem hagyják el a szervereinket
A játékon belüli chatfordításhoz nem használunk külső fordítószolgáltatást. Egy helyi, kizárólag CPU-n futó fordítómodellt üzemeltetünk a saját szervereinken; a játékosok üzeneteit semmilyen külső API-nak nem küldjük el. Így a különböző nyelveket beszélő játékosok megérthetik egymást anélkül, hogy az üzeneteik valaha is elhagynák a stúdiót.
Mi következik?
A devlog második része azokról a döntésekről szól, amelyeket azért hoztunk, hogy ugyanaz a világ egérrel, érintőképernyővel és kontrollerrel is játszható legyen. A stúdió megismeréséhez olvasd el a Ki az Astronaut Entertainment? című bejegyzést; magáról a játékról pedig a Játékok oldalon olvashatsz.


