From ff175008a17071c88721b08d717048fda4294ad1 Mon Sep 17 00:00:00 2001 From: Jarno Date: Sun, 5 Feb 2023 22:06:38 +0100 Subject: [PATCH] Core: Phase out Print packets (#1364) --- MultiServer.py | 75 +++++++++++++++------------------------- docs/network protocol.md | 15 ++------ 2 files changed, 30 insertions(+), 60 deletions(-) diff --git a/MultiServer.py b/MultiServer.py index 98d0905b..e7151919 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -41,7 +41,6 @@ from NetUtils import Endpoint, ClientStatus, NetworkItem, decode, encode, Networ SlotType min_client_version = Version(0, 1, 6) -print_command_compatability_threshold = Version(0, 3, 5) # Remove backwards compatibility around 0.3.7 colorama.init() @@ -309,6 +308,10 @@ class Context: endpoints = (endpoint for endpoint in self.endpoints if endpoint.auth) async_start(self.broadcast_send_encoded_msgs(endpoints, msgs)) + def broadcast_text_all(self, text: str, additional_arguments: dict = {}): + logging.info("Notice (all): %s" % text) + self.broadcast_all([{**{"cmd": "PrintJSON", "data": [{ "text": text }]}, **additional_arguments}]) + def broadcast_team(self, team: int, msgs: typing.List[dict]): msgs = self.dumper(msgs) endpoints = (endpoint for endpoint in itertools.chain.from_iterable(self.clients[team].values())) @@ -325,29 +328,18 @@ class Context: self.clients[endpoint.team][endpoint.slot].remove(endpoint) await on_client_disconnected(self, endpoint) - # text - - def notify_all(self, text: str): - logging.info("Notice (all): %s" % text) - broadcast_text_all(self, text) def notify_client(self, client: Client, text: str): if not client.auth: return logging.info("Notice (Player %s in team %d): %s" % (client.name, client.team + 1, text)) - if client.version >= print_command_compatability_threshold: - async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]}])) - else: - async_start(self.send_msgs(client, [{"cmd": "Print", "text": text}])) + async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]}])) + def notify_client_multiple(self, client: Client, texts: typing.List[str]): if not client.auth: return - if client.version >= print_command_compatability_threshold: - async_start(self.send_msgs(client, - [{"cmd": "PrintJSON", "data": [{ "text": text }]} for text in texts])) - else: - async_start(self.send_msgs(client, [{"cmd": "Print", "text": text} for text in texts])) + async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]} for text in texts])) # loading @@ -685,7 +677,7 @@ class Context: def on_goal_achieved(self, client: Client): finished_msg = f'{self.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1})' \ f' has completed their goal.' - self.notify_all(finished_msg) + self.broadcast_text_all(finished_msg) if "auto" in self.collect_mode: collect_player(self, client.team, client.slot) if "auto" in self.release_mode: @@ -778,7 +770,7 @@ 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( + ctx.broadcast_text_all( f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) " f"{verb} {ctx.games[client.slot]} has joined. " f"Client({version_str}), {client.tags}).") @@ -791,42 +783,26 @@ async def on_client_joined(ctx: Context, client: Client): async def on_client_left(ctx: Context, client: Client): update_client_status(ctx, client, ClientStatus.CLIENT_UNKNOWN) - ctx.notify_all( + ctx.broadcast_text_all( "%s (Team #%d) has left the game" % (ctx.get_aliased_name(client.team, client.slot), client.team + 1)) ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc) async def countdown(ctx: Context, timer: int): - broadcast_countdown(ctx, timer, f"[Server]: Starting countdown of {timer}s") + ctx.broadcast_text_all(f"[Server]: Starting countdown of {timer}s", {"type": "Countdown", "countdown": timer}) if ctx.countdown_timer: ctx.countdown_timer = timer # timer is already running, set it to a different time else: ctx.countdown_timer = timer while ctx.countdown_timer > 0: - broadcast_countdown(ctx, ctx.countdown_timer, f"[Server]: {ctx.countdown_timer}") + ctx.broadcast_text_all(f"[Server]: {ctx.countdown_timer}", + {"type": "Countdown", "countdown": ctx.countdown_timer}) ctx.countdown_timer -= 1 await asyncio.sleep(1) - broadcast_countdown(ctx, 0, f"[Server]: GO") + ctx.broadcast_text_all(f"[Server]: GO", {"type": "Countdown", "countdown": 0}) ctx.countdown_timer = 0 -def broadcast_text_all(ctx: Context, text: str, additional_arguments: dict = {}): - old_clients, new_clients = [], [] - - for teams in ctx.clients.values(): - for clients in teams.values(): - for client in clients: - new_clients.append(client) if client.version >= print_command_compatability_threshold \ - else old_clients.append(client) - - ctx.broadcast(old_clients, [{"cmd": "Print", "text": text }]) - ctx.broadcast(new_clients, [{**{"cmd": "PrintJSON", "data": [{ "text": text }]}, **additional_arguments}]) - - -def broadcast_countdown(ctx: Context, timer: int, message: str): - broadcast_text_all(ctx, message, {"type": "Countdown", "countdown": timer}) - - def get_players_string(ctx: Context): auth_clients = {(c.team, c.slot) for c in ctx.endpoints if c.auth} @@ -894,7 +870,9 @@ def update_checked_locations(ctx: Context, team: int, slot: int): def release_player(ctx: Context, team: int, slot: int): """register any locations that are in the multidata""" all_locations = set(ctx.locations[slot]) - ctx.notify_all("%s (Team #%d) has released all remaining items from their world." % (ctx.player_names[(team, slot)], team + 1)) + ctx.broadcast_text_all( + "%s (Team #%d) has released all remaining items from their world." % + (ctx.player_names[(team, slot)], team + 1)) register_location_checks(ctx, team, slot, all_locations) update_checked_locations(ctx, team, slot) @@ -907,7 +885,8 @@ def collect_player(ctx: Context, team: int, slot: int, is_group: bool = False): if values[1] == slot: all_locations[source_slot].add(location_id) - ctx.notify_all("%s (Team #%d) has collected their items from other worlds." % (ctx.player_names[(team, slot)], team + 1)) + ctx.broadcast_text_all( + "%s (Team #%d) has collected their items from other worlds." % (ctx.player_names[(team, slot)], team + 1)) for source_player, location_ids in all_locations.items(): register_location_checks(ctx, team, source_player, location_ids, count_activity=False) update_checked_locations(ctx, team, source_player) @@ -1177,7 +1156,7 @@ class ClientMessageProcessor(CommonCommandProcessor): def __call__(self, raw: str) -> typing.Optional[bool]: if not raw.startswith("!admin"): - self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + raw) + self.ctx.broadcast_text_all(self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + raw) return super(ClientMessageProcessor, self).__call__(raw) def output(self, text): @@ -1205,8 +1184,8 @@ class ClientMessageProcessor(CommonCommandProcessor): "!admin /option server_password"): output = f"!admin /option server_password {('*' * random.randint(4, 16))}" # Otherwise notify the others what is happening. - self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team, - self.client.slot) + ': ' + output) + self.ctx.broadcast_text_all( + self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + output) if not self.ctx.server_password: self.output("Sorry, Remote administration is disabled") @@ -1243,7 +1222,7 @@ class ClientMessageProcessor(CommonCommandProcessor): def _cmd_players(self) -> bool: """Get information about connected and missing players.""" if len(self.ctx.player_names) < 10: - self.ctx.notify_all(get_players_string(self.ctx)) + self.ctx.broadcast_text_all(get_players_string(self.ctx)) else: self.output(get_players_string(self.ctx)) return True @@ -1381,7 +1360,7 @@ class ClientMessageProcessor(CommonCommandProcessor): new_item = NetworkItem(names[item_name], -1, self.client.slot) get_received_items(self.ctx, self.client.team, self.client.slot, False).append(new_item) get_received_items(self.ctx, self.client.team, self.client.slot, True).append(new_item) - self.ctx.notify_all( + self.ctx.broadcast_text_all( 'Cheat console: sending "' + item_name + '" to ' + self.ctx.get_aliased_name(self.client.team, self.client.slot)) send_new_items(self.ctx) @@ -1682,7 +1661,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): client.tags = args["tags"] if set(old_tags) != set(client.tags): client.no_locations = 'TextOnly' in client.tags or 'Tracker' in client.tags - ctx.notify_all( + ctx.broadcast_text_all( f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) has changed tags " f"from {old_tags} to {client.tags}.") @@ -1806,7 +1785,7 @@ class ServerCommandProcessor(CommonCommandProcessor): super(ServerCommandProcessor, self).output(text) def default(self, raw: str): - self.ctx.notify_all('[Server]: ' + raw) + self.ctx.broadcast_text_all('[Server]: ' + raw) def _cmd_save(self) -> bool: """Save current state to multidata""" @@ -1947,7 +1926,7 @@ class ServerCommandProcessor(CommonCommandProcessor): send_items_to(self.ctx, team, slot, *new_items) send_new_items(self.ctx) - self.ctx.notify_all( + self.ctx.broadcast_text_all( 'Cheat console: sending ' + ('' if amount == 1 else f'{amount} of ') + f'"{item_name}" to {self.ctx.get_aliased_name(team, slot)}') return True diff --git a/docs/network protocol.md b/docs/network protocol.md index ff503515..f743e474 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -9,7 +9,7 @@ These steps should be followed in order to establish a gameplay connection with 5. Client sends [Connect](#Connect) packet in order to authenticate with the server. 6. Server validates the client's packet and responds with [Connected](#Connected) or [ConnectionRefused](#ConnectionRefused). 7. Server may send [ReceivedItems](#ReceivedItems) to the client, in the case that the client is missing items that are queued up for it. -8. Server sends [Print](#Print) to all players to notify them of the new client connection. +8. Server sends [PrintJSON](#PrintJSON) to all players to notify them of updates. In the case that the client does not authenticate properly and receives a [ConnectionRefused](#ConnectionRefused) then the server will maintain the connection and allow for follow-up [Connect](#Connect) packet. @@ -54,7 +54,6 @@ These packets are are sent from the multiworld server to the client. They are no * [ReceivedItems](#ReceivedItems) * [LocationInfo](#LocationInfo) * [RoomUpdate](#RoomUpdate) -* [Print](#Print) * [PrintJSON](#PrintJSON) * [DataPackage](#DataPackage) * [Bounced](#Bounced) @@ -160,16 +159,8 @@ The arguments for RoomUpdate are identical to [RoomInfo](#RoomInfo) barring: All arguments for this packet are optional, only changes are sent. -### Print -Sent to clients purely to display a message to the player. -* *Deprecation warning: clients that connect with version 0.3.5 or higher will nolonger recieve Print packets, instead all messsages are send as [PrintJSON](#PrintJSON)* -#### Arguments -| Name | Type | Notes | -| ---- | ---- | ----- | -| text | str | Message to display to player. | - ### PrintJSON -Sent to clients purely to display a message to the player. This packet differs from [Print](#Print) in that the data being sent with this packet allows for more configurable or specific messaging. +Sent to clients purely to display a message to the player. The data being sent with this packet allows for configurability or specific messaging. #### Arguments | Name | Type | Notes | | ---- | ---- | ----- | @@ -181,7 +172,7 @@ Sent to clients purely to display a message to the player. This packet differs f | countdown | int | Is present if type is `Countdown`, denotes the amount of seconds remaining on the countdown. | ##### PrintJsonType -PrintJsonType indicates the type of [PrintJson](#PrintJson) packet, different types can be handled differently by the client and can also contain additional arguments. When receiving an unknown type the data's list\[[JSONMessagePart](#JSONMessagePart)\] should still be printed as normal. +PrintJsonType indicates the type of [PrintJson](#PrintJson) packet, different types can be handled differently by the client and can also contain additional arguments. When receiving an unknown or missing type the data's list\[[JSONMessagePart](#JSONMessagePart)\] should still be displayed to the player as normal text. Currently defined types are: | Type | Notes |