From e74333cbd391dde70e6f37de4a3eb0e1a959473c Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 16 Jan 2022 02:20:37 +0100 Subject: [PATCH] MultiServer: remove location hinting from !hint and /hint; add /hint_location --- MultiServer.py | 65 +++++++++++++++++++++++++++++++-------------- worlds/AutoWorld.py | 8 +++--- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/MultiServer.py b/MultiServer.py index 12845121..e431f8b5 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -543,7 +543,10 @@ async def on_client_joined(ctx: Context, client: Client): 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}).") - + ctx.notify_client(client, "Now that you are connected, " + "you can use !help to list commands to run via the server." + "If your client supports it, " + "you may have additional local commands you can list with /help.") ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc) @@ -1088,7 +1091,7 @@ class ClientMessageProcessor(CommonCommandProcessor): self.output("Cheating is disabled.") return False - def get_hints(self, input_text: str, explicit_location: bool = False) -> bool: + def get_hints(self, input_text: str, for_location: bool = False) -> bool: points_available = get_client_points(self.ctx, self.client) if not input_text: hints = {hint.re_check(self.ctx, self.client.team) for hint in @@ -1100,20 +1103,21 @@ class ClientMessageProcessor(CommonCommandProcessor): return True else: world = proxy_worlds[self.ctx.games[self.client.slot]] - item_name, usable, response = get_intended_text(input_text, - world.all_names if not explicit_location else world.location_names) + names = world.location_names if for_location else world.all_item_and_group_names + hint_name, usable, response = get_intended_text(input_text, + names) if usable: - if item_name in world.hint_blacklist: - self.output(f"Sorry, \"{item_name}\" is marked as non-hintable.") + if hint_name in world.hint_blacklist: + self.output(f"Sorry, \"{hint_name}\" is marked as non-hintable.") hints = [] - elif item_name in world.item_name_groups and not explicit_location: + elif not for_location and hint_name in world.item_name_groups: # item group name hints = [] - for item in world.item_name_groups[item_name]: + for item in world.item_name_groups[hint_name]: hints.extend(collect_hints(self.ctx, self.client.team, self.client.slot, item)) - elif item_name in world.item_names and not explicit_location: # item name - hints = collect_hints(self.ctx, self.client.team, self.client.slot, item_name) + elif not for_location and hint_name in world.item_names: # item name + hints = collect_hints(self.ctx, self.client.team, self.client.slot, hint_name) else: # location name - hints = collect_hints_location(self.ctx, self.client.team, self.client.slot, item_name) + hints = collect_hints_location(self.ctx, self.client.team, self.client.slot, hint_name) cost = self.ctx.get_hint_cost(self.client.slot) if hints: new_hints = set(hints) - self.ctx.hints[self.client.team, self.client.slot] @@ -1175,8 +1179,8 @@ class ClientMessageProcessor(CommonCommandProcessor): @mark_raw def _cmd_hint(self, item_or_location: str = "") -> bool: - """Use !hint {item_name/location_name}, - for example !hint Lamp or !hint Link's House to get a spoiler peek for that location or item. + """Use !hint {item_name}, + for example !hint Lamp to get a spoiler peek for that item. If hint costs are on, this will only give you one new result, you can rerun the command to get more in that case.""" return self.get_hints(item_or_location) @@ -1511,23 +1515,44 @@ class ServerCommandProcessor(CommonCommandProcessor): self.output(response) return False - def _cmd_hint(self, player_name: str, *item_or_location: str) -> bool: - """Send out a hint for a player's item or location to their team""" + def _cmd_hint(self, player_name: str, *item: str) -> bool: + """Send out a hint for a player's item to their team""" seeked_player, usable, response = get_intended_text(player_name, self.ctx.player_names.values()) if usable: team, slot = self.ctx.player_name_lookup[seeked_player] - item = " ".join(item_or_location) + item = " ".join(item) world = proxy_worlds[self.ctx.games[slot]] - item, usable, response = get_intended_text(item, world.all_names) + item, usable, response = get_intended_text(item, world.all_item_and_group_names) if usable: if item in world.item_name_groups: hints = [] for item in world.item_name_groups[item]: hints.extend(collect_hints(self.ctx, team, slot, item)) - elif item in world.item_names: # item name + else: # item name hints = collect_hints(self.ctx, team, slot, item) - else: # location name - hints = collect_hints_location(self.ctx, team, slot, item) + if hints: + notify_hints(self.ctx, team, hints) + else: + self.output("No hints found.") + return True + else: + self.output(response) + return False + + else: + self.output(response) + return False + + def _cmd_hint_location(self, player_name: str, *location: str) -> bool: + """Send out a hint for a player's location to their team""" + seeked_player, usable, response = get_intended_text(player_name, self.ctx.player_names.values()) + if usable: + team, slot = self.ctx.player_name_lookup[seeked_player] + item = " ".join(location) + world = proxy_worlds[self.ctx.games[slot]] + item, usable, response = get_intended_text(item, world.location_names) + if usable: + hints = collect_hints_location(self.ctx, team, slot, item) if hints: notify_hints(self.ctx, team, hints) else: diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index abeb9783..3da65162 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Dict, Set, Tuple, List, Optional, TextIO +from typing import Dict, Set, Tuple, List, Optional, TextIO, Any from BaseClasses import MultiWorld, Item, CollectionState, Location from Options import Option @@ -8,7 +8,7 @@ from Options import Option class AutoWorldRegister(type): world_types: Dict[str, World] = {} - def __new__(cls, name, bases, dct): + def __new__(cls, name: str, bases, dct: Dict[str, Any]): # filter out any events dct["item_name_to_id"] = {name: id for name, id in dct["item_name_to_id"].items() if id} dct["location_name_to_id"] = {name: id for name, id in dct["location_name_to_id"].items() if id} @@ -19,7 +19,7 @@ class AutoWorldRegister(type): # build rest dct["item_names"] = frozenset(dct["item_name_to_id"]) dct["location_names"] = frozenset(dct["location_name_to_id"]) - dct["all_names"] = dct["item_names"] | dct["location_names"] | set(dct.get("item_name_groups", {})) + dct["all_item_and_group_names"] = frozenset(dct["item_names"] | set(dct.get("item_name_groups", {}))) # construct class new_class = super().__new__(cls, name, bases, dct) @@ -71,7 +71,7 @@ class World(metaclass=AutoWorldRegister): options: Dict[str, type(Option)] = {} # link your Options mapping game: str # name the game topology_present: bool = False # indicate if world type has any meaningful layout/pathing - all_names: Set[str] = frozenset() # gets automatically populated with all item, item group and location names + all_item_and_group_names: Set[str] = frozenset() # gets automatically populated with all item and item group names # map names to their IDs item_name_to_id: Dict[str, int] = {}