From 3e1941a561693c077d32ade153ee820361acae4e Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 16 May 2021 00:21:00 +0200 Subject: [PATCH] allow Factorio Client to recognize if it's trying to connect to the wrong multiworld. --- BaseClasses.py | 1 + CommonClient.py | 68 ++++++++++++++------------ FactorioClient.py | 1 + Main.py | 10 ++-- MultiServer.py | 2 +- data/factorio/mod_template/control.lua | 4 +- worlds/factorio/Mod.py | 8 +-- worlds/minecraft/__init__.py | 7 +-- 8 files changed, 56 insertions(+), 45 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 84a558d1..884a31df 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -44,6 +44,7 @@ class MultiWorld(): self.shops = [] self.itempool = [] self.seed = None + self.seed_name: str = "Unavailable" self.precollected_items = [] self.state = CollectionState(self) self._cached_entrances = None diff --git a/CommonClient.py b/CommonClient.py index 3ffe0b62..de17302c 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -112,6 +112,7 @@ class CommonContext(): self.team = None self.slot = None self.auth = None + self.seed_name = None self.locations_checked: typing.Set[int] = set() self.locations_scouted: typing.Set[int] = set() @@ -278,39 +279,42 @@ async def process_server_cmd(ctx: CommonContext, args: dict): logger.exception(f"Could not get command from {args}") raise if cmd == 'RoomInfo': - logger.info('--------------------------------') - logger.info('Room Information:') - logger.info('--------------------------------') - version = args["version"] - ctx.server_version = tuple(version) - version = ".".join(str(item) for item in version) - - logger.info(f'Server protocol version: {version}') - logger.info("Server protocol tags: " + ", ".join(args["tags"])) - if args['password']: - logger.info('Password required') - logger.info(f"Forfeit setting: {args['forfeit_mode']}") - logger.info(f"Remaining setting: {args['remaining_mode']}") - logger.info(f"A !hint costs {args['hint_cost']}% of checks points and you get {args['location_check_points']}" - f" for each location checked. Use !hint for more information.") - ctx.hint_cost = int(args['hint_cost']) - ctx.check_points = int(args['location_check_points']) - ctx.forfeit_mode = args['forfeit_mode'] - ctx.remaining_mode = args['remaining_mode'] - if len(args['players']) < 1: - logger.info('No player connected') + if ctx.seed_name and ctx.seed_name != args["seed_name"]: + logger.info("The server is running a different multiworld than your client is. (invalid seed_name)") else: - args['players'].sort() - current_team = -1 - logger.info('Players:') - for network_player in args['players']: - if network_player.team != current_team: - logger.info(f' Team #{network_player.team + 1}') - current_team = network_player.team - logger.info(' %s (Player %d)' % (network_player.alias, network_player.slot)) - if args["datapackage_version"] > network_data_package["version"] or args["datapackage_version"] == 0: - await ctx.send_msgs([{"cmd": "GetDataPackage"}]) - await ctx.server_auth(args['password']) + logger.info('--------------------------------') + logger.info('Room Information:') + logger.info('--------------------------------') + version = args["version"] + ctx.server_version = tuple(version) + version = ".".join(str(item) for item in version) + + logger.info(f'Server protocol version: {version}') + logger.info("Server protocol tags: " + ", ".join(args["tags"])) + if args['password']: + logger.info('Password required') + logger.info(f"Forfeit setting: {args['forfeit_mode']}") + logger.info(f"Remaining setting: {args['remaining_mode']}") + logger.info(f"A !hint costs {args['hint_cost']}% of checks points and you get {args['location_check_points']}" + f" for each location checked. Use !hint for more information.") + ctx.hint_cost = int(args['hint_cost']) + ctx.check_points = int(args['location_check_points']) + ctx.forfeit_mode = args['forfeit_mode'] + ctx.remaining_mode = args['remaining_mode'] + if len(args['players']) < 1: + logger.info('No player connected') + else: + args['players'].sort() + current_team = -1 + logger.info('Players:') + for network_player in args['players']: + if network_player.team != current_team: + logger.info(f' Team #{network_player.team + 1}') + current_team = network_player.team + logger.info(' %s (Player %d)' % (network_player.alias, network_player.slot)) + if args["datapackage_version"] > network_data_package["version"] or args["datapackage_version"] == 0: + await ctx.send_msgs([{"cmd": "GetDataPackage"}]) + await ctx.server_auth(args['password']) elif cmd == 'DataPackage': logger.info("Got new ID/Name Datapackage") diff --git a/FactorioClient.py b/FactorioClient.py index b08c44b8..51261c13 100644 --- a/FactorioClient.py +++ b/FactorioClient.py @@ -107,6 +107,7 @@ async def game_watcher(ctx: FactorioContext, bridge_file: str): research_data = {int(tech_name.split("-")[1]) for tech_name in research_data} victory = data["victory"] ctx.auth = data["slot_name"] + ctx.seed_name = data["seed_name"] if not ctx.finished_game and victory: await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) diff --git a/Main.py b/Main.py index c1026e87..51eb2040 100644 --- a/Main.py +++ b/Main.py @@ -67,6 +67,7 @@ def main(args, seed=None): world.secure() else: world.random.seed(world.seed) + world.seed_name = str(args.outputname if args.outputname else world.seed) world.shuffle = args.shuffle.copy() world.logic = args.logic.copy() @@ -314,7 +315,7 @@ def main(args, seed=None): balance_multiworld_progression(world) logger.info('Generating output files.') - outfilebase = 'AP_%s' % (args.outputname if args.outputname else world.seed) + outfilebase = 'AP_' + world.seed_name rom_names = [] def _gen_rom(team: int, player: int): @@ -422,8 +423,7 @@ def main(args, seed=None): for player in world.alttp_player_ids: rom_futures.append(pool.submit(_gen_rom, team, player)) for player in world.factorio_player_ids: - mod_futures.append(pool.submit(generate_mod, world, player, - str(args.outputname if args.outputname else world.seed))) + mod_futures.append(pool.submit(generate_mod, world, player)) def get_entrance_to_region(region: Region): for entrance in region.entrances: @@ -559,7 +559,7 @@ def main(args, seed=None): "version": tuple(_version_tuple), "tags": ["AP"], "minimum_versions": minimum_versions, - "seed_name": str(args.outputname if args.outputname else world.seed) + "seed_name": world.seed_name }), 9) with open(output_path('%s.archipelago' % outfilebase), 'wb') as f: @@ -578,7 +578,7 @@ def main(args, seed=None): multidata_task.result() # retrieve exception if one exists pool.shutdown() # wait for all queued tasks to complete for player in world.minecraft_player_ids: # Doing this after shutdown prevents the .apmc from being generated if there's an error - generate_mc_data(world, player, str(args.outputname if args.outputname else world.seed)) + generate_mc_data(world, player) if not args.skip_playthrough: logger.info('Calculating playthrough.') create_playthrough(world) diff --git a/MultiServer.py b/MultiServer.py index 68accf0e..592dc13b 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -43,7 +43,7 @@ class Client(Endpoint): version = Version(0, 0, 0) tags: typing.List[str] = [] - def __init__(self, socket: websockets.server.WebSocketServerProtocol, ctx: Context): + def __init__(self, socket: websockets.WebSocketServerProtocol, ctx: Context): super().__init__(socket) self.auth = False self.name = None diff --git a/data/factorio/mod_template/control.lua b/data/factorio/mod_template/control.lua index ceb8f73d..b57c26e5 100644 --- a/data/factorio/mod_template/control.lua +++ b/data/factorio/mod_template/control.lua @@ -10,6 +10,7 @@ require "util" FREE_SAMPLES = {{ free_samples }} SLOT_NAME = "{{ slot_name }}" +SEED_NAME = "{{ seed_name }}" --SUPPRESS_INVENTORY_EVENTS = false -- Initialize force data, either from it being created or already being part of the game when the mod was added. @@ -180,7 +181,8 @@ function dumpInfo(force) local data_collection = { ["research_done"] = research_done, ["victory"] = chain_lookup(global, "forcedata", force.name, "victory"), - ["slot_name"] = SLOT_NAME + ["slot_name"] = SLOT_NAME, + ["seed_name"] = SEED_NAME } for tech_name, tech in pairs(force.technologies) do diff --git a/worlds/factorio/Mod.py b/worlds/factorio/Mod.py index 2a9aa957..c8efb435 100644 --- a/worlds/factorio/Mod.py +++ b/worlds/factorio/Mod.py @@ -46,7 +46,7 @@ rocket_recipes = { '{{"copper-cable", 10}, {"iron-plate", 10}, {"wood", 10}}' } -def generate_mod(world: MultiWorld, player: int, seedname: str): +def generate_mod(world: MultiWorld, player: int): global template, locale_template, control_template with template_load_lock: if not template: @@ -59,7 +59,7 @@ def generate_mod(world: MultiWorld, player: int, seedname: str): locations = [] for location in world.get_filled_locations(player): locations.append((location.name, location.item.name, location.item.player)) - mod_name = f"AP-{seedname}-P{player}-{world.player_names[player][0]}" + mod_name = f"AP-{world.seed_name}-P{player}-{world.player_names[player][0]}" tech_cost = {0: 0.1, 1: 0.25, 2: 0.5, @@ -72,10 +72,12 @@ def generate_mod(world: MultiWorld, player: int, seedname: str): "tech_cost_scale": tech_cost, "custom_data": world.custom_data[player], "tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites[player], "rocket_recipe" : rocket_recipes[world.max_science_pack[player].value], - "slot_name": world.player_names[player][0], + "slot_name": world.player_names[player][0], "seed_name": world.seed_name, "starting_items": world.starting_items[player]} + for factorio_option in Options.factorio_options: template_data[factorio_option] = getattr(world, factorio_option)[player].value + control_code = control_template.render(**template_data) data_final_fixes_code = template.render(**template_data) diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py index 02bcba60..6c7e2562 100644 --- a/worlds/minecraft/__init__.py +++ b/worlds/minecraft/__init__.py @@ -9,20 +9,21 @@ from Options import minecraft_options client_version = (0, 3) -def generate_mc_data(world: MultiWorld, player: int, seedname: str): +def generate_mc_data(world: MultiWorld, player: int): import base64, json from Utils import output_path exits = ["Overworld Structure 1", "Overworld Structure 2", "Nether Structure 1", "Nether Structure 2", "The End Structure"] data = { 'world_seed': Random(world.rom_seeds[player]).getrandbits(32), # consistent and doesn't interfere with other generation - 'seed_name': seedname, + 'seed_name': world.seed_name, 'player_name': world.get_player_names(player), + 'player_id': player, 'client_version': client_version, 'structures': {exit: world.get_entrance(exit, player).connected_region.name for exit in exits} } - filename = f"AP_{seedname}_P{player}_{world.get_player_names(player)}.apmc" + filename = f"AP_{world.seed_name}_P{player}_{world.get_player_names(player)}.apmc" with open(output_path(filename), 'wb') as f: f.write(base64.b64encode(bytes(json.dumps(data), 'utf-8')))