Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into mm2
Browse files Browse the repository at this point in the history
  • Loading branch information
Silvris committed May 3, 2024
2 parents 01963b8 + 255e526 commit efa6bb4
Show file tree
Hide file tree
Showing 22 changed files with 500 additions and 435 deletions.
18 changes: 9 additions & 9 deletions Generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def main(args=None, callback=ERmain):
raise ValueError(f"File {fname} is invalid. Please fix your yaml.") from e

# sort dict for consistent results across platforms:
weights_cache = {key: value for key, value in sorted(weights_cache.items())}
weights_cache = {key: value for key, value in sorted(weights_cache.items(), key=lambda k: k[0].casefold())}
for filename, yaml_data in weights_cache.items():
if filename not in {args.meta_file_path, args.weights_file_path}:
for yaml in yaml_data:
Expand Down Expand Up @@ -353,7 +353,7 @@ def roll_meta_option(option_key, game: str, category_dict: Dict) -> Any:
if options[option_key].supports_weighting:
return get_choice(option_key, category_dict)
return category_dict[option_key]
raise Exception(f"Error generating meta option {option_key} for {game}.")
raise Options.OptionError(f"Error generating meta option {option_key} for {game}.")


def roll_linked_options(weights: dict) -> dict:
Expand Down Expand Up @@ -409,19 +409,19 @@ def roll_triggers(weights: dict, triggers: list) -> dict:


def handle_option(ret: argparse.Namespace, game_weights: dict, option_key: str, option: type(Options.Option), plando_options: PlandoOptions):
if option_key in game_weights:
try:
try:
if option_key in game_weights:
if not option.supports_weighting:
player_option = option.from_any(game_weights[option_key])
else:
player_option = option.from_any(get_choice(option_key, game_weights))
setattr(ret, option_key, player_option)
except Exception as e:
raise Exception(f"Error generating option {option_key} in {ret.game}") from e
else:
player_option.verify(AutoWorldRegister.world_types[ret.game], ret.name, plando_options)
player_option = option.from_any(option.default) # call the from_any here to support default "random"
setattr(ret, option_key, player_option)
except Exception as e:
raise Options.OptionError(f"Error generating option {option_key} in {ret.game}") from e
else:
setattr(ret, option_key, option.from_any(option.default)) # call the from_any here to support default "random"
player_option.verify(AutoWorldRegister.world_types[ret.game], ret.name, plando_options)


def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.bosses):
Expand Down
32 changes: 28 additions & 4 deletions MultiServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -803,14 +803,25 @@ async def on_client_disconnected(ctx: Context, client: Client):
await on_client_left(ctx, client)


_non_game_messages = {"HintGame": "hinting", "Tracker": "tracking", "TextOnly": "viewing"}
""" { tag: ui_message } """


async def on_client_joined(ctx: Context, client: Client):
if ctx.client_game_state[client.team, client.slot] == ClientStatus.CLIENT_UNKNOWN:
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"

for tag, verb in _non_game_messages.items():
if tag in client.tags:
final_verb = verb
break
else:
final_verb = "playing"

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"{final_verb} {ctx.games[client.slot]} has joined. "
f"Client({version_str}), {client.tags}.",
{"type": "Join", "team": client.team, "slot": client.slot, "tags": client.tags})
ctx.notify_client(client, "Now that you are connected, "
Expand All @@ -825,8 +836,19 @@ async def on_client_left(ctx: Context, client: Client):
if len(ctx.clients[client.team][client.slot]) < 1:
update_client_status(ctx, client, ClientStatus.CLIENT_UNKNOWN)
ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc)

version_str = '.'.join(str(x) for x in client.version)

for tag, verb in _non_game_messages.items():
if tag in client.tags:
final_verb = f"stopped {verb}"
break
else:
final_verb = "left"

ctx.broadcast_text_all(
"%s (Team #%d) has left the game" % (ctx.get_aliased_name(client.team, client.slot), client.team + 1),
f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) has {final_verb} the game. "
f"Client({version_str}), {client.tags}.",
{"type": "Part", "team": client.team, "slot": client.slot})


Expand Down Expand Up @@ -1631,7 +1653,9 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
else:
team, slot = ctx.connect_names[args['name']]
game = ctx.games[slot]
ignore_game = ("TextOnly" in args["tags"] or "Tracker" in args["tags"]) and not args.get("game")

ignore_game = not args.get("game") and any(tag in _non_game_messages for tag in args["tags"])

