Ludwig-Maximilians-Universität München München, 03.06.2024
Institut für Informatik
Softwareentwicklungspraktikum
SoSe 2024 – Robo Rally
Protokoll Version 1.0
Übersicht
• (Neu) Support für folgende Spielfelder: Extra Crispy, Lost Bearings, Death Trap
• (Neu) Fehlerbehandlung von Verbindungsverlust
• (Neu) Support für Schadenskarten
• (Offen) Kein Support für Upgradekarten
1 Grundlagen
Die Daten werden über TCP als JSON-Objekte übertragen. Als Zeichensatz wird hierfür UTF-8
verwendet, wobei unterschiedliche Nachrichtentypen durch sogenannte Wrapper-Objekte modelliert
sind.
Ein Objekt darf dabei stets nur eine Nachricht enthalten und sieht folgendermaßen aus:
{
"messageType": "Sample",
"messageBody": {
"keyOne": "valueOne",
"keyTwo": "keyTwo"
}
}
Statt eines primitiven "value" können hier jedoch auch komplexere JSON-Objekte auftreten. So
kann ein "messageBody" z.B. mehrere Objekte und / oder Listen enthalten. Auch Kombinationen
aus primitiven Typen und Objekten sind denkbar.
12 Verbindungsaufbau
Verbindung erfolgreich
Nach dem Aufbau der TCP-Verbindung sendet der Server seine Protokollversion an den Client.
{
"messageType": "HelloClient",
"messageBody": {
"protocol": "Version 1.0"
}
}
Verbindungsverlust
Es kann jederzeit vorkommen, dass ein Client die Verbindung verliert.
Um dies regelmäßig zu kontrollieren, sendet der Server folgende Nachricht regelmäßig alle 5
Sekunden. Diese Nachricht wird mit derselben Nachricht vom Client quittiert.
{
"messageType": "Alive",
"messageBody": {}
}
Gruppenidentiffkation
Anschließend antwortet der Client mit Informationen zu seiner benutzten Protokollversion, dem
Gruppennamen und ob es sich um den Login einer KI handelt.
Die Client ID ist hier optional zu senden. Näheres dazu im Abschnitt "Verbindungsverlust".
{
"messageType": "HelloServer",
"messageBody": {
"group": "TolleTrolle",
"isAI": false,
"protocol": "Version 1.0",
"clientID": 42
}
}
Sollte der Server die Protokollversion der Gruppe nicht unterstützen, so ist die Verbindung abzubrechen.
Genaueres hierzu im Teil: Besondere Nachrichten
Übergabe der Client ID
Ist alles in Ordnung, erhält der Client vom Server eine Nummer zugewiesen.
Diese sollte im positiven Bereich liegen und muss natürlich einzigartig sein.
2{
"messageType": "Welcome",
"messageBody": {
"clientID": 42
}
}
3 Lobby
Setzen des Spielernamens und der Figur
Nach dem erfolgreichen Verbindungsaufbau kann ein Spieler seinen Namen und seine Spielffgur
auswählen. Die Spielffgur soll dabei einzigartig sein, der Spielernamen darf mehrfach vergeben
werden.
{
"messageType": "PlayerValues",
"messageBody": {
"name": "Nr. 5",
"figure": 5
}
}
Bestätigung der Spielerauswahl
Eine bereits vergebene Figur soll, wie im Bereich Besondere Nachrichten beschrieben, quittiert
werden. Valide Wünsche werden für alle verbundenen Clients sichtbar bestätigt.
{
"messageType": "PlayerAdded",
"messageBody": {
"clientID": 42,
"name": "Nr. 5",
"figure": 5
}
}
Bedenken Sie, dass neu hinzukommende Clients auch nachträglich über bereits vorhandene
Spieler informiert werden müssen.
Bereitschaft signalisieren
Sobald ein Spieler seine Auswahl erfolgreich getroffen hat, kann er dem Server signalisieren
bereit zu sein. Diese Meinung kann er via Boolean aber auch zurückziehen!
3{
"messageType": "SetStatus",
"messageBody": {
"ready": true
}
}
Bereitschaft bestätigen oder widerrufen
Von Seiten des Servers wird der Spielerstatus an alle verbundenen Clients verteilt.
{
"messageType": "PlayerStatus",
"messageBody": {
"clientID": 42,
"ready": true
}
}
Auch diesen Status müssen neu verbindende Clients empfangen.
Map auswählen
Der erste Spieler, der Bereitschaft signalisiert, darf die Map auswählen, auf der gespielt wird.
Falls der erste Spieler die Bereitschaft widerruft oder das Spiel verlässt, ist der nächste Spieler
an der Reihe. Falls es nur KIs gibt, wählt der Server eine zufällige Map. Damit ein sofortiger
Spielstart bei zwei KIs vermieden wird, soll der Parameter "MinPlayer" beim Serverstart als
Flag übergeben werden können. Um eine Map auszuwählen, wird folgende Nachricht vom Server
verschickt:
{
"messageType": "SelectMap",
"messageBody": {
"availableMaps": ["Dizzy Highway"]
}
}
Daraufhin antwortet der Spieler mit folgender Nachricht (Beispiel):
{
"messageType": "MapSelected",
"messageBody": {
"map": "Dizzy Highway"
}
}
4Diese Nachricht wird vom Server auch an alle Spieler weitergeleitet, um die Auswahl des Startboards
zu erleichtern.
Sind alle Spieler bereit (mind. 2!), erstellt der Server die Karte und teilt diese den anderen mit.
Jedes Feldobjekt beinhaltet die einzelnen Feldtypen und deren Orientierung.
• Die Map wird als JSON-Dateien gespeichert und mit einem Parser weiterverarbeitet.
• Die Map wird als ListX
übertragen und ist wie ein Koordinatensystem
aufgebaut. x0,y0 ist links oben. x5,y0 liegt rechts vom ersten Feld.
• Felder können mehr als einen Typus haben. Daher die dritte verschachtelte Liste, um
Felder wie "eine Wand mit Laser" zu modellieren.
• Leere Felder (z.B. bei nicht rechteckigen Spielfeldern) werden mit null belegt.
• Valide Feldtypen sind: Empty, StartPoint, ConveyorBelt, PushPanel, Gear, Pit, EnergySpace,
Wall, Laser, Antenna, CheckPoint, RestartPoint
• Als Grundlage für die Orientierung dient die Aktion der Felder: Bei einem ConveyorBelt
die Zuffuss- bzw. Abffussrichtung, bei einem Push-Panel die Pushrichtung, usw. Felder,
die keine Effekte haben (z.B. Checkpoints) bekommen kein Attribut "orientations".
• "ConveyorBelt" hat mindestens zwei Orientierungen. Die erste gibt die "Abffussrichtung"
an, in die ein Bot geschoben wird, alle folgenden geben die "Zuffussrichtung" an. Damit
können sowohl normale als auch rotierende Conveyor Belts umgesetzt werden. Es gibt
folgende Orientierungen: "top", "bottom", "right", "left". Das Feld hat einen zusätzlichen
Wert für Speed (1 / 2 - Grün / Blau).
• Die Orientierung von "Gear" ist als Drehrichtung zu interpretieren. Hierbei ist sie z.B.
im Uhrzeigersinn als "clockwise" und gegen den Uhrzeigersinn als "counterclockwise"
anzugeben.
• Die "Antenna" Orientierung gibt die Senderichtung an.
• "EnergySpaces", "Laser" und "CheckPoint" haben einen zusätzlichen Wert "count" für die
vorhandene Energie und die Anzahl der Laser bzw. Nummer des Punktes.
• "PushPanel" inkludiert eine Liste der Register, in denen sie aktiv werden.
• "Wall" kann mehrere Orientierungen haben. Diese geben die Seiten an, die mit einer Wand
geblockt werden.
• Jedes Feld hat einen Wert "isOnBoard", der angibt, auf welchem Feld es liegt (Unterschied
beim Reboot). Die Boardbezeichnungen können Sie dem Regelwerk entnehmen.
5Beispiel für eine Map mit komplexen Feldern
x0;y0: Blauer Belt, dessen Abflussrichtung oben ist und der rechts und unten eine Zuflussrichtung
hat.
x0;y1: Panel, das nach links schiebt, wenn Register zwei oder vier aktiv sind.
x1;y0: Wall, die oben und rechts begrenzt und einen Doppel-Laser nach unten verschießt.
x1;y1: Leeres Feld
Jedes der beschriebenen Felder liegt nicht auf dem Startboard.
{
"messageType": "GameStarted",
"messageBody": {
"gameMap": [
[
[
{
"type": "ConveyorBelt",
"isOnBoard": "1B",
"speed": 2,
"orientations": [
"top",
"right",
"bottom"
]
}
],
[
{
"type": "PushPanel",
"isOnBoard": "1B",
"orientations": [
"left"
],
"registers": [
2, 4
]
}
]
],
[
[
{
"type": "Wall",
"isOnBoard": "4A",
"orientations": [
"top",
"right"
]
},
{
"type": "Laser",
"isOnBoard": "4A",
"orientations": [
"bottom"
],
"count": 2
}
],
[
6null
]
]
]
}
}
4 Chatnachrichten
Privat senden
Spieler sollen untereinander Nachrichten austauschen können. Diese werden vom Server verteilt.
Ein Client kann eine private Nachricht an Client ID 42 wie folgt veranlassen:
{
"messageType": "SendChat",
"messageBody": {
"message": "Yoh, Bob! How is your head doing after last night?",
"to": 42
}
}
Privat verteilen
Der Server steht in der Pflicht, diese Nachricht nur dem richtigen Spieler zu schicken.
{
"messageType": "ReceivedChat",
"messageBody": {
"message": "Yoh, Bob! How is your head doing after last night?",
"from": 42,
"isPrivate": true
}
}
Öffentlich senden
Im Falle einer Nachricht für alle Spieler wird "to" zu -1.
{
"messageType": "SendChat",
"messageBody": {
"message": "Hi all! I will crush your robots in no time.",
"to": -1
}
}
Öffentlich verteilen
Hier kann die Nachricht normal im Chatfenster ausgegeben werden.
7{
"messageType": "ReceivedChat",
"messageBody": {
"message": "Hi all! I will crush your robots in no time.",
"from": 42,
"isPrivate": false
}
}
5 Besondere Nachrichten
Fehlerhafte Nachrichten
Bei einer fehlerhaften Nachricht des Clients soll der Server den Client entsprechend informieren.
Der Client soll den Fehler registrieren und die Fehlernachricht ausgeben. Dies geschieht optional
mit einer detaillierten Beschreibung oder nur mit einem generellen Hinweis. Die Fehlernachricht
dient nur zur Information des Benutzers und soll im Code nicht zur Korrektur genutzt werden.
{
"messageType": "Error",
"messageBody": {
"error": "Whoops. That did not work. Try to adjust something."
}
}
Wenn ein Verbindungsverlust auftritt, soll das den anderen natürlich mitgeteilt werden. Wie Sie
mit dieser Situation dann umgehen, ist Ihnen überlassen. Auch eine erneute Verbindung kann
gestattet werden. Natürlich macht dies nur Sinn, wenn der Roboter vorher nicht entfernt wurde.
{
"messageType": "ConnectionUpdate",
"messageBody": {
"clientID": 9001,
"isConnected": false,
"action": "AIControl"
}
}
Mögliche Optionen sind also
• Remove: Entfernt den Roboter des Clients aus dem Spiel.
• AIControl: Eine KI übernimmt die Kontrolle.
• Ignore: Der Client wird ignoriert, verbleibt aber (regungslos) im Spiel. Sein Roboter kann
weiterhin Interaktionen auslösen, wird aber nicht mehr aktiv bewegt.
8• Reconnect: Der Client ist wieder online und verbunden. Dafür sollen die Nachrichten aus
dem Abschnitt "Verbindungsaufbau" mit einer zusätzlichen Client ID verwendet werden.
Es ist nicht nötig, jede dieser Optionen umzusetzen. Es ist ausreichend, die "Remove" Version
zu implementieren.
6 Spielkarten
Karten spielen
Basiskarten werden ganz rudimentär als String übertragen. Natürlich sollten diese Karten in
Ihrem Modell trotzdem in Objektform hinterlegt sein!
{
"messageType": "PlayCard",
"messageBody": {
"card": "MoveI"
}
}
Der Server gibt dies an die anderen Clients weiter.
{
"messageType": "CardPlayed",
"messageBody": {
"clientID": 2,
"card": "MoveI"
}
}
Programmierkarten
MoveI, MoveII, MoveIII, TurnLeft, TurnRight, UTurn, BackUp, PowerUp und Again.
Schadenskarten
Spam, Worm, Virus und Trojan.
Achtung: Die Beschreibung des Viruskarteneffekts im Regelwerk weicht vom Kartentext ab (s.
Regeln S. 15). Wir gehen folgendermaßen vor: Jeder Roboter innerhalb des 6-Feld-Radius bekommt
eine SPAM Karte.
Besondere Programmierkarten
EnergyRoutine, SandboxRoutine, WeaselRoutine, SpeedRoutine, SpamFolder und RepeatRoutine.
Upgradekarten
sind in dieser Version des Protokolls noch nicht inkludiert.
97 Spielzug abhandeln
Aktiven Spieler setzen
Der Server gibt eine kurze Nachricht aus, um den aktuell aktiven Spieler zu bestimmen. Entgegen
der offiziellen Anleitung beginnt derjenige, der zuerst die Verbindung aufgebaut hat.
{
"messageType": "CurrentPlayer",
"messageBody": {
"clientID": 7
}
}
(Druck-)fehler in der englischen Spieleanleitung (S. 4, Reihenfolge der Roboter):
• Falsch: Zoom Bot (grün), Smash Bot (gelb), Hulk x90 (rot)
• Richtig: Smash Bot (gelb), Zoom Bot (grün), Hulk x90 (rot)
Aktive Spielphase setzen
Die aktuelle Phase wird mittels einer eigenen ID übertragen. Hierbei gilt:
0 => Aufbauphase, 1 => Upgradephase, 2 => Programmierphase, 3 => Aktivierungsphase
{
"messageType": "ActivePhase",
"messageBody": {
"phase": 3
}
}
7.1 Aufbauphase
Startpunkt wählen
Spieler setzen ihren Startpunkt wie folgt:
{
"messageType": "SetStartingPoint",
"messageBody": {
"x": 4,
"y": 2
}
}
Bestätigen des Startpunktes
Wenn die gewünschte Position valide ist, werden alle Spieler darüber benachrichtigt.
10{
"messageType": "StartingPointTaken",
"messageBody": {
"x": 4,
"y": 2,
"clientID": 42
}
}
7.2 Upgradephase
Kommt in einer späteren Version des Protokolls. Diese Phase wird in dieser Version des Protokolls
nicht an den Client übermittelt sondern übersprungen. Nach der Aufbauphase wird direkt
zur Programmierphase übergegangen.
7.3 Programmierphase
Spieler zieht Karten
Es werden die obersten neun Programmierkarten gezogen. Die Nachricht vom Server sollte natürlich
nur von dem jeweils betroffenen Spieler empfangen werden können!
{
"messageType": "YourCards",
"messageBody": {
"cardsInHand": [
"card1",
"..."
]
}
}
Die anderen Spieler werden lediglich über die Anzahl der Karten benachrichtigt.
{
"messageType": "NotYourCards",
"messageBody": {
"clientID": 42,
"cardsInHand": 9
}
}
Programmierkarten mischen
Sind nicht mehr genug Karten auf dem Kartenstapel, muss der Ablegestapel gemischt werden.
11Dazu werden zuerst alle restlichen Karten vergeben, dann wird der Ablegestapel gemischt und
danach die verbleibende Kartenanzahl versandt.
{
"messageType": "ShuffleCoding",
"messageBody": {
"clientID": 42
}
}
Karten auswählen
Die Kartenauswahl wird vom Spieler zum Server übertragen, jede Karte einzeln. Überschreibungen
und ein Leeren des Registers sind möglich, indem "card = Null" gesetzt wird.
{
"messageType": "SelectedCard",
"messageBody": {
"card": "Again",
"register": 5
}
}
Kartenwahl bestätigen
Der Server gibt das belegte Register, natürlich ohne Karteninformation, an alle weiter. "Filled"
steht hierbei für die Information, ob eine Karte in ein Register gelegt oder entfernt wurde.
{
"messageType": "CardSelected",
"messageBody": {
"clientID": 42,
"register": 5,
"filled": true
}
}
Auswahl beendet
Sobald ein Spieler die fünfte Karte gelegt hat, sind keine Änderungen mehr möglich! Dies wird
für alle sichtbar übertragen.
{
"messageType": "SelectionFinished",
"messageBody": {
"clientID": 42
}
}
12Timer gestartet
Als Folge des ersten fertigen Spielers startet der 30 Sekunden Timer.
{
"messageType": "TimerStarted",
"messageBody": {}
}
Timer beendet
Zum Ende des Timers wird eine Meldung an die Clients geschickt. Diese beinhaltet auch die
Information über evtl. zu langsam reagierende Spieler.
{
"messageType": "TimerEnded",
"messageBody": {
"clientIDs": [
1,
3,
6
]
}
}
Hand abwerfen und blind ziehen
Diese Karten werden in der vorgegebenen Reihenfolge auf die noch offenen Register gelegt.
{
"messageType": "CardsYouGotNow",
"messageBody": {
"cards": [
"card1",
"..."
]
}
}
7.4 Aktivierungsphase
Aktive Programmierkarten
Jede der fünf Runden wird durch eine Nachricht mit den nun aktiven Karten eingeläutet. Es wird
jeweils nur die Karte aus dem aktuellen Register angezeigt. Bedenken Sie, dass die Prioritäten
nach jedem Register neu berechnet werden müssen. Beachten Sie außerdem, dass bestimmte
Karten auch während der Aktivierung des Registers ersetzt werden können, wie z.B. Spam Karten.
13{
"messageType": "CurrentCards",
"messageBody": {
"activeCards": [
{
"clientID": 1,
"card": "MoveI"
},
{
"clientID": 2,
"card": "Spam"
}
]
}
}
{
"messageType": "ReplaceCard",
"messageBody": {
"register": 3,
"newCard": "MoveI",
"clientID": 9001
}
}
8 Aktionen, Ereignisse und Effekte
Bewegung
Bewegungen einer Spielfigur werden vom Server übertragen, jedoch ohne den Auslöser zu nennen.
Wichtig: Es handelt sich rein um die Bewegung zwischen Feldern. Ohne Drehung!
{
"messageType": "Movement",
"messageBody": {
"clientID": 42,
"x": 4,
"y": 2
}
}
Drehung
Die Richtung wird hier mit "clockwise" (Uhrzeigersinn), bzw. "counterclockwise" (gegen den
Uhrzeigersinn) angegeben.
14{
"messageType": "PlayerTurning",
"messageBody": {
"clientID": 42,
"rotation": "counterclockwise"
}
}
Schadensmeldungen
Es werden immer alle auf einmal übertragen. Wenn ein Spieler also drei Schaden bekommt, wird
trotzdem nur eine Nachricht verschickt.
{
"messageType": "DrawDamage",
"messageBody": {
"clientID": 42,
"cards": [
"Spam",
"..."
]
}
}
Sollten alle Spamkarten vergeben sein, fragt der Server vorher nach der Auswahl des Spielers
und schickt nach der Wahl des Spielers die Schadensmeldung.
{
"messageType": "PickDamage",
"messageBody": {
"count": 2,
"availablePiles": ["Virus", "Trojan"]
}
}
{
"messageType": "SelectedDamage",
"messageBody": {
"cards": [
"Virus",
"Trojan"
]
}
}
"availablePiles" beinhaltet hierbei die Kartenstapel, auf denen sich noch Schadenskarten befinden.
Achten Sie auf Sonderfälle, wie z.B. folgenden: Sollte ein letzter Stapel nur noch eine Karte
15beinhalten, der Client allerdings zwei Karten ziehen müssen, reicht es aus, diese eine Karte zu
verteilen. Sollte der Fall eintreten, dass alle Schadenskarten aufgebraucht sind, können keine
neuen Karten mehr gezogen werden. Das gilt bis es wieder Schadenskarten im Stapel gibt.
Animationen
Für Animationen (wie BlueConveyorBelt, GreenConveyorBelt, PushPanel, Gear, CheckPoint,
PlayerShooting, WallShooting, EnergySpace) hilfreich, ansonsten überflüssig.
{
"messageType": "Animation",
"messageBody": {
"type": "PlayerShooting"
}
}
Neustart
Der Neustart wird sofort nach dem betreffenden Register durchgeführt. Roboter werden standardmäßig
immer nach Oben / Norden (top) ausgerichtet. Es werden zusätzlich die Koordinaten
des Rebootfeldes per "Movement"-Nachricht verschickt. Der Client, den der Neustart betrifft,
quittiert die Nachricht mit der Richtung, in die der Bot ausgerichtet werden soll (top, right,
bottom, left). Sollte die Nachricht zur Ausrichtung nicht bis zum Ende der aktuellen Runde
angekommen sein, wird die Standardausrichtung verwendet.
Startet ein Roboter auf dem Startfeld neu, während sich ein weiterer auf seinem Neustart-Feld
befindet, schiebt der neustartende Roboter den Roboter, der das Feld besetzt, in Richtung des
Pfeils auf dem Neustart-Feld weg. Sollte der Fall auftreten, dass ein Roboter zwischen Wand
und Startfeld steht, sodass dieser gegen die Wand geschoben würde, wenn ein Roboter auf dem
Startfeld rebooted, dann soll der Roboter auf einem anderen, freien Startfeld rebooten.
{
"messageType": "Reboot",
"messageBody": {
"clientID": 42
}
}
{
"messageType": "RebootDirection",
"messageBody": {
"direction": "right"
}
}
Energie
Anpassungen an Energiewerten werden mit der neuen Summe übertragen.
16Ein zusätzlicher Wert liefert die Info, woher die Veränderung kam (PowerUpCard,
EnergySpace). Sie können Client-seitig entscheiden, wie Sie damit umgehen wollen.
{
"messageType": "Energy",
"messageBody": {
"clientID": 42,
"count": 1,
"source": "EnergySpace"
}
}
Erreichte Checkpoints
Hier entspricht der Key "number" den Kontrollpunkt-Marken der physischen Version. Ein Spieler
mit dem Wert 3 hat also die ersten drei Checkpoints erreicht.
{
"messageType": "CheckPointReached",
"messageBody": {
"clientID": 42,
"number": 3
}
}
Spielsieg
Sobald ein Spieler den letzten Checkpoint erreicht, gilt das Spiel als gewonnen.
{
"messageType": "GameFinished",
"messageBody": {
"clientID": 42
}
}
17
版权所有:编程辅导网 2021 All Rights Reserved 联系方式:QQ:99515681 微信:codinghelp 电子信箱:99515681@qq.com
免责声明:本站部分内容从网络整理而来,只供参考!如有版权问题可联系本站删除。