- Die Serious Engine 1 setzt Singleplayer, Multiplayer und Demo-Wiedergabe auf dieselbe deterministische Simulation auf und zeichnet bzw. überträgt pro Tick nicht den vollständigen Zustand, sondern Spieleraktionen und Game-Stream-Blöcke
- Demo und Multiplayer teilen sich einen anfänglichen Spielzustand und wenden danach Eingabedeltas wie
CPlayerActionan, weshalb die Deterministik von Zufalls-Seed und Spiellogik der Schlüssel zur Synchronisation ist - Die Netzwerkschicht verarbeitet über UDP Sequenznummern, ACKs, Wiederholungen, Bandbreitenbegrenzung und verbindungsspezifische Puffer selbst, um das Spiel auch über langsame Leitungen spielbar zu halten
CNetworkMessagebietet Serialisierung, LZ77-/LZRW1-Komprimierung und XOR-basiertes Delta-Encoding, aber Nachrichten einschließlich Chat können unverschlüsselt sein und nur komprimiert übertragen werden- Das Client-Server-Modell von Serious Sam spart Bandbreite, indem jeder Client seine eigene Simulation beibehält, benötigt dafür aber Hilfsmechanismen wie Synchronisationsprüfung, Vorhersage, erneute Übertragung und CRC-Prüfung
Gemeinsame Simulationsstruktur der Serious Engine
- Der Quellcode der Serious Engine 1 wurde 2016 unter GNU GPL v2 veröffentlicht; die Analyse basiert auf dem Lesen und Debuggen dieser freigegebenen Codebasis
- Serious Sam wurde von Anfang an als Multiplayer-Spiel entworfen, und auch die Singleplayer-Kampagne arbeitet intern wie ein Sonderfall der Multiplayer-Struktur
- Der Engine unterstützte Modi sind:
- Offline-Singleplayer-Kampagne
- Online-, LAN- und lokaler Koop-Modus sowie mehrere Spielmodi
- Splitscreen, bei dem mehrere Spieler an einem Client teilnehmen
- Demo-Aufzeichnung und -Wiedergabe
Demo-Aufzeichnung: Aktionen statt vollständigem Zustand speichern
- Würde eine Demo pro Tick den gesamten Spielzustand speichern, würden die Dateien groß werden. Daher speichert die Serious Engine zu Beginn der Aufzeichnung einmal den vollständigen Spielzustand und danach pro Tick Game-Stream-Blöcke
- Ein Game-Stream-Block enthält folgende Nachrichtentypen:
MSG_SEQ_ALLACTIONS: SpieleraktionenMSG_SEQ_ADDPLAYER: Spieler hinzufügenMSG_SEQ_REMPLAYER: Spieler entfernenMSG_SEQ_PAUSE: Pause oder FortsetzenMSG_SEQ_CHARACTERCHANGE: Änderung von Spielercharakter-Eigenschaften
- Zentral ist
MSG_SEQ_ALLACTIONS; die Engine deserialisiert für jeden aktiven Spieler einCPlayerAction-Objekt und wendet es aufCPlayerTargetan CPlayerActionenthält den Spielerzustand:- Bewegungsgeschwindigkeit im Weltraum
pa_vTranslation - Charakterrotation im Weltraum
pa_aRotation - Blickrotation im Weltraum
pa_aViewRotation - aktuell gedrückte Tasten
pa_ulButtons - TSC-basierter Millisekunden-Zeitstempel
pa_llCreated
- Bewegungsgeschwindigkeit im Weltraum
- Bei der Wiedergabe wird der anfängliche Spielzustand geladen und die Spieleraktionen jedes Ticks wie in einer echten Partie angewendet
Warum Deterministik nötig ist
- Diese Struktur setzt voraus, dass alles im Spiel vollständig vorhersagbar ist und nur Spieleraktionen den Spielverlauf verändern
- Auch Zufallszahlen werden über einen Pseudozufallszahlengenerator verarbeitet, der einen Seed verwendet, der Teil des Spielzustands ist
CEntity::IRnd()verwendetCSessionState::Rnd()ses_ulRandomSeedwird beim Deserialisieren des Spielzustands initialisiert
- Würde man einen echten Zufallszahlengenerator oder unterschiedliche Seeds verwenden, könnte dieselbe Demo bei der Wiedergabe andere Ergebnisse liefern, was zu einer Desynchronisation führen würde
Gleitkomma und Tick-Verarbeitung
- Die PC-Version von Serious Sam erschien ursprünglich nur für Windows, weshalb die Annahme desselben Compilers und derselben Runtime Probleme mit der Gleitkomma-Synchronisation verringerte
- Der Renderer ist eine DLL, und OpenGL- oder DirectX-API-Aufrufe können die FPU-Präzision verändern; daher verwendet die Serious Engine Präzisions-Guards wie
CSetFPUPrecision FPUPrecision(FPT_24BIT) - Es wurde keine Stelle gefunden, die die Rundungssteuerung explizit setzt, aber es gibt ein Assert, das per
_controlfpden Zustand_RC_NEARprüft - Die Spiellogik ist von der Rendering-Framerate getrennt
- Das Rendering hängt von Hardware und Einstellungen ab und scheint intern auf 500 FPS begrenzt zu sein
- Die Spiellogik läuft fest mit 20 Ticks pro Sekunde
- Flüssige Bewegung wird durch lineare Interpolation zwischen aktuellem und vorherigem Tick erzeugt; in der Konsole lässt sich die Interpolation mit
/net_bLerping=0abschalten
Eigene Paketebene über UDP
- In der Serious-Engine-Multiplayer-Implementierung existieren noch Funktionsnamen wie
StartPeerToPeer_t, das tatsächliche Modell ist aber eine Client-Server-Architektur - Der Server empfängt Client-Nachrichten, verarbeitet sie und verteilt relevante Informationen an alle Clients
- Jeder Spieler führt ähnlich wie im Demo-System seine eigene Simulation aus und entwickelt seinen Zustand anhand der Aktionsinformationen anderer Spieler weiter
- Das Netzwerk verwendet UDP und legt ein eigenes Protokoll darüber, um Reihenfolge und Paketverlust zu behandeln
CPacketverwaltet Paketreihenfolge und Zuverlässigkeitpa_ulSequence: Sequenznummer zur Sortierung und Duplikatserkennungpa_ubReliable: Flag für Zuverlässigkeitpa_ubRetryNumber: zählt Wiederholungsversuchepa_tvSendWhen: wird für geplante Sendezeit und Staukontrolle verwendet
- Zuverlässige Pakete warten auf ACKs und werden ohne ACK erneut gesendet
- Die maximale Zahl an Wiederholungen wird über
net_iMaxSendRetriesgesetzt und scheint standardmäßig10zu sein - Das Wiederholungsintervall wird über
net_fSendRetryWaitgesetzt und scheint standardmäßig0.5fzu sein
- Die maximale Zahl an Wiederholungen wird über
- Zuverlässige Pakete können einen Stream bilden, der sich über mehrere Pakete erstreckt
- Das erste Paket ist
UDP_PACKET_RELIABLE_HEAD - Das letzte Paket ist
UDP_PACKET_RELIABLE_TAIL - Ein einzelnes zuverlässiges Paket trägt beide Flags
- Das erste Paket ist
- Unzuverlässige Pakete bilden keinen Stream, da dieser bei Verlust unterbrochen würde
Verbindungslebenszyklus und Paket-Routing
CCommunicationInterfaceist für die Kommunikation auf Paketebene zuständig und besitzt Schnittstellenfunktionen für Server, Client und Broadcast- Server- und Client-Schnittstellen gehen davon aus, dass das Gegenüber bereits bekannt ist; die Broadcast-Schnittstelle dient der Kommunikation mit beliebigen Adressen
CCommunicationInterfaceverwaltet zwei Master-Puffercci_pbMasterInput: speichert eingehende UDP-Pakete nach der Deserialisierung zuCPacketcci_pbMasterOutput: serialisiert ausgehendeCPacket-Objekte und sendet sie über die Socket-API
- Die eigentliche clientbezogene Kommunikationsabstraktion übernimmt
CClientInterface- Der Server hält für jeden Spieler eine Schnittstelle im Array
cm_aciClients - Der Client kommuniziert mit dem Server über
cm_ciLocalClient - Sowohl Client als auch Server verwenden
cm_ciBroadcastzum Verbindungsaufbau
- Der Server hält für jeden Spieler eine Schnittstelle im Array
adr_uwIDinCAddressdient als eindeutige Client-Kennung oder als Markierung für Broadcast-Pakete- Ist der Wert
'//'oder0, handelt es sich um ein Broadcast-Paket - Andere Werte sind Client-IDs innerhalb der Sitzung
- Ist der Wert
Verbindungsaufbau und grundlegende Sicherheitsmechanismen
- Um sich mit einem Server zu verbinden, sendet der Client ein zuverlässiges Broadcast-Paket mit dem Flag
UDP_PACKET_CONNECT_REQUEST - Ist bereits ein Client mit derselben Adresse und demselben Port verbunden, ignoriert der Server die Anfrage
- Bei einem neuen Client sucht der Server eine freie Client-Schnittstelle und führt dann Folgendes aus
- Er erzeugt eine eindeutige Kennung für diesen Client
- Er sendet die Kennung dem Client in einem zuverlässigen Broadcast-Paket
UDP_PACKET_CONNECT_RESPONSE
- Die Kennung verwendet nicht einfach einen festen Index, sondern kombiniert Teile eines Timerwerts mit dem Client-Index
- Wer sich als anderer Spieler ausgeben will, muss
uwIDtreffen, wodurch sich die Angriffsfläche verringert - Sendet ein nicht verbundener Spieler ein Nicht-Broadcast-Paket, kann die Serious Engine eine Warnung auf der Konsole ausgeben
Singleplayer und Demos als Sonderfall lokaler Verbindungen
- Auch Singleplayer und Demo-Wiedergabe besitzen intern Server und Client, laufen aber im selben Prozess
- Da keine Sockets zwischen demselben Prozess nötig sind, verbindet
Client_OpenLocal()die lokale Client-Schnittstelle direkt mit der serverseitigen Schnittstelle - Zwei verbundene
CClientInterfaceverschieben perExchangeBuffersPakete aus dem Ausgabepuffer der einen Seite in den Eingabepuffer der anderen - Lokales Spielen braucht daher weder Master-Ein-/Ausgabepuffer noch echte Netzwerksockets zu durchlaufen
Schicht der Netzwerk-Nachrichten
CNetworkMessageist die Nachrichtenabstraktion oberhalb der Pakete und lässt sich wie ein Stream lesen und schreiben- Nachrichten werden mit
Read,Write,ReadBits,WriteBitssowie den Operatoren<<und>>serialisiert bzw. deserialisiert - Sie können auch Unter-Nachrichten enthalten; nach dem Schreiben der benötigten Daten kann
Shrinkdie Puffergröße an die Datenmenge anpassen - Der
CNetworkMessage-Puffer wird überAllocMemoryangelegt, das intern offenbarmallocaufruft CLinearAllocatorexistiert zwar, es ließ sich jedoch kein Einsatz finden; Nachrichtenpuffer werden häufig allokiert und reallokiert
Komprimierung und Delta-Encoding
- Nachrichten können mit einem angegebenen Kompressor oder dem Standardkompressor des jeweiligen Nachrichtentyps komprimiert werden
MESSAGETYPEverwendet die unteren 6 Bit für den Typ und die restlichen 2 Bit für die Komprimierungsmethode- LZ77
CzlibCompressor - LZRW1
CLZCompressor - unkomprimiert
- LZ77
- Standardmäßig scheint LZRW1 verwendet zu werden; über die Shell-Variable
net_iCompressionlässt sich das ändern CPlayerActionwird nicht direkt gesendet, sondern als XOR-basiertes Delta zwischen aktueller und letzter Aktion übertragen- Der Empfänger XORt das Delta erneut mit der letzten Aktion und rekonstruiert so das ursprüngliche
CPlayerAction - Deltas komprimieren besser, wenn sich Daten nur wenig ändern
- Gedrückte Tasten bleiben oft über mehrere Frames gleich
- Auch Geschwindigkeit und Blickrotation decken nicht ständig den gesamten Gleitkommabereich ab
- Wenn der Server mit
MSG_SEQ_ALLACTIONSdie Aktionen mehrerer Spieler gesammelt sendet, kann dieser Ansatz noch effektiver sein
Nachrichtenverschlüsselung und Chat
- Nachrichten der Serious Engine sind nicht verschlüsselt
- Wird die Komprimierung mit
net_iCompression=0deaktiviert, sind Chat-Nachrichten im Spiel im Payload von UDP-Paketen im Klartext sichtbar - Ist die Komprimierung in realen Situationen aktiviert, muss ein Mitschneider zunächst den LZ-komprimierten Stream erkennen und dekomprimieren, die benötigten Daten befinden sich aber im Paket
- Spiele jener Zeit verzichteten oft auf Verschlüsselung, und Mechanismen wie Authentifizierung und Schlüsselaustausch hätten die Komplexität erhöht
- Auch das Web war damals überwiegend HTTP
Spielsitzungsebene
CNetworkLibraryverwaltet trotz seines Namens die Spielsitzung einschließlich des SpielzustandsCSessionStateCNetworkLibraryerbt von dem zuvor erwähntenCMessageDispatcher- Beim Start eines Servers führt die Engine folgende Schritte aus
- Die CRC-Sammlung wird initialisiert, um später prüfen zu können, ob verbundene Clients dieselben Dateien wie der Server besitzen
- Ein neuer
CSessionStatewird erzeugt, serialisiert und als Standardzustandga_pubDefaultStategespeichert - Die lokale Weltinstanz wird geladen
- Die globale Kommunikationsschnittstelle wird initialisiert
- Der lokale Sitzungszustand wird initialisiert, damit bei Verbindungen von Clients der Standardzustand und ein Zustandsdelta zum aktuellen Serverzustand gesendet werden können
- Die CRC-Sammlung wird abgeschlossen und in
ga_ulCRCgespeichert
- Die CRC-Prüfung dient weniger der Cheat-Abwehr als der frühen Erkennung von Desynchronisationen
- Der Beitrittsablauf eines Clients folgt grob diesem Schema
- Ein leerer lokaler Sitzungszustand und die Kommunikationsschnittstelle werden initialisiert
- Mit
MSG_REQ_CONNECTREMOTESESSIONSTATEwerden Build-Version, Modusname, Server-Passwort, Zahl lokaler Spieler undCSessionSocketParamsgesendet - Mit
MSG_REP_CONNECTREMOTESESSIONSTATEkommen Nachricht, Weltdateiname, Schwierigkeits- und Spielmodus-Flags sowie Sitzungseigenschaften zurück - Der Basis-Spielzustand wird initialisiert
- Mit
MSG_REQ_STATEDELTAwird der Unterschied zum aktuellen Serverzustand angefordert - Nach Empfang von
MSG_REP_STATEDELTAwird der Spielzustands-Stream über ein rückwärts gerichtetes Diff rekonstruiert CSessionState::Read_t()initialisiert den lokalen Sitzungszustand- Danach erfolgt die CRC-Prüfung; bei Abweichung wird die Verbindung getrennt
Hauptschleife und erneute Übertragung von Game-Streams
- Die Hauptschleifen von Client und Server sind weitgehend ähnlich, der Server erledigt jedoch zusätzliche Aufgaben
- Die Schleife aktualisiert die lokale Client-Schnittstelle und die Broadcast-Schnittstelle und verarbeitet eingehende Netzwerk-Nachrichten im lokalen Sitzungszustand
- Der Server übernimmt außerdem den Pufferaustausch zwischen gepaarten Client-Schnittstellen, die Aktualisierung serverseitiger Client-Schnittstellen, die Aktualisierung des GameAgent und die Verarbeitung von Shell-Kommandos für Remote-Management
SessionStateLoop()verarbeitet unzuverlässige und zuverlässige Nachrichten getrennt- unzuverlässig:
MSG_GAMESTREAMBLOCKS,MSG_KEEPALIVE,MSG_INF_PINGS,MSG_CHAT_OUT - zuverlässig:
MSG_INF_DISCONNECTED,MSG_ADMIN_RESPONSE
- unzuverlässig:
MSG_GAMESTREAMBLOCKSist zwar eine unzuverlässige Nachricht, ihr Verlust kann aber die Synchronisation zerstören- Daher prüft die Serious Engine beim Verarbeiten des Game-Streams auf fehlende Sequenzen und fordert diese erneut an
- Ist der nächste erwartete Sequenzblock vorhanden, wird er verarbeitet
- Fehlt der nächste Block und existiert auch kein neuerer, passiert in dieser Schleife nichts
- Fehlt der nächste Block, existiert aber bereits ein neuerer, wird wegen möglichem Verlust ein Timeout gesetzt
- Nach Ablauf des Timeouts werden mit
MSG_REQUESTGAMESTREAMRESENDdie fehlende Blocksequenz und deren Anzahl angefordert
- Der Server sendet die angeforderten Game-Stream-Blöcke erneut
Verarbeitung von Game-Stream-Blöcken
MSG_SEQ_ADDPLAYERwird gesendet, wenn ein Spieler dem Spiel beitritt, und enthält den Spielerindex sowie einenCPlayerCharacter-DeskriptorMSG_SEQ_REMPLAYERwird beim Trennen eines Spielers gesendet und enthält nur den SpielerindexMSG_SEQ_CHARACTERCHANGEübermittelt Änderungen an Spielername, Team und Aussehen- In Serious Sam enthält der Aussehen-Puffer eine
CPlayerSettings-Struktur - Darin stehen Dateiname des Spielermodells, Richtlinie zur automatischen Waffenauswahl, Fadenkreuztyp und verschiedene Flags
- In Serious Sam enthält der Aussehen-Puffer eine
MSG_SEQ_PAUSEübermittelt Pausieren oder Fortsetzen und gibt den Namen des anfordernden Spielers auf der Konsole ausMSG_SEQ_ALLACTIONSenthält die aktuelle Tick-Zeit und die Aktionen aller Spieler- Es wendet
CPlayerActionauf jedesCPlayerTargetan - Danach folgen Timer-, Ereignis-, Bewegungsentitäten- und Physikverarbeitung
- Es wendet
- Die Synchronisationsprüfung erfolgt über
MakeSynchronisationCheck()- Über
ChecksumForSync()von Entitäten, Spielerzielen usw. wird einCSyncCheckerstellt - Der Client sendet
MSG_SYNCCHECKan den Server; stimmt es nicht mit dem Serverzustand überein, wird die Verbindung getrennt
- Über
Vorhersage zur Reduzierung von Eingabeverzögerung
- Vorhersage soll verhindern, dass sich schnelle Spiele durch Internetlatenz träge anfühlen
- Die Vorhersage für den lokalen Spieler verwendet die an den Server gesendeten Aktionen
- Die Vorhersage für entfernte Spieler verwendet die zuletzt vom Server empfangenen Aktionen
- Würde der Client ohne Antwort des Servers einfach den echten Spielzustand fortschreiben, fehlten ihm die Aktionen anderer Spieler, was zu Desynchronisationen führen könnte
- Um vorhergesagten und echten Zustand nicht zu vermischen, verwendet die Serious Engine einen Predictor
- Ein Predictor ist im Wesentlichen eine „Geister“-Kopie, die mit einer normalen Entität verbunden ist
- Ein temporärer Predictor entsteht während der Vorhersage und besitzt keine zugeordnete Entität im echten Spielzustand
- Während der Verarbeitung eines Vorhersage-Ticks werden nur Predictor-Entitäten verarbeitet
- Sobald der Client Spieleraktionen vom Server erhält, zerstört er bestehende Predictor-Objekte und startet einen neuen Vorhersagezyklus
- Beim Rendering wird die ursprüngliche Entität, für die eine Vorhersage läuft, nicht gezeichnet; stattdessen wird der Predictor gezeichnet, damit Bewegung fortlaufend wirkt, ohne den echten Zustand stark zu verändern
- Der lokale Spieler kann nur so viele Schritte vorhersagen, wie servergesendete Aktionen in
plt_abPredictiongespeichert sind - Ist bei der Vorhersage entfernter Spieler
cli_bLerpActionsdeaktiviert, wird die letzte empfangene Aktion wiederholt - Ist
cli_bLerpActionsaktiviert, wird linear zwischen den letzten beiden Aktionen interpoliert, standardmäßig ist dies jedoch ausgeschaltet
Vergleich mit Doom und Quake
- Das Networking von Doom war praktisch Peer-to-Peer, aber die Clients tauschten Strukturen ähnlich zu
CPlayerActionaus und führten jeweils eine eigene Simulation aus - Auch Doom verwendete für Demo-Aufzeichnung und -Wiedergabe ein ähnliches System
- Quake verfolgte einen anderen Ansatz; der Client verarbeitet dort nicht große Teile der Spiellogik selbst, sondern erhält eher Zustandsupdates vom Server
- Beim Quake-Modell muss man sich weniger um Synchronisationsprobleme kümmern, und Cheat-Abwehr ist leichter, etwa weil der Server keine Informationen über Entitäten hinter Wänden senden muss
- In Serious Sam sind Sitzungen mit deutlich mehr aktiven Gegnern und Objekten als in Quake üblich, weshalb das Senden vieler Objektzustände pro Tick eine größere Bandbreitenlast erzeugt hätte
Portabilität und strukturelle Grenzen
- Einige Netzwerk-Nachrichten serialisieren Strukturen auf eine Weise, die einem
reinterpret castnahekommt - Unter der Annahme eines einzelnen Compilers und einer einzelnen Plattform kann das funktionieren, bei plattformübergreifenden Spielen können sich Struktur-Layout und Padding jedoch unterscheiden
- 32-Bit-Binärdateien versuchen möglicherweise eine Ausrichtung auf 4-Byte-Grenzen, 64-Bit-Binärdateien auf 8-Byte-Grenzen
- Auch Endianness ist ein Problem
- x86-PCs sind Little Endian
- PS3 ist Big Endian
- Die Struktur der Serious Engine ist elegant, weil sie Unterschiede zwischen Übertragungsmedien wie Netzwerk und Dateien von der Spiellogik abstrahiert; zugleich erlaubt das Modell, bei dem alle Clients eine Kopie des Spielzustands besitzen, Cheats
- So könnte etwa ein modifizierter Client in einem Deathmatch die Umrisse anderer Spieler hinter Wänden anzeigen
1 Kommentare
Hacker-News-Kommentare
Ich war einer der Entwickler, die die Netzwerk-Codebasis von Serious Sam umgesetzt haben
Ich habe oft unter einem Schreibtisch im Croteam-Büro geschlafen und Usenet durchforstet, besonders inspiriert von einem Beitrag, der das Vorhersagesystem von QuakeWorld erklärte
In jener Nacht habe ich eine einfache Minimalversion programmiert, während mein Kollege Dan auf einer alten 486er-Unix-Maschine als Router testete, mit der sich Latenz simulieren ließ
Das war lange bevor das eigentliche Spiel darauf aufgebaut wurde
Ich höre das Geräusch immer noch
Ich mochte Koop-Spiele und Shooter sehr, aber meine Freunde wollten immer nur Counter-Strike spielen
Dank Serious Sam konnte ich sie manchmal dazu überreden, auch mal ein Spiel zu spielen, das ich mochte
Mein Onkel war ein wirklich wichtiger Mensch in meinem Leben, und die Spiele zu spielen, die er mochte, ist eine schöne Möglichkeit, die Erinnerung an ihn lebendig zu halten
Ein großartiges Spiel, großartiger Multiplayer und sehr schöne Erinnerungen
Serious Sam war schon immer ein starkes LAN-Party-Spiel
Nicht weil es damals der schickste Titel war und auch nicht, weil es jemand im Voraus geplant hätte
Wenn andere Spiele an Treiberproblemen, Hitzeproblemen oder Updates scheiterten, startete man Serious Sam und es funktionierte einfach, deshalb dominierte es LAN-Partys
Das setzte sich auch in den Nachfolgern fort: Selbst wenn der PC von jemandem komplett zerschossen war, unterstützte es zuverlässig Splitscreen und kam gut mit Eingabegeräten zurecht
Die systemischen Aspekte des Spiels waren in Sachen Zuverlässigkeit wirklich herausragend
Ähnlich war es bei Counter-Strike: grafisch nicht besonders beeindruckend, aber es lief selbst auf Toaster-PCs gut und blieb deshalb lange populär
Es war der einzige Ego-Shooter, der auf den Büro-PCs konstant gut lief, und er hat wirklich Spaß gemacht
Damals gab es bei EA eine ziemlich große Überschneidung zwischen QA und technischem Support: Im Sommer arbeiteten Support-Mitarbeiter als interne Beta-Tester für die Veröffentlichungen zum Jahresende, und im Winter rund um Weihnachten, wenn die Anrufe zunahmen, machten sie technischen Support
Als wir Multiplayer für die Game-Boy-Color-Portierung von Vigilante 8 umgesetzt haben, haben wir deterministisches Gameplay verwendet
Das GBC-Link-Kabel übertrug gleichzeitig ein Byte in beide Richtungen und verhielt sich wie ein Paar Schieberegister, die sich über das Kabel hinweg gegenseitig füllten
Das Spiel war an die Bildrate des GBC gebunden, und praktisch bei jedem V-Blank fiel viel Arbeit für die Bildschirmaktualisierung an; wenn man das verpasste, war es vorbei mit weichem Scrolling
Zu Beginn einer Multiplayer-Partie wurden Seeds ausgetauscht, und der Ablauf war im Wesentlichen so: In Frame A wurden Eingaben gelesen, zu einem Byte komprimiert und in den Sendepuffer gelegt. Während Frame B gerendert wurde, fand die Übertragung statt. Zu Beginn von Frame C hatte man dann die lokalen Eingaben, die in Frame A gesendet worden waren, sowie die gegnerischen Eingaben, die in Frame B empfangen wurden
Diese Eingaben wurden auf den Spielzustand angewendet und dann Frame C gerendert, wodurch sowohl lokale als auch entfernte Eingaben mit einem Frame Verzögerung angewendet wurden
Lokales Spielen hatte keine Eingabeverzögerung, also dürft ihr, wenn ihr im Multiplayer verloren habt, gern die Latenz verantwortlich machen oder, falls nötig, auch ausdrücklich mich
Wirklich ein tolles Spiel, und die technische Erklärung der Multiplayer-Umsetzung ist auch sehr cool
Croteam ist wirklich ein talentiertes Spieleentwicklerteam
Ich habe The Talos Principle 1 und 2 sehr genossen, und beim ersten Teil gehörten sie zu den frühen Pionieren mit einer vollständig eigenen Vulkan-Game-Engine
Ich frage mich, ob das dieselbe Idee ist wie bei „1500 Archers on a 28.8K“ aus Age of Empires
https://www.gamedeveloper.com/programming/1500-archers-on-a-...
Sehr viele Spiele haben solche Systeme lange Zeit verwendet, aber heute scheinen sie aus verschiedenen Gründen seltener zu sein als früher
Entweder man hat Ressourcen oder eben nicht; man braucht keine Beschränkung nur der Beschränkung wegen
Selbst Spiele mit zehnmal so viel Bandbreite tun sich schwer damit, so viele Gegner zu unterstützen
Mir wird gerade klar, dass der Zuwachs an technischen Ressourcen der Effizienz und Kreativität in der Informatik eher geschadet zu haben scheint
Je mehr Bandbreite, Speicherplatz, RAM und Rechenleistung wir bekommen, desto langsamer, aufgeblähter und unfähiger wird Software pro Ressourceneinheit
Man könnte das den Benjamin-Button-Effekt im Softwaredesign nennen
Falls im Artikel eine Zahl steht, habe ich sie nicht gefunden
Es gibt auch bei modernen Spielen viele Fälle, in denen Multiplayer plus Gegnerzahl völlig ausreicht, um als „massiv“ zu gelten, und wenn es um Spielerzahlen geht, gibt es ebenfalls Spiele mit sehr großen Spielerzahlen
Für mich war das ein Spiel, in dem ich mehr Zeit rückwärts laufend verbracht habe als vorwärts
Es ist auf Steam, und ich weiß nicht, ob es vom selben Studio ist, aber es gehört zum Serious-Sam-Universum
Die Architektur von Factorio ist ähnlich: Es werden fast nur Eingabeereignisse übertragen, und sie stützt sich auf einen Lockstep-Simulationskern
Auffällige Ausnahmen gibt es etwa beim Schienenplanungswerkzeug
Das wirkt wie eine befriedigende und gut testbare Design-Einschränkung
Ich erinnere mich noch daran, als Kind die Serious-Sam-Demo aus dem PC Gamer gespielt zu haben
Schon damals galt es als Retro-Spiel, das sich wie eine Rückkehr zu den alten DOOM- und Quake-Tagen anfühlte
Jetzt sind buchstäblich 20 Jahre vergangen, und es ist selbst ein Klassiker geworden
Starsiege: Tribes war selbst über eine 56K-Verbindung groß angelegt und absurd spaßig
https://www.gamedevs.org/uploads/tribes-networking-model.pdf
Tatsächlich habe ich Tribes 2 erst vor Kurzem wieder heruntergeladen und noch vor ein paar Monaten gegen Bots gespielt
Es ist ein altes Spiel, aber immer noch unterhaltsam, und ich denke oft darüber nach, es mit etwas wie Unity neu zu bauen
Vielleicht mache ich das irgendwann
1999 sah man prozedural erzeugtes Terrain, auf dem man wie beim Skifahren hinunterglitt und andere wieder hinaufgleiten sah, dazu große Karten und viele Spieler