Αυτό που ξεχωρίζει ένα MMORPG από ένα παιχνίδι για έναν παίκτη είναι ο διακομιστής που βρίσκεται πίσω από καθετί που βλέπετε. Σε αυτό το πρώτο μέρος του ημερολογίου ανάπτυξης (devlog) του Ancient Online: Remastered, εξηγούμε πώς χτίζουμε ένα MMORPG με Unreal Engine 5.8: πώς μοιράζεται η δουλειά ανάμεσα σε client και διακομιστή, πώς προσομοιώνεται ο κόσμος και πώς καταφέρνουμε να επεξεργαζόμαστε ζωντανά έναν ενιαίο κόσμο.
Δύο κομμάτια: client και διακομιστής
Το Ancient Online αποτελείται από δύο ξεχωριστά κομμάτια λογισμικού:
- Ο client είναι γραμμένος σε C++ πάνω στο Unreal Engine 5.8. Η απόδοση γραφικών, ο ήχος, το περιβάλλον χρήστη και η είσοδος είναι δική του δουλειά.
- Ο διακομιστής είναι ανεξάρτητος από το Unreal: τον γράψαμε από το μηδέν σε C++23. Όλοι οι κανόνες του παιχνιδιού τρέχουν εδώ.
Γιατί γράψαμε τον διακομιστή χωριστά και όχι μέσα στη μηχανή; Επειδή οι ανάγκες ενός διακομιστή MMORPG διαφέρουν από τις ανάγκες μιας μηχανής παιχνιδιών: δεν υπάρχουν γραφικά, υπάρχουν όμως χιλιάδες οντότητες, μόνιμα δεδομένα, μεγάλοι χρόνοι αδιάκοπης λειτουργίας και ανάγκη για προβλέψιμες επιδόσεις. Ένας ξεχωριστός διακομιστής μάς επιτρέπει να κάνουμε μόνο τη δουλειά που χρειάζεται και να ελέγχουμε κάθε byte.
Ο κυρίαρχος διακομιστής: δεν εμπιστευόμαστε τον client
Ο βασικός μας κανόνας είναι ο εξής: ο client ισχυρίζεται, ο διακομιστής αποφασίζει.
- Κίνηση: Η είσοδος του παίκτη πηγαίνει στον διακομιστή και ο διακομιστής υπολογίζει τη θέση. Ο client εμφανίζει τη θέση που αναφέρει ο διακομιστής, με ομαλή παρεμβολή. Έτσι, ένας client που «τηλεμεταφέρεται μόνος του» ή περνά μέσα από τοίχο δεν πηγαίνει πουθενά, όσον αφορά τον διακομιστή.
- Μάχη: Αποτελέσματα όπως η ζημιά, τα χτυπήματα και η απώθηση υπολογίζονται στον διακομιστή. Για παράδειγμα, ο διακομιστής αποφασίζει αν ένα χτύπημα combo ρίχνει κάτω τον αντίπαλο και μεταδίδει το αποτέλεσμα σε όλους την ίδια στιγμή.
- Οικονομία: Τα αντικείμενα, το νόμισμα και οι συναλλαγές του καταστήματος αλλάζουν μόνο στον διακομιστή και στη βάση δεδομένων.
Το κόστος αυτής της προσέγγισης είναι ότι πρέπει να διαχειριζόμαστε καλά την καθυστέρηση του δικτύου· το όφελος είναι ένας κόσμος όπου το κλέψιμο είναι πολύ δυσκολότερο και ισχύουν οι ίδιοι κανόνες για όλους.
Βρόχος κόσμου στα 25 Hz
Ο διακομιστής ενημερώνει τον κόσμο 25 φορές το δευτερόλεπτο. Σε κάθε βήμα προχωρούν η τεχνητή νοημοσύνη των τεράτων, η κίνηση, η μάχη, τα εφέ των δεξιοτήτων και ο καιρός. Το σταθερό βήμα κρατά την προσομοίωση προβλέψιμη και εξασφαλίζει ότι η ίδια είσοδος δίνει πάντα το ίδιο αποτέλεσμα.
Δεν χρειάζεται κάθε παίκτης να γνωρίζει τα πάντα. Ο διακομιστής χωρίζει τον κόσμο σε πλέγμα και στέλνει σε κάθε παίκτη ενημερώσεις μόνο για τις οντότητες που βρίσκονται μέσα στην περιοχή ενδιαφέροντός του (όσες είναι κοντά). Έτσι η κίνηση του δικτύου μένει σε λογικά επίπεδα ακόμη και σε μια γεμάτη πόλη.
Ένα ελαφρύ δυαδικό πρωτόκολλο
Ανάμεσα σε client και διακομιστή δεν υπάρχει JSON ούτε REST· κάθε μήνυμα είναι ένα δυαδικό πακέτο με σταθερή διάταξη. Αυτό εξοικονομεί τόσο εύρος ζώνης όσο και χρόνο επεξεργαστή. Όταν προσθέτουμε ένα νέο σύστημα (όπως ο καιρός, η κίνηση ή η υπηρεσία λογαριασμών μέσα στο παιχνίδι), γράφουμε γι' αυτό ένα ξεχωριστό συμβόλαιο πακέτων με αριθμό έκδοσης, και χρησιμοποιούμε κοινά αρχεία κεφαλίδων για να εγγυηθούμε ότι client και διακομιστής δουλεύουν με το ίδιο συμβόλαιο.
Μονιμότητα: αποθήκευση χωρίς να περιμένει το παιχνίδι
Τα δεδομένα των παικτών ζουν σε μια βάση δεδομένων PostgreSQL. Όμως το να περιμένει ο βρόχος του κόσμου τη βάση δεδομένων σε κάθε βήμα δεν είναι αποδεκτό. Γι' αυτό η αποθήκευση γίνεται στο παρασκήνιο, ως αποθήκευση write-behind: ο διακομιστής κόσμου ρίχνει τις αλλαγές σε μια ουρά (Redis) και ένας εγγραφέας στο παρασκήνιο τις καταχωρίζει στη βάση δεδομένων κατά παρτίδες. Η ροή του παιχνιδιού δεν εξαρτάται από την ταχύτητα της βάσης δεδομένων.
Κανάλια και ο διακομιστής σύνδεσης
Οι παίκτες συνδέονται πρώτα σε έναν διακομιστή σύνδεσης, ο οποίος αναλαμβάνει την επαλήθευση του λογαριασμού, την επιλογή χαρακτήρα και τη συνεδρία. Ύστερα περνούν σε ένα από τα τέσσερα κανάλια του παιχνιδιού (CH1–CH4). Η ζωντανή κατάσταση των καναλιών εμφανίζεται στη σελίδα Κατάσταση διακομιστών του ιστότοπου του παιχνιδιού.
Ένας ενιαίος κόσμος, με ζωντανή επεξεργασία
Ο κόσμος του Ancient Online έχει σχεδιαστεί ως ένας ενιαίος κόσμος και όχι ως ξεχωριστοί χάρτες που τους χωρίζουν οθόνες φόρτωσης· χωρίζεται σε κελιά και φορτώνεται με streaming.
Το περιεχόμενο του κόσμου (έδαφος, αντικείμενα, τοποθετήσεις τεράτων) δεν φυλάσσεται στον κώδικα αλλά στη βάση δεδομένων, με εκδόσεις. Τον κόσμο αυτόν μπορούμε να τον επεξεργαζόμαστε με έναν επεξεργαστή κόσμου μέσα στο παιχνίδι: οι αλλαγές γίνονται πρώτα σε ένα πρόχειρο, ελέγχονται και ύστερα δημοσιεύονται. Κάθε δημοσίευση δημιουργεί μια νέα έκδοση, και ο client επαληθεύει, συγκρίνοντας μια σύνοψη (digest), ότι τα δεδομένα κόσμου που έχει ταιριάζουν με του διακομιστή. Αν δεν ταιριάζουν, περιμένει να ενημερωθούν τα δεδομένα πριν συνεχίσει προς το παιχνίδι.
Αποστολές: η Lua ως γλώσσα σεναρίων
Οι αποστολές και οι διάλογοι των NPC γράφονται σε Lua 5.4 στον διακομιστή. Έτσι μπορούμε να αναπτύσσουμε και να δοκιμάζουμε αποστολές χωρίς να μεταγλωττίζουμε ξανά τον διακομιστή.
Τα μηνύματα των παικτών δεν φεύγουν ποτέ από τους διακομιστές μας
Για τη μετάφραση της συνομιλίας μέσα στο παιχνίδι δεν χρησιμοποιούμε εξωτερική υπηρεσία μετάφρασης. Τρέχουμε στους δικούς μας διακομιστές ένα τοπικό μοντέλο μετάφρασης που δουλεύει μόνο με CPU· τα μηνύματα των παικτών δεν στέλνονται σε κανένα εξωτερικό API. Έτσι, παίκτες που μιλούν διαφορετικές γλώσσες μπορούν να συνεννοούνται χωρίς τα μηνύματά τους να φεύγουν ποτέ από το στούντιο.
Τι ακολουθεί
Το δεύτερο μέρος του ημερολογίου ανάπτυξης καλύπτει τις αποφάσεις που πήραμε για να παίζεται ο ίδιος κόσμος με ποντίκι, οθόνη αφής και χειριστήριο. Για να γνωρίσετε το στούντιο, διαβάστε το «Ποια είναι η Astronaut Entertainment;»· για το ίδιο το παιχνίδι, δείτε τη σελίδα Παιχνίδια.


