Összes bejegyzés

MMORPG építése Unreal Engine 5.8-cal – Devlog #1: az autoritatív szerver és a világ architektúrája

Az Ancient Online architektúrája: Unreal Engine 5.8-as kliens, C++-ban írt autoritatív szerver, 25 Hz-es világciklus és egy élőben szerkesztett világ.

Egymás mellé állított monitorokból és laptopokból álló munkaállomás egy félhomályos stúdióban

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.

Megosztás:
Más nyelveken is29