From 05fe423ef1980f3d6c6b37f53e88796761b79a99 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Thu, 24 Feb 2022 00:51:31 +0100 Subject: [PATCH] Factorio: implement EnergyLink --- CommonClient.py | 2 +- FactorioClient.py | 44 ++++++++++++-- Utils.py | 2 +- worlds/factorio/__init__.py | 2 +- worlds/factorio/data/mod_template/control.lua | 58 +++++++++++++++++-- worlds/factorio/data/mod_template/data.lua | 21 +++++++ 6 files changed, 118 insertions(+), 11 deletions(-) diff --git a/CommonClient.py b/CommonClient.py index ddebf6fe..41c88650 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -441,7 +441,7 @@ async def process_server_cmd(ctx: CommonContext, args: dict): else: args['players'].sort() current_team = -1 - logger.info('Players:') + logger.info('Connected Players:') for network_player in args['players']: if network_player.team != current_team: logger.info(f' Team #{network_player.team + 1}') diff --git a/FactorioClient.py b/FactorioClient.py index a9659e85..1d8e4152 100644 --- a/FactorioClient.py +++ b/FactorioClient.py @@ -19,7 +19,7 @@ if __name__ == "__main__": Utils.init_logging("FactorioClient", exception_logger="Client") from CommonClient import CommonContext, server_loop, console_loop, ClientCommandProcessor, logger, gui_enabled, \ - get_base_parser + get_base_parser from MultiServer import mark_raw from NetUtils import NetworkItem, ClientStatus, JSONtoTextParser, JSONMessagePart @@ -62,6 +62,8 @@ class FactorioContext(CommonContext): self.write_data_path = None self.death_link_tick: int = 0 # last send death link on Factorio layer self.factorio_json_text_parser = FactorioJSONtoTextParser(self) + self.energy_link_increment = 0 + self.last_deplete = 0 async def server_auth(self, password_requested: bool = False): if password_requested and not self.password: @@ -105,6 +107,15 @@ class FactorioContext(CommonContext): if "checked_locations" in args and args["checked_locations"]: self.rcon_client.send_commands({item_name: f'/ap-get-technology ap-{item_name}-\t-1' for item_name in args["checked_locations"]}) + elif cmd == "SetReply": + if args["key"] == "EnergyLink": + logger.info(f"Got EnergyLink package: {args}") + if self.energy_link_increment and args.get("last_deplete", -1) == self.last_deplete: + # it's our deplete request + gained = args["original_value"] - args["value"] + if gained: + logger.info(f"EnergyLink: Received {gained} Joules") + self.rcon_client.send_command(f"/ap-energylink {int(gained)}") async def game_watcher(ctx: FactorioContext): @@ -113,7 +124,8 @@ async def game_watcher(ctx: FactorioContext): next_bridge = time.perf_counter() + 1 try: while not ctx.exit_event.is_set(): - if ctx.awaiting_bridge and ctx.rcon_client and time.perf_counter() > next_bridge: + # TODO: restore on-demand refresh + if ctx.rcon_client and time.perf_counter() > next_bridge: next_bridge = time.perf_counter() + 1 ctx.awaiting_bridge = False data = json.loads(ctx.rcon_client.send_command("/ap-sync")) @@ -127,8 +139,7 @@ async def game_watcher(ctx: FactorioContext): research_data = data["research_done"] research_data = {int(tech_name.split("-")[1]) for tech_name in research_data} victory = data["victory"] - if "death_link" in data: # TODO: Remove this if statement around version 0.2.4 or so - await ctx.update_death_link(data["death_link"]) + await ctx.update_death_link(data["death_link"]) if not ctx.finished_game and victory: await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) @@ -145,6 +156,29 @@ async def game_watcher(ctx: FactorioContext): ctx.death_link_tick = death_link_tick if "DeathLink" in ctx.tags: await ctx.send_death() + if ctx.energy_link_increment: + in_world_bridges = data["energy_bridges"] + if in_world_bridges: + in_world_energy = data["energy"] + if in_world_energy < (ctx.energy_link_increment * in_world_bridges): + # attempt to refill + ctx.last_deplete = time.time() + asyncio.create_task(ctx.send_msgs([{ + "cmd": "Set", "key": "EnergyLink", "operation": "deplete", + "value": -ctx.energy_link_increment * in_world_bridges, + "last_deplete": ctx.last_deplete + }])) + # Above Capacity - (len(Bridges) * ENERGY_INCREMENT) + elif in_world_energy > (in_world_bridges * ctx.energy_link_increment * 5) - \ + ctx.energy_link_increment*in_world_bridges: + value = ctx.energy_link_increment * in_world_bridges + asyncio.create_task(ctx.send_msgs([{ + "cmd": "Set", "key": "EnergyLink", "operation": "add", + "value": value + }])) + ctx.rcon_client.send_command( + f"/ap-energylink -{value}") + logger.info(f"EnergyLink: Sent {value} Joules") await asyncio.sleep(0.1) @@ -239,6 +273,8 @@ async def get_info(ctx, rcon_client): ctx.seed_name = info["seed_name"] # 0.2.0 addition, not present earlier death_link = bool(info.get("death_link", False)) + ctx.energy_link_increment = info.get("energy_link", 0) + logger.debug(f"Energy Link Increment: {ctx.energy_link_increment}") await ctx.update_death_link(death_link) diff --git a/Utils.py b/Utils.py index 911e4358..b3d5a47f 100644 --- a/Utils.py +++ b/Utils.py @@ -24,7 +24,7 @@ class Version(typing.NamedTuple): build: int -__version__ = "0.2.5" +__version__ = "0.2.6" version_tuple = tuplize_version(__version__) from yaml import load, dump, SafeLoader diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index 4f9382f0..b1e695a2 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -174,7 +174,7 @@ class Factorio(World): return super(Factorio, self).collect_item(state, item, remove) def get_required_client_version(self) -> tuple: - return max((0, 2, 1), super(Factorio, self).get_required_client_version()) + return max((0, 2, 6), super(Factorio, self).get_required_client_version()) options = factorio_options diff --git a/worlds/factorio/data/mod_template/control.lua b/worlds/factorio/data/mod_template/control.lua index 9d93a99d..7d5d870b 100644 --- a/worlds/factorio/data/mod_template/control.lua +++ b/worlds/factorio/data/mod_template/control.lua @@ -11,6 +11,7 @@ TRAP_EVO_FACTOR = {{ evolution_trap_increase }} / 100 MAX_SCIENCE_PACK = {{ max_science_pack }} GOAL = {{ goal }} ARCHIPELAGO_DEATH_LINK_SETTING = "archipelago-death-link-{{ slot_player }}-{{ seed_name }}" +ENERGY_INCREMENT = 1000000 if settings.global[ARCHIPELAGO_DEATH_LINK_SETTING].value then DEATH_LINK = 1 @@ -20,6 +21,40 @@ end CURRENTLY_DEATH_LOCK = 0 +function on_check_energy_link(event) + --- assuming 1 MJ increment and 5MJ battery: + --- first 2 MJ request fill, last 2 MJ push energy, middle 1 MJ does nothing + if event.tick % 60 == 30 then + local surface = game.get_surface(1) + local force = "player" + local bridges = surface.find_entities_filtered({name="ap-energy-bridge", force=force}) + local bridgecount = table_size(bridges) + global.forcedata[force].energy_bridges = bridgecount + if global.forcedata[force].energy == nil then + global.forcedata[force].energy = 0 + end + if global.forcedata[force].energy < ENERGY_INCREMENT * bridgecount * 5 then + for i, bridge in ipairs(bridges) do + if bridge.energy > ENERGY_INCREMENT*3 then + global.forcedata[force].energy = global.forcedata[force].energy + ENERGY_INCREMENT + bridge.energy = bridge.energy - ENERGY_INCREMENT + end + end + end + for i, bridge in ipairs(bridges) do + if global.forcedata[force].energy < ENERGY_INCREMENT then + break + end + if bridge.energy < ENERGY_INCREMENT*2 and global.forcedata[force].energy > ENERGY_INCREMENT then + global.forcedata[force].energy = global.forcedata[force].energy - ENERGY_INCREMENT + bridge.energy = bridge.energy + ENERGY_INCREMENT + end + end + game.print("Bridges: " .. bridgecount .. " With: " .. global.forcedata["player"].energy) + end +end +script.on_event(defines.events.on_tick, on_check_energy_link) + {% if not imported_blueprints -%} function set_permissions() local group = game.permissions.get_group("Default") @@ -69,6 +104,8 @@ function on_force_created(event) data['earned_samples'] = {{ dict_to_lua(starting_items) }} data["victory"] = 0 data["death_link_tick"] = 0 + data["energy"] = 0 + data["energy_bridges"] = 0 global.forcedata[event.force] = data {%- if silo == 2 %} check_spawn_silo(force) @@ -434,11 +471,14 @@ commands.add_command("ap-sync", "Used by the Archipelago client to get progress force = game.players[call.player_index].force end local research_done = {} + local forcedata = chain_lookup(global, "forcedata", force.name) local data_collection = { ["research_done"] = research_done, - ["victory"] = chain_lookup(global, "forcedata", force.name, "victory"), - ["death_link_tick"] = chain_lookup(global, "forcedata", force.name, "death_link_tick"), - ["death_link"] = DEATH_LINK + ["victory"] = chain_lookup(forcedata, "victory"), + ["death_link_tick"] = chain_lookup(forcedata, "death_link_tick"), + ["death_link"] = DEATH_LINK, + ["energy"] = chain_lookup(forcedata, "energy"), + ["energy_bridges"] = chain_lookup(forcedata, "energy_bridges"), } for tech_name, tech in pairs(force.technologies) do @@ -515,7 +555,12 @@ end) commands.add_command("ap-rcon-info", "Used by the Archipelago client to get information", function(call) - rcon.print(game.table_to_json({["slot_name"] = SLOT_NAME, ["seed_name"] = SEED_NAME, ["death_link"] = DEATH_LINK})) + rcon.print(game.table_to_json({ + ["slot_name"] = SLOT_NAME, + ["seed_name"] = SEED_NAME, + ["death_link"] = DEATH_LINK, + ["energy_link"] = ENERGY_INCREMENT + })) end) @@ -533,6 +578,11 @@ commands.add_command("ap-deathlink", "Kill all players", function(call) game.print("Death was granted by " .. source) end) +commands.add_command("ap-energylink", "Used by the Archipelago client to manage Energy Link", function(call) + local change = tonumber(call.parameter or "0") + local force = "player" + global.forcedata[force].energy = global.forcedata[force].energy + change +end) -- data progressive_technologies = {{ dict_to_lua(progressive_technology_table) }} diff --git a/worlds/factorio/data/mod_template/data.lua b/worlds/factorio/data/mod_template/data.lua index ef62f72a..95874a06 100644 --- a/worlds/factorio/data/mod_template/data.lua +++ b/worlds/factorio/data/mod_template/data.lua @@ -1,4 +1,25 @@ {% from "macros.lua" import dict_to_lua %} +local energy_bridge = table.deepcopy(data.raw["accumulator"]["accumulator"]) +energy_bridge.name = "ap-energy-bridge" +energy_bridge.minable.result = "ap-energy-bridge" +energy_bridge.localised_name = "Archipelago EnergyLink Bridge" +data.raw["accumulator"]["ap-energy-bridge"] = energy_bridge + +local energy_bridge_item = table.deepcopy(data.raw["item"]["accumulator"]) +energy_bridge_item.name = "ap-energy-bridge" +energy_bridge_item.localised_name = "Archipelago EnergyLink Bridge" +energy_bridge_item.place_result = energy_bridge.name +data.raw["item"]["ap-energy-bridge"] = energy_bridge_item + +local energy_bridge_recipe = table.deepcopy(data.raw["recipe"]["accumulator"]) +energy_bridge_recipe.name = "ap-energy-bridge" +energy_bridge_recipe.result = energy_bridge_item.name +energy_bridge_recipe.energy_required = 1 +energy_bridge_recipe.ingredients = { {# {type="item", name="iron-plate", amount=1} TODO: randomized recipe #} } +energy_bridge_recipe.enabled = true +energy_bridge_recipe.localised_name = "Archipelago EnergyLink Bridge" +data.raw["recipe"]["ap-energy-bridge"] = energy_bridge_recipe + data.raw["map-gen-presets"].default["archipelago"] = {{ dict_to_lua({"default": False, "order": "a", "basic_settings": world_gen["basic"], "advanced_settings": world_gen["advanced"]}) }} if mods["science-not-invited"] then local weights = {