Core: Phase out Print packets (#1364)
This commit is contained in:
parent
cae1e683e2
commit
ff175008a1
|
@ -41,7 +41,6 @@ from NetUtils import Endpoint, ClientStatus, NetworkItem, decode, encode, Networ
|
||||||
SlotType
|
SlotType
|
||||||
|
|
||||||
min_client_version = Version(0, 1, 6)
|
min_client_version = Version(0, 1, 6)
|
||||||
print_command_compatability_threshold = Version(0, 3, 5) # Remove backwards compatibility around 0.3.7
|
|
||||||
colorama.init()
|
colorama.init()
|
||||||
|
|
||||||
|
|
||||||
|
@ -309,6 +308,10 @@ class Context:
|
||||||
endpoints = (endpoint for endpoint in self.endpoints if endpoint.auth)
|
endpoints = (endpoint for endpoint in self.endpoints if endpoint.auth)
|
||||||
async_start(self.broadcast_send_encoded_msgs(endpoints, msgs))
|
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]):
|
def broadcast_team(self, team: int, msgs: typing.List[dict]):
|
||||||
msgs = self.dumper(msgs)
|
msgs = self.dumper(msgs)
|
||||||
endpoints = (endpoint for endpoint in itertools.chain.from_iterable(self.clients[team].values()))
|
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)
|
self.clients[endpoint.team][endpoint.slot].remove(endpoint)
|
||||||
await on_client_disconnected(self, 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):
|
def notify_client(self, client: Client, text: str):
|
||||||
if not client.auth:
|
if not client.auth:
|
||||||
return
|
return
|
||||||
logging.info("Notice (Player %s in team %d): %s" % (client.name, client.team + 1, text))
|
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 }]}]))
|
async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]}]))
|
||||||
else:
|
|
||||||
async_start(self.send_msgs(client, [{"cmd": "Print", "text": text}]))
|
|
||||||
|
|
||||||
def notify_client_multiple(self, client: Client, texts: typing.List[str]):
|
def notify_client_multiple(self, client: Client, texts: typing.List[str]):
|
||||||
if not client.auth:
|
if not client.auth:
|
||||||
return
|
return
|
||||||
if client.version >= print_command_compatability_threshold:
|
async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]} for text in texts]))
|
||||||
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]))
|
|
||||||
|
|
||||||
# loading
|
# loading
|
||||||
|
|
||||||
|
@ -685,7 +677,7 @@ class Context:
|
||||||
def on_goal_achieved(self, client: Client):
|
def on_goal_achieved(self, client: Client):
|
||||||
finished_msg = f'{self.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1})' \
|
finished_msg = f'{self.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1})' \
|
||||||
f' has completed their goal.'
|
f' has completed their goal.'
|
||||||
self.notify_all(finished_msg)
|
self.broadcast_text_all(finished_msg)
|
||||||
if "auto" in self.collect_mode:
|
if "auto" in self.collect_mode:
|
||||||
collect_player(self, client.team, client.slot)
|
collect_player(self, client.team, client.slot)
|
||||||
if "auto" in self.release_mode:
|
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)
|
update_client_status(ctx, client, ClientStatus.CLIENT_CONNECTED)
|
||||||
version_str = '.'.join(str(x) for x in client.version)
|
version_str = '.'.join(str(x) for x in client.version)
|
||||||
verb = "tracking" if "Tracker" in client.tags else "playing"
|
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"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) "
|
||||||
f"{verb} {ctx.games[client.slot]} has joined. "
|
f"{verb} {ctx.games[client.slot]} has joined. "
|
||||||
f"Client({version_str}), {client.tags}).")
|
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):
|
async def on_client_left(ctx: Context, client: Client):
|
||||||
update_client_status(ctx, client, ClientStatus.CLIENT_UNKNOWN)
|
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))
|
"%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)
|
ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
async def countdown(ctx: Context, timer: int):
|
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:
|
if ctx.countdown_timer:
|
||||||
ctx.countdown_timer = timer # timer is already running, set it to a different time
|
ctx.countdown_timer = timer # timer is already running, set it to a different time
|
||||||
else:
|
else:
|
||||||
ctx.countdown_timer = timer
|
ctx.countdown_timer = timer
|
||||||
while ctx.countdown_timer > 0:
|
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
|
ctx.countdown_timer -= 1
|
||||||
await asyncio.sleep(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
|
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):
|
def get_players_string(ctx: Context):
|
||||||
auth_clients = {(c.team, c.slot) for c in ctx.endpoints if c.auth}
|
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):
|
def release_player(ctx: Context, team: int, slot: int):
|
||||||
"""register any locations that are in the multidata"""
|
"""register any locations that are in the multidata"""
|
||||||
all_locations = set(ctx.locations[slot])
|
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)
|
register_location_checks(ctx, team, slot, all_locations)
|
||||||
update_checked_locations(ctx, team, slot)
|
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:
|
if values[1] == slot:
|
||||||
all_locations[source_slot].add(location_id)
|
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():
|
for source_player, location_ids in all_locations.items():
|
||||||
register_location_checks(ctx, team, source_player, location_ids, count_activity=False)
|
register_location_checks(ctx, team, source_player, location_ids, count_activity=False)
|
||||||
update_checked_locations(ctx, team, source_player)
|
update_checked_locations(ctx, team, source_player)
|
||||||
|
@ -1177,7 +1156,7 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||||
|
|
||||||
def __call__(self, raw: str) -> typing.Optional[bool]:
|
def __call__(self, raw: str) -> typing.Optional[bool]:
|
||||||
if not raw.startswith("!admin"):
|
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)
|
return super(ClientMessageProcessor, self).__call__(raw)
|
||||||
|
|
||||||
def output(self, text):
|
def output(self, text):
|
||||||
|
@ -1205,8 +1184,8 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||||
"!admin /option server_password"):
|
"!admin /option server_password"):
|
||||||
output = f"!admin /option server_password {('*' * random.randint(4, 16))}"
|
output = f"!admin /option server_password {('*' * random.randint(4, 16))}"
|
||||||
# Otherwise notify the others what is happening.
|
# Otherwise notify the others what is happening.
|
||||||
self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team,
|
self.ctx.broadcast_text_all(
|
||||||
self.client.slot) + ': ' + output)
|
self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + output)
|
||||||
|
|
||||||
if not self.ctx.server_password:
|
if not self.ctx.server_password:
|
||||||
self.output("Sorry, Remote administration is disabled")
|
self.output("Sorry, Remote administration is disabled")
|
||||||
|
@ -1243,7 +1222,7 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||||
def _cmd_players(self) -> bool:
|
def _cmd_players(self) -> bool:
|
||||||
"""Get information about connected and missing players."""
|
"""Get information about connected and missing players."""
|
||||||
if len(self.ctx.player_names) < 10:
|
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:
|
else:
|
||||||
self.output(get_players_string(self.ctx))
|
self.output(get_players_string(self.ctx))
|
||||||
return True
|
return True
|
||||||
|
@ -1381,7 +1360,7 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||||
new_item = NetworkItem(names[item_name], -1, self.client.slot)
|
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, False).append(new_item)
|
||||||
get_received_items(self.ctx, self.client.team, self.client.slot, True).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,
|
'Cheat console: sending "' + item_name + '" to ' + self.ctx.get_aliased_name(self.client.team,
|
||||||
self.client.slot))
|
self.client.slot))
|
||||||
send_new_items(self.ctx)
|
send_new_items(self.ctx)
|
||||||
|
@ -1682,7 +1661,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
||||||
client.tags = args["tags"]
|
client.tags = args["tags"]
|
||||||
if set(old_tags) != set(client.tags):
|
if set(old_tags) != set(client.tags):
|
||||||
client.no_locations = 'TextOnly' in client.tags or 'Tracker' in 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"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) has changed tags "
|
||||||
f"from {old_tags} to {client.tags}.")
|
f"from {old_tags} to {client.tags}.")
|
||||||
|
|
||||||
|
@ -1806,7 +1785,7 @@ class ServerCommandProcessor(CommonCommandProcessor):
|
||||||
super(ServerCommandProcessor, self).output(text)
|
super(ServerCommandProcessor, self).output(text)
|
||||||
|
|
||||||
def default(self, raw: str):
|
def default(self, raw: str):
|
||||||
self.ctx.notify_all('[Server]: ' + raw)
|
self.ctx.broadcast_text_all('[Server]: ' + raw)
|
||||||
|
|
||||||
def _cmd_save(self) -> bool:
|
def _cmd_save(self) -> bool:
|
||||||
"""Save current state to multidata"""
|
"""Save current state to multidata"""
|
||||||
|
@ -1947,7 +1926,7 @@ class ServerCommandProcessor(CommonCommandProcessor):
|
||||||
send_items_to(self.ctx, team, slot, *new_items)
|
send_items_to(self.ctx, team, slot, *new_items)
|
||||||
|
|
||||||
send_new_items(self.ctx)
|
send_new_items(self.ctx)
|
||||||
self.ctx.notify_all(
|
self.ctx.broadcast_text_all(
|
||||||
'Cheat console: sending ' + ('' if amount == 1 else f'{amount} of ') +
|
'Cheat console: sending ' + ('' if amount == 1 else f'{amount} of ') +
|
||||||
f'"{item_name}" to {self.ctx.get_aliased_name(team, slot)}')
|
f'"{item_name}" to {self.ctx.get_aliased_name(team, slot)}')
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -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.
|
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).
|
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.
|
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.
|
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)
|
* [ReceivedItems](#ReceivedItems)
|
||||||
* [LocationInfo](#LocationInfo)
|
* [LocationInfo](#LocationInfo)
|
||||||
* [RoomUpdate](#RoomUpdate)
|
* [RoomUpdate](#RoomUpdate)
|
||||||
* [Print](#Print)
|
|
||||||
* [PrintJSON](#PrintJSON)
|
* [PrintJSON](#PrintJSON)
|
||||||
* [DataPackage](#DataPackage)
|
* [DataPackage](#DataPackage)
|
||||||
* [Bounced](#Bounced)
|
* [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.
|
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
|
### 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
|
#### Arguments
|
||||||
| Name | Type | Notes |
|
| 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. |
|
| countdown | int | Is present if type is `Countdown`, denotes the amount of seconds remaining on the countdown. |
|
||||||
|
|
||||||
##### PrintJsonType
|
##### 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:
|
Currently defined types are:
|
||||||
| Type | Notes |
|
| Type | Notes |
|
||||||
|
|
Loading…
Reference in New Issue