allow Factorio Client to recognize if it's trying to connect to the wrong multiworld.

This commit is contained in:
Fabian Dill 2021-05-16 00:21:00 +02:00
parent 8e27ad3547
commit 3e1941a561
8 changed files with 56 additions and 45 deletions

View File

@ -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

View File

@ -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")

View File

@ -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}])

10
Main.py
View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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')))