if not ignore_game and args['game'] != game:
errors.add('InvalidGame')
minver = min_client_version if ignore_game else ctx.minimum_client_versions[slot]
Expand Down
4 changes: 4 additions & 0 deletions Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
import pathlib


class OptionError(ValueError):
pass


class Visibility(enum.IntFlag):
none = 0b0000
template = 0b0001
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
try:
requirement = 'cx-Freeze>=6.15.16,<7'
requirement = 'cx-Freeze>=7.0.0'
import pkg_resources
try:
pkg_resources.require(requirement)
Expand Down Expand Up @@ -228,8 +228,8 @@ def finalize_options(self):


# Override cx_Freeze's build_exe command for pre and post build steps
class BuildExeCommand(cx_Freeze.command.build_exe.BuildEXE):
user_options = cx_Freeze.command.build_exe.BuildEXE.user_options + [
class BuildExeCommand(cx_Freeze.command.build_exe.build_exe):
user_options = cx_Freeze.command.build_exe.build_exe.user_options + [
('yes', 'y', 'Answer "yes" to all questions.'),
('extra-data=', None, 'Additional files to add.'),
]
Expand Down
66 changes: 62 additions & 4 deletions test/general/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from argparse import Namespace
from typing import List, Optional, Tuple, Type, Union

from BaseClasses import CollectionState, MultiWorld
from BaseClasses import CollectionState, Item, ItemClassification, Location, MultiWorld, Region
from worlds.AutoWorld import World, call_all

gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill")
Expand All @@ -17,19 +17,21 @@ def setup_solo_multiworld(
:param steps: The gen steps that should be called on the generated multiworld before returning. Default calls
steps through pre_fill
:param seed: The seed to be used when creating this multiworld
:return: The generated multiworld
"""
return setup_multiworld(world_type, steps, seed)


def setup_multiworld(worlds: Union[List[Type[World]], Type[World]], steps: Tuple[str, ...] = gen_steps,
seed: Optional[int] = None) -> MultiWorld:
seed: Optional[int] = None) -> MultiWorld:
"""
Creates a multiworld with a player for each provided world type, allowing duplicates, setting default options, and
calling the provided gen steps.
:param worlds: type/s of worlds to generate a multiworld for
:param steps: gen steps that should be called before returning. Default calls through pre_fill
:param worlds: Type/s of worlds to generate a multiworld for
:param steps: Gen steps that should be called before returning. Default calls through pre_fill
:param seed: The seed to be used when creating this multiworld
:return: The generated multiworld
"""
if not isinstance(worlds, list):
worlds = [worlds]
Expand All @@ -49,3 +51,59 @@ def setup_multiworld(worlds: Union[List[Type[World]], Type[World]], steps: Tuple
for step in steps:
call_all(multiworld, step)
return multiworld


class TestWorld(World):
game = f"Test Game"
item_name_to_id = {}
location_name_to_id = {}
hidden = True


def generate_test_multiworld(players: int = 1) -> MultiWorld:
"""
Generates a multiworld using a special Test Case World class, and seed of 0.
:param players: Number of players to generate the multiworld for
:return: The generated test multiworld
"""
multiworld = setup_multiworld([TestWorld] * players, seed=0)
multiworld.regions += [Region("Menu", player_id + 1, multiworld) for player_id in range(players)]

return multiworld


def generate_locations(count: int, player_id: int, region: Region, address: Optional[int] = None,
tag: str = "") -> List[Location]:
"""
Generates the specified amount of locations for the player and adds them to the specified region.
:param count: Number of locations to create
:param player_id: ID of the player to create the locations for
:param address: Address for the specified locations. They will all share the same address if multiple are created
:param region: Parent region to add these locations to
:param tag: Tag to add to the name of the generated locations
:return: List containing the created locations
"""
prefix = f"player{player_id}{tag}_location"

locations = [Location(player_id, f"{prefix}{i}", address, region) for i in range(count)]
region.locations += locations
return locations


def generate_items(count: int, player_id: int, advancement: bool = False, code: int = None) -> List[Item]:
"""
Generates the specified amount of items for the target player.
:param count: The amount of items to create
:param player_id: ID of the player to create the items for
:param advancement: Whether the created items should be advancement
:param code: The code the items should be created with
:return: List containing the created items
"""
item_type = "prog" if advancement else ""
classification = ItemClassification.progression if advancement else ItemClassification.filler

items = [Item(f"player{player_id}_{item_type}item{i}", classification, code, player_id) for i in range(count)]
return items
Loading

0 comments on commit efa6bb4

Please sign in to comment.