From e756a77c700ee1b9107d251171491a94483fbbde Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Thu, 4 Nov 2021 13:23:13 +0100 Subject: [PATCH] MultiServer: implement Tracker tag Docs: add InvalidPacket Docs: add known Tags Docs: add DeathLink LttPClient: potentially fix DeathLink chaining --- CommonClient.py | 2 +- FactorioClient.py | 1 + LttPClient.py | 1 + MultiServer.py | 9 +++++++-- docs/api.md | 5 +++-- docs/network protocol.md | 28 +++++++++++++++++++++++++++- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/CommonClient.py b/CommonClient.py index eac8894f..fa4e0881 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -262,7 +262,7 @@ class CommonContext(): def on_deathlink(self, data: dict): """Gets dispatched when a new DeathLink is triggered by another linked player.""" - raise NotImplementedError + self.last_death_link = max(data["time"], self.last_death_link) async def send_death(self): self.last_death_link = time.time() diff --git a/FactorioClient.py b/FactorioClient.py index 916ff327..fefb4cb2 100644 --- a/FactorioClient.py +++ b/FactorioClient.py @@ -104,6 +104,7 @@ class FactorioContext(CommonContext): def on_deathlink(self, data: dict): if self.rcon_client: self.rcon_client.send_command(f"/ap-deathlink {data['source']}") + super(FactorioContext, self).on_deathlink(data) def on_package(self, cmd: str, args: dict): if cmd in {"Connected", "RoomUpdate"}: diff --git a/LttPClient.py b/LttPClient.py index 1bd2fa08..b09b57be 100644 --- a/LttPClient.py +++ b/LttPClient.py @@ -149,6 +149,7 @@ class Context(CommonContext): asyncio.create_task(snes_flush_writes(self)) self.death_state = True snes_logger.info(f"Received DeathLink from {data['source']}") + super(Context, self).on_deathlink(data) def color_item(item_id: int, green: bool = False) -> str: diff --git a/MultiServer.py b/MultiServer.py index 5588fa52..748acc03 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -520,9 +520,10 @@ async def on_client_disconnected(ctx: Context, client: Client): async def on_client_joined(ctx: Context, client: Client): update_client_status(ctx, client, ClientStatus.CLIENT_CONNECTED) version_str = '.'.join(str(x) for x in client.version) + verb = "tracking" if "Tracker" in client.tags else "playing" ctx.notify_all( f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) " - f"playing {ctx.games[client.slot]} has joined. " + f"{verb} {ctx.games[client.slot]} has joined. " f"Client({version_str}), {client.tags}).") ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc) @@ -1290,7 +1291,11 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): "items": items}]) elif cmd == 'LocationChecks': - register_location_checks(ctx, client.team, client.slot, args["locations"]) + if "Tracker" in client.tags: + await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "cmd", + "text": "Trackers can't register new Location Checks"}]) + else: + register_location_checks(ctx, client.team, client.slot, args["locations"]) elif cmd == 'LocationScouts': locs = [] diff --git a/docs/api.md b/docs/api.md index 924313e4..f49cad33 100644 --- a/docs/api.md +++ b/docs/api.md @@ -34,6 +34,7 @@ They are assigned by writing a string without any assignment right below a definition. The string must be a triple-quoted string. Example: ```python +from worlds.AutoWorld import World class MyGameWorld(World): """This is the description of My Game that will be displayed on the AP website.""" @@ -198,7 +199,7 @@ or your `Items.py`. For a more elaborate example see `worlds/oot/Items.py`. The same we have done for items above, we will do for locations ```python -from BasClasses import Location +from BaseClasses import Location class MyGameLocation(Location): game: str = "My Game" @@ -257,7 +258,7 @@ default = 0 ```python # Options.py -from Options import Toggle, Range, Choice +from Options import Toggle, Range, Choice, Option import typing class Difficulty(Choice): diff --git a/docs/network protocol.md b/docs/network protocol.md index b0f8aee9..b29d8a93 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -176,7 +176,13 @@ Sent to clients after a client requested this message be sent to them, more info | ---- | ---- | ----- | | data | dict | The data in the Bounce package copied | +### InvalidPacket +Sent to clients if the server caught a problem with a packet. This only occurs for errors that are explicitly checked for. +| Name | Type | Notes | +| ---- | ---- | ----- | +| type | string | "cmd" if the Packet isn't available/allowed, "arguments" if the problem is with the package data. | +| text | string | Error text explaining the caught error. | ## (Client -> Server) These packets are sent purely from client to server. They are not accepted by clients. @@ -200,7 +206,7 @@ Sent by the client to initiate a connection to an Archipelago game session. | name | str | The player name for this client. | | uuid | str | Unique identifier for player client. | | version | NetworkVersion | An object representing the Archipelago version this client supports. | -| tags | list\[str\] | Denotes special features or capabilities that the sender is capable of. | +| tags | list\[str\] | Denotes special features or capabilities that the sender is capable of. [Tags](#Tags) | #### Authentication Many, if not all, other packets require a successfully authenticated client. This is described in more detail in [Archipelago Connection Handshake](#Archipelago-Connection-Handshake). @@ -409,8 +415,28 @@ Note: #### GameData GameData is a **dict** but contains these keys and values. It's broken out into another "type" for ease of documentation. + | Name | Type | Notes | | ---- | ---- | ----- | | item_name_to_id | dict[str, int] | Mapping of all item names to their respective ID. | | location_name_to_id | dict[str, int] | Mapping of all location names to their respective ID. | | version | int | Version number of this game's data | + +### Tags +Tags are represented as a list of strings, the common Client tags follow: + +| Name | Notes | +| ----- | ---- | +| AP | Signifies that this client is a reference client, its usefulness is mostly in debugging to compare client behaviours more easily. | +| IgnoreGame | Tells the server to ignore the "game" attribute in the [Connect](#Connect) packet. | +| DeathLink | Client participates in the DeathLink mechanic, therefore will send and receive DeathLink bounce packets | +| Tracker | Tells the server that this client is actually a Tracker and will refuse new locations from this client. | + +### DeathLink +A special kind of Bounce packet that can be supported by any AP game. It targets the tag "DeathLink" and carries the following data: + +| Name | Type | Notes | +| ---- | ---- | ---- | +| time | float | Unix Time Stamp of time of death. | +| cause | str | Optional. Text to explain the cause of death, ex. "Berserker was run over by a train." | +| source | str | Name of the player who first died. Can be a slot name, but can also be a name from within a multiplayer game. |