format MultiServer.py
This commit is contained in:
parent
1e7214a86b
commit
f4a2f344a7
|
@ -39,6 +39,7 @@ all_items = frozenset(lookup_any_item_name_to_id)
|
|||
all_locations = frozenset(lookup_any_location_name_to_id)
|
||||
all_console_names = frozenset(all_items | all_locations)
|
||||
|
||||
|
||||
class Client(Endpoint):
|
||||
version = Version(0, 0, 0)
|
||||
tags: typing.List[str] = []
|
||||
|
@ -75,10 +76,10 @@ class Context(Node):
|
|||
self.save_filename = None
|
||||
self.saving = False
|
||||
self.player_names = {}
|
||||
self.connect_names = {} # names of slots clients can connect to
|
||||
self.connect_names = {} # names of slots clients can connect to
|
||||
self.allow_forfeits = {}
|
||||
self.remote_items = set()
|
||||
self.locations:typing.Dict[int, typing.Dict[int, typing.Tuple[int, int]]] = {}
|
||||
self.locations: typing.Dict[int, typing.Dict[int, typing.Tuple[int, int]]] = {}
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.server_password = server_password
|
||||
|
@ -166,7 +167,6 @@ class Context(Node):
|
|||
server_options = decoded_obj.get("server_options", {})
|
||||
self._set_options(server_options)
|
||||
|
||||
|
||||
def get_players_package(self):
|
||||
return [NetworkPlayer(t, p, self.get_aliased_name(t, p), n) for (t, p), n in self.player_names.items()]
|
||||
|
||||
|
@ -174,7 +174,7 @@ class Context(Node):
|
|||
for key, value in server_options.items():
|
||||
data_type = self.simple_options.get(key, None)
|
||||
if data_type is not None:
|
||||
if value not in {False, True, None}: # some can be boolean OR text, such as password
|
||||
if value not in {False, True, None}: # some can be boolean OR text, such as password
|
||||
try:
|
||||
value = data_type(value)
|
||||
except Exception as e:
|
||||
|
@ -200,7 +200,7 @@ class Context(Node):
|
|||
|
||||
return False
|
||||
|
||||
def _save(self, exit_save:bool=False) -> bool:
|
||||
def _save(self, exit_save: bool = False) -> bool:
|
||||
try:
|
||||
encoded_save = pickle.dumps(self.get_save())
|
||||
with open(self.save_filename, "wb") as f:
|
||||
|
@ -366,7 +366,8 @@ async def server(websocket, path, ctx: Context):
|
|||
if not isinstance(e, websockets.WebSocketException):
|
||||
logging.exception(e)
|
||||
finally:
|
||||
logging.info("Disconnected")
|
||||
if ctx.log_network:
|
||||
logging.info("Disconnected")
|
||||
await ctx.disconnect(client)
|
||||
|
||||
|
||||
|
@ -374,8 +375,10 @@ async def on_client_connected(ctx: Context, client: Client):
|
|||
await ctx.send_msgs(client, [{
|
||||
'cmd': 'RoomInfo',
|
||||
'password': ctx.password is not None,
|
||||
'players': [NetworkPlayer(client.team, client.slot, ctx.name_aliases.get((client.team, client.slot), client.name), client.name) for client
|
||||
in ctx.endpoints if client.auth],
|
||||
'players': [
|
||||
NetworkPlayer(client.team, client.slot, ctx.name_aliases.get((client.team, client.slot), client.name),
|
||||
client.name) for client
|
||||
in ctx.endpoints if client.auth],
|
||||
# tags are for additional features in the communication.
|
||||
# Name them by feature or fork, as you feel is appropriate.
|
||||
'tags': ctx.tags,
|
||||
|
@ -403,9 +406,11 @@ async def on_client_joined(ctx: Context, client: Client):
|
|||
|
||||
ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
|
||||
async def on_client_left(ctx: Context, client: Client):
|
||||
update_client_status(ctx, client, ClientStatus.CLIENT_UNKNOWN)
|
||||
ctx.notify_all("%s (Team #%d) has left the game" % (ctx.get_aliased_name(client.team, client.slot), client.team + 1))
|
||||
ctx.notify_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)
|
||||
|
||||
|
||||
|
@ -447,7 +452,7 @@ def get_received_items(ctx: Context, team: int, player: int) -> typing.List[Netw
|
|||
|
||||
def send_new_items(ctx: Context):
|
||||
for client in ctx.endpoints:
|
||||
if client.auth: # can't send to disconnected client
|
||||
if client.auth: # can't send to disconnected client
|
||||
items = get_received_items(ctx, client.team, client.slot)
|
||||
if len(items) > client.send_index:
|
||||
asyncio.create_task(ctx.send_msgs(client, [{
|
||||
|
@ -504,7 +509,6 @@ def notify_team(ctx: Context, team: int, text: str):
|
|||
ctx.broadcast_team(team, [['Print', {"text": text}]])
|
||||
|
||||
|
||||
|
||||
def collect_hints(ctx: Context, team: int, slot: int, item: str) -> typing.List[NetUtils.Hint]:
|
||||
hints = []
|
||||
seeked_item_id = lookup_any_item_name_to_id[item]
|
||||
|
@ -520,7 +524,6 @@ def collect_hints(ctx: Context, team: int, slot: int, item: str) -> typing.List[
|
|||
|
||||
|
||||
def collect_hints_location(ctx: Context, team: int, slot: int, location: str) -> typing.List[NetUtils.Hint]:
|
||||
|
||||
seeked_location: int = Regions.lookup_name_to_id[location]
|
||||
item_id, receiving_player = ctx.locations[slot].get(seeked_location, (None, None))
|
||||
if item_id:
|
||||
|
@ -540,6 +543,7 @@ def format_hint(ctx: Context, team: int, hint: NetUtils.Hint) -> str:
|
|||
text += f" at {hint.entrance}"
|
||||
return text + (". (found)" if hint.found else ".")
|
||||
|
||||
|
||||
def json_format_send_event(net_item: NetworkItem, receiving_player: int):
|
||||
parts = []
|
||||
NetUtils.add_json_text(parts, net_item.player, type=NetUtils.JSONTypes.player_id)
|
||||
|
@ -559,7 +563,9 @@ def json_format_send_event(net_item: NetworkItem, receiving_player: int):
|
|||
return {"cmd": "PrintJSON", "data": parts, "type": "ItemSend",
|
||||
"receiving": receiving_player, "sending": net_item.player}
|
||||
|
||||
def get_intended_text(input_text: str, possible_answers: typing.Iterable[str]= all_console_names) -> typing.Tuple[str, bool, str]:
|
||||
|
||||
def get_intended_text(input_text: str, possible_answers: typing.Iterable[str] = all_console_names) -> typing.Tuple[
|
||||
str, bool, str]:
|
||||
picks = fuzzy_process.extract(input_text, possible_answers, limit=2)
|
||||
if len(picks) > 1:
|
||||
dif = picks[0][1] - picks[1][1]
|
||||
|
@ -684,11 +690,12 @@ class CommonCommandProcessor(CommandProcessor):
|
|||
"""List all current options. Warning: lists password."""
|
||||
self.output("Current options:")
|
||||
for option in self.ctx.simple_options:
|
||||
if option == "server_password" and self.marker == "!": #Do not display the server password to the client.
|
||||
self.output(f"Option server_password is set to {('*' * random.randint(4,16))}")
|
||||
if option == "server_password" and self.marker == "!": # Do not display the server password to the client.
|
||||
self.output(f"Option server_password is set to {('*' * random.randint(4, 16))}")
|
||||
else:
|
||||
self.output(f"Option {option} is set to {getattr(self.ctx, option)}")
|
||||
|
||||
|
||||
class ClientMessageProcessor(CommonCommandProcessor):
|
||||
marker = "!"
|
||||
|
||||
|
@ -715,11 +722,14 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
|||
"""Allow remote administration of the multiworld server"""
|
||||
|
||||
output = f"!admin {command}"
|
||||
if output.lower().startswith("!admin login"): # disallow others from seeing the supplied password, whether or not it is correct.
|
||||
if output.lower().startswith(
|
||||
"!admin login"): # disallow others from seeing the supplied password, whether or not it is correct.
|
||||
output = f"!admin login {('*' * random.randint(4, 16))}"
|
||||
elif output.lower().startswith("!admin /option server_password"): # disallow others from knowing what the new remote administration password is.
|
||||
elif output.lower().startswith(
|
||||
"!admin /option server_password"): # disallow others from knowing what the new remote administration password is.
|
||||
output = f"!admin /option server_password {('*' * random.randint(4, 16))}"
|
||||
self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + output) # Otherwise notify the others what is happening.
|
||||
self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team,
|
||||
self.client.slot) + ': ' + output) # Otherwise notify the others what is happening.
|
||||
|
||||
if not self.ctx.server_password:
|
||||
self.output("Sorry, Remote administration is disabled")
|
||||
|
@ -727,7 +737,8 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
|||
|
||||
if not command:
|
||||
if self.is_authenticated():
|
||||
self.output("Usage: !admin [Server command].\nUse !admin /help for help.\nUse !admin logout to log out of the current session.")
|
||||
self.output(
|
||||
"Usage: !admin [Server command].\nUse !admin /help for help.\nUse !admin logout to log out of the current session.")
|
||||
else:
|
||||
self.output("Usage: !admin login [password]")
|
||||
return True
|
||||
|
@ -810,7 +821,6 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
|||
"Sorry, !remaining requires you to have beaten the game on this server")
|
||||
return False
|
||||
|
||||
|
||||
def _cmd_missing(self) -> bool:
|
||||
"""List all missing location checks from the server's perspective"""
|
||||
|
||||
|
@ -850,7 +860,9 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
|||
if usable:
|
||||
new_item = NetworkItem(Items.item_table[item_name][2], -1, self.client.slot)
|
||||
get_received_items(self.ctx, self.client.team, self.client.slot).append(new_item)
|
||||
self.ctx.notify_all('Cheat console: sending "' + item_name + '" to ' + self.ctx.get_aliased_name(self.client.team, self.client.slot))
|
||||
self.ctx.notify_all(
|
||||
'Cheat console: sending "' + item_name + '" to ' + self.ctx.get_aliased_name(self.client.team,
|
||||
self.client.slot))
|
||||
send_new_items(self.ctx)
|
||||
return True
|
||||
else:
|
||||
|
@ -959,7 +971,7 @@ def get_client_points(ctx: Context, client: Client) -> int:
|
|||
|
||||
async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
||||
try:
|
||||
cmd:str = args["cmd"]
|
||||
cmd: str = args["cmd"]
|
||||
except:
|
||||
logging.exception(f"Could not get command from {args}")
|
||||
raise
|
||||
|
@ -1045,7 +1057,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
|||
items = get_received_items(ctx, client.team, client.slot)
|
||||
if items:
|
||||
client.send_index = len(items)
|
||||
await ctx.send_msgs(client, [{"cmd": "ReceivedItems","index": 0,
|
||||
await ctx.send_msgs(client, [{"cmd": "ReceivedItems", "index": 0,
|
||||
"items": items}])
|
||||
|
||||
elif cmd == 'LocationChecks':
|
||||
|
@ -1067,11 +1079,12 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
|||
|
||||
if cmd == 'Say':
|
||||
if "text" not in args or type(args["text"]) is not str or not args["text"].isprintable():
|
||||
await ctx.send_msgs(client, [{"cmd": "InvalidArguments", "text" : 'Say'}])
|
||||
await ctx.send_msgs(client, [{"cmd": "InvalidArguments", "text": 'Say'}])
|
||||
return
|
||||
|
||||
client.messageprocessor(args["text"])
|
||||
|
||||
|
||||
def update_client_status(ctx: Context, client: Client, new_status: ClientStatus):
|
||||
current = ctx.client_game_state[client.team, client.slot]
|
||||
if current != ClientStatus.CLIENT_GOAL: # can't undo goal completion
|
||||
|
@ -1083,6 +1096,7 @@ def update_client_status(ctx: Context, client: Client, new_status: ClientStatus)
|
|||
|
||||
ctx.client_game_state[client.team, client.slot] = new_status
|
||||
|
||||
|
||||
class ServerCommandProcessor(CommonCommandProcessor):
|
||||
def __init__(self, ctx: Context):
|
||||
self.ctx = ctx
|
||||
|
@ -1190,7 +1204,8 @@ class ServerCommandProcessor(CommonCommandProcessor):
|
|||
for (team, slot), name in self.ctx.player_names.items():
|
||||
if name.lower() == seeked_player:
|
||||
self.ctx.allow_forfeits[(team, slot)] = False
|
||||
self.output(f"Player {player_name} has to follow the server restrictions on use of the !forfeit command.")
|
||||
self.output(
|
||||
f"Player {player_name} has to follow the server restrictions on use of the !forfeit command.")
|
||||
return True
|
||||
|
||||
self.output(f"Could not find player {player_name} to forbid the !forfeit command for.")
|
||||
|
@ -1270,6 +1285,7 @@ class ServerCommandProcessor(CommonCommandProcessor):
|
|||
f"{', '.join(known)}")
|
||||
return False
|
||||
|
||||
|
||||
async def console(ctx: Context):
|
||||
session = prompt_toolkit.PromptSession()
|
||||
while ctx.running:
|
||||
|
@ -1356,7 +1372,7 @@ async def auto_shutdown(ctx, to_cancel=None):
|
|||
|
||||
|
||||
async def main(args: argparse.Namespace):
|
||||
logging.basicConfig(force = True,
|
||||
logging.basicConfig(force=True,
|
||||
format='[%(asctime)s] %(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO))
|
||||
|
||||
ctx = Context(args.host, args.port, args.server_password, args.password, args.location_check_points,
|
||||
|
|
Loading…
Reference in New Issue