Wat een MMORPG onderscheidt van een singleplayergame, is de server achter alles wat u ziet. In dit eerste deel van de devlog van Ancient Online: Remastered leggen we uit hoe we een MMORPG bouwen met Unreal Engine 5.8: hoe het werk verdeeld is tussen client en server, hoe de wereld gesimuleerd wordt en hoe we één doorlopende wereld live kunnen bewerken.
Twee delen: client en server
Ancient Online bestaat uit twee aparte stukken software:
- De client is geschreven in C++ op Unreal Engine 5.8. Rendering, audio, interface en invoer zijn zijn taak.
- De server staat los van Unreal: we hebben hem van nul geschreven in C++23. Alle spelregels draaien hier.
Waarom hebben we de server apart geschreven in plaats van binnen de engine? Omdat een MMORPG-server iets anders nodig heeft dan een game-engine: er zijn geen beelden, maar wel duizenden entiteiten, persistente data, lange uptimes en een nood aan voorspelbare prestaties. Met een aparte server doen we alleen het werk dat nodig is en controleren we elke byte.
De autoritatieve server: de client niet vertrouwen
Onze basisregel is deze: de client beweert, de server beslist.
- Beweging: De invoer van de speler gaat naar de server, en de server berekent de positie. De client toont de positie die de server meldt, met vloeiende interpolatie. Een client die “zichzelf teleporteert” of door een muur loopt, komt voor de server dus nergens.
- Gevechten: Uitkomsten zoals schade, treffers en terugslag worden op de server berekend. Zo beslist de server of een combotreffer de tegenstander tegen de grond slaat, en stuurt hij het resultaat naar iedereen tegelijk.
- Economie: Items, munt en shoptransacties veranderen alleen op de server en in de database.
De prijs van deze aanpak is dat we netwerkvertraging goed moeten beheren; de winst is een wereld waarin valsspelen veel moeilijker is en voor iedereen dezelfde regels gelden.
Een wereldlus van 25 Hz
De server werkt de wereld 25 keer per seconde bij. Bij elke stap gaan de AI van monsters, beweging, gevechten, vaardigheidseffecten en het weer allemaal vooruit. Een vaste stap houdt de simulatie voorspelbaar en zorgt ervoor dat dezelfde invoer altijd hetzelfde resultaat geeft.
Niet elke speler hoeft van alles op de hoogte te zijn. De server verdeelt de wereld in een raster en stuurt elke speler alleen updates over de entiteiten binnen zijn interessegebied (die in de buurt). Zo blijft het netwerkverkeer redelijk, zelfs in een drukke stad.
Een licht binair protocol
Tussen client en server is er geen JSON of REST; elk bericht is een binair pakket met een vaste indeling. Dat bespaart zowel bandbreedte als CPU-tijd. Voegen we een nieuw systeem toe (zoals het weer, beweging of de accountdienst in het spel), dan schrijven we er een apart pakketcontract met versienummer voor, en met gedeelde headerbestanden garanderen we dat client en server met hetzelfde contract werken.
Persistentie: opslaan zonder het spel te laten wachten
Spelersdata zit in een PostgreSQL-database. Maar de wereldlus bij elke stap op de database laten wachten is niet aanvaardbaar. Daarom gebeurt opslaan op de achtergrond, als write-behind-persistentie: de wereldserver zet wijzigingen in een wachtrij (Redis), en een schrijver op de achtergrond schrijft ze in batches weg naar de database. De flow van het spel hangt niet af van de snelheid van de database.
Kanalen en de loginserver
Spelers verbinden eerst met een loginserver, die de accountverificatie, de personagekeuze en de sessie afhandelt. Daarna gaan ze door naar een van de vier spelkanalen (CH1–CH4). De live status van de kanalen staat op de pagina Serverstatus van de spelsite.
Eén doorlopende wereld, live bewerkt
De wereld van Ancient Online is ontworpen als één doorlopende wereld in plaats van aparte kaarten met laadschermen ertussen; ze is opgedeeld in cellen en wordt via streaming geladen.
De inhoud van de wereld (terrein, objecten, plaatsing van monsters) wordt niet in de code bijgehouden maar in de database, met versies. We kunnen deze wereld bewerken met een wereldeditor in het spel: wijzigingen worden eerst in een concept gemaakt, gecontroleerd en dan gepubliceerd. Elke publicatie maakt een nieuwe versie aan, en de client gaat na of de werelddata die hij heeft overeenkomen met die van de server door een digest te vergelijken. Is er een verschil, dan wacht hij tot de data bijgewerkt zijn voor hij verdergaat naar het spel.
Quests: Lua als scripttaal
Quests en NPC-dialogen worden op de server in Lua 5.4 geschreven. Zo kunnen we quests ontwikkelen en testen zonder de server opnieuw te compileren.
Berichten van spelers verlaten onze servers nooit
Voor de vertaling van de chat in het spel gebruiken we geen externe vertaaldienst. We draaien op onze eigen servers een lokaal vertaalmodel dat alleen de CPU gebruikt; berichten van spelers worden naar geen enkele externe API gestuurd. Zo kunnen spelers die verschillende talen spreken elkaar begrijpen zonder dat hun berichten ooit de studio verlaten.
Wat volgt
Deel twee van de devlog behandelt de beslissingen die we namen om dezelfde wereld speelbaar te maken met een muis, een touchscreen en een controller. Wilt u de studio leren kennen, lees dan Wie is Astronaut Entertainment?; voor het spel zelf is er de pagina Games.


