Factorio: add DeathLink option
This commit is contained in:
		
							parent
							
								
									c152790011
								
							
						
					
					
						commit
						4472ef20fe
					
				| 
						 | 
				
			
			@ -5,6 +5,7 @@ import asyncio
 | 
			
		|||
import urllib.parse
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import typing
 | 
			
		||||
 | 
			
		||||
import websockets
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +92,7 @@ class ClientCommandProcessor(CommandProcessor):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class CommonContext():
 | 
			
		||||
    tags:typing.Set[str] = {"AP"}
 | 
			
		||||
    starting_reconnect_delay: int = 5
 | 
			
		||||
    current_reconnect_delay: int = starting_reconnect_delay
 | 
			
		||||
    command_processor: int = ClientCommandProcessor
 | 
			
		||||
| 
						 | 
				
			
			@ -489,7 +491,10 @@ if __name__ == '__main__':
 | 
			
		|||
    # Text Mode to use !hint and such with games that have no text entry
 | 
			
		||||
    init_logging("TextClient")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class TextContext(CommonContext):
 | 
			
		||||
        tags = {"AP", "IgnoreGame"}
 | 
			
		||||
 | 
			
		||||
        async def server_auth(self, password_requested: bool = False):
 | 
			
		||||
            if password_requested and not self.password:
 | 
			
		||||
                await super(TextContext, self).server_auth(password_requested)
 | 
			
		||||
| 
						 | 
				
			
			@ -499,10 +504,11 @@ if __name__ == '__main__':
 | 
			
		|||
 | 
			
		||||
            await self.send_msgs([{"cmd": 'Connect',
 | 
			
		||||
                                   'password': self.password, 'name': self.auth, 'version': Utils.version_tuple,
 | 
			
		||||
                                   'tags': ['AP', 'IgnoreGame'],
 | 
			
		||||
                                   'tags': self.tags,
 | 
			
		||||
                                   'uuid': Utils.get_unique_identifier(), 'game': self.game
 | 
			
		||||
                                   }])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    async def main(args):
 | 
			
		||||
        ctx = TextContext(args.connect, args.password)
 | 
			
		||||
        ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,10 +54,12 @@ class FactorioContext(CommonContext):
 | 
			
		|||
 | 
			
		||||
    def __init__(self, server_address, password):
 | 
			
		||||
        super(FactorioContext, self).__init__(server_address, password)
 | 
			
		||||
        self.send_index = 0
 | 
			
		||||
        self.send_index: int = 0
 | 
			
		||||
        self.rcon_client = None
 | 
			
		||||
        self.awaiting_bridge = False
 | 
			
		||||
        self.write_data_path = None
 | 
			
		||||
        self.last_death_link: float = time.time()  # last send/received death link on AP layer
 | 
			
		||||
        self.death_link_tick: int = 0  # last send death link on Factorio layer
 | 
			
		||||
        self.factorio_json_text_parser = FactorioJSONtoTextParser(self)
 | 
			
		||||
 | 
			
		||||
    async def server_auth(self, password_requested: bool = False):
 | 
			
		||||
| 
						 | 
				
			
			@ -76,13 +78,13 @@ class FactorioContext(CommonContext):
 | 
			
		|||
            'password': self.password,
 | 
			
		||||
            'name': self.auth,
 | 
			
		||||
            'version': Utils.version_tuple,
 | 
			
		||||
            'tags': ['AP'],
 | 
			
		||||
            'tags': self.tags,
 | 
			
		||||
            'uuid': Utils.get_unique_identifier(),
 | 
			
		||||
            'game': "Factorio"
 | 
			
		||||
        }])
 | 
			
		||||
 | 
			
		||||
    def on_print(self, args: dict):
 | 
			
		||||
        logger.info(args["text"])
 | 
			
		||||
        super(FactorioContext, self).on_print(args)
 | 
			
		||||
        if self.rcon_client:
 | 
			
		||||
            self.print_to_game(args['text'])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +109,12 @@ class FactorioContext(CommonContext):
 | 
			
		|||
                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 == "Bounced":
 | 
			
		||||
            if self.rcon_client:
 | 
			
		||||
                tags = args.get("tags", [])
 | 
			
		||||
                if "DeathLink" in tags and self.last_death_link != args["data"]["time"]:
 | 
			
		||||
                    self.rcon_client.send_command(f"/ap-deathlink {args['data']['source']}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def game_watcher(ctx: FactorioContext):
 | 
			
		||||
    bridge_logger = logging.getLogger("FactorioWatcher")
 | 
			
		||||
| 
						 | 
				
			
			@ -139,6 +147,17 @@ async def game_watcher(ctx: FactorioContext):
 | 
			
		|||
                            f"{[lookup_id_to_name[rid] for rid in research_data - ctx.locations_checked]}")
 | 
			
		||||
                        ctx.locations_checked = research_data
 | 
			
		||||
                        await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": tuple(research_data)}])
 | 
			
		||||
                    death_link_tick = data.get("death_link_tick", 0)
 | 
			
		||||
                    if death_link_tick != ctx.death_link_tick:
 | 
			
		||||
                        ctx.death_link_tick = death_link_tick
 | 
			
		||||
                        ctx.last_death_link = time.time()
 | 
			
		||||
                        await ctx.send_msgs([{
 | 
			
		||||
                            "cmd": "Bounce", "tags": ["DeathLink"],
 | 
			
		||||
                            "data": {
 | 
			
		||||
                                "time": ctx.last_death_link,
 | 
			
		||||
                                "source": ctx.player_names[ctx.slot]
 | 
			
		||||
                            }
 | 
			
		||||
                        }])
 | 
			
		||||
            await asyncio.sleep(0.1)
 | 
			
		||||
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
| 
						 | 
				
			
			@ -227,6 +246,10 @@ def get_info(ctx, rcon_client):
 | 
			
		|||
    info = json.loads(rcon_client.send_command("/ap-rcon-info"))
 | 
			
		||||
    ctx.auth = info["slot_name"]
 | 
			
		||||
    ctx.seed_name = info["seed_name"]
 | 
			
		||||
    # 0.2.0 addition, not present earlier
 | 
			
		||||
    death_link = bool(info.get("death_link", False))
 | 
			
		||||
    if death_link:
 | 
			
		||||
        ctx.tags.add("DeathLink")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def factorio_spinup_server(ctx: FactorioContext) -> bool:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -122,7 +122,7 @@ class Context(CommonContext):
 | 
			
		|||
        auth = base64.b64encode(self.rom).decode()
 | 
			
		||||
        await self.send_msgs([{"cmd": 'Connect',
 | 
			
		||||
                              'password': self.password, 'name': auth, 'version': Utils.version_tuple,
 | 
			
		||||
                              'tags': get_tags(self),
 | 
			
		||||
                              'tags': self.tags,
 | 
			
		||||
                              'uuid': Utils.get_unique_identifier(), 'game': "A Link to the Past"
 | 
			
		||||
                              }])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -705,12 +705,6 @@ async def snes_flush_writes(ctx: Context):
 | 
			
		|||
    await snes_write(ctx, writes)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# kept as function for easier wrapping by plugins
 | 
			
		||||
def get_tags(ctx: Context):
 | 
			
		||||
    tags = ['AP']
 | 
			
		||||
    return tags
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def track_locations(ctx: Context, roomid, roomdata):
 | 
			
		||||
    new_locations = []
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1245,7 +1245,6 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
 | 
			
		|||
 | 
			
		||||
            await ctx.send_msgs(client, reply)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    elif cmd == "GetDataPackage":
 | 
			
		||||
        exclusions = set(args.get("exclusions", []))
 | 
			
		||||
        if exclusions:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ def generate_mod(world, output_directory: str):
 | 
			
		|||
    random = multiworld.slot_seeds[player]
 | 
			
		||||
 | 
			
		||||
    def flop_random(low, high, base=None):
 | 
			
		||||
        """Guarentees 50% bwlo base and 50% above base, uniform distribution in each direction."""
 | 
			
		||||
        """Guarentees 50% below base and 50% above base, uniform distribution in each direction."""
 | 
			
		||||
        if base:
 | 
			
		||||
            distance = random.random()
 | 
			
		||||
            if random.randint(0, 1):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
from __future__ import annotations
 | 
			
		||||
import typing
 | 
			
		||||
 | 
			
		||||
from Options import Choice, OptionDict, ItemDict, Option, DefaultOnToggle, Range
 | 
			
		||||
from Options import Choice, OptionDict, ItemDict, Option, DefaultOnToggle, Range, Toggle
 | 
			
		||||
from schema import Schema, Optional, And, Or
 | 
			
		||||
 | 
			
		||||
# schema helpers
 | 
			
		||||
| 
						 | 
				
			
			@ -284,6 +284,11 @@ class ImportedBlueprint(DefaultOnToggle):
 | 
			
		|||
    displayname = "Blueprints"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DeathLink(Toggle):
 | 
			
		||||
    """When you die, everyone dies. Of course the reverse is true too."""
 | 
			
		||||
    displayname = "Death Link"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
factorio_options: typing.Dict[str, type(Option)] = {
 | 
			
		||||
    "max_science_pack": MaxSciencePack,
 | 
			
		||||
    "tech_tree_layout": TechTreeLayout,
 | 
			
		||||
| 
						 | 
				
			
			@ -300,4 +305,5 @@ factorio_options: typing.Dict[str, type(Option)] = {
 | 
			
		|||
    "evolution_traps": EvolutionTrapCount,
 | 
			
		||||
    "attack_traps": AttackTrapCount,
 | 
			
		||||
    "evolution_trap_increase": EvolutionTrapIncrease,
 | 
			
		||||
    "death_link": DeathLink
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,9 @@ SLOT_NAME = "{{ slot_name }}"
 | 
			
		|||
SEED_NAME = "{{ seed_name }}"
 | 
			
		||||
FREE_SAMPLE_BLACKLIST = {{ dict_to_lua(free_sample_blacklist) }}
 | 
			
		||||
TRAP_EVO_FACTOR = {{ evolution_trap_increase }} / 100
 | 
			
		||||
DEATH_LINK = {{ death_link | int }}
 | 
			
		||||
 | 
			
		||||
CURRENTLY_DEATH_LOCK = 0
 | 
			
		||||
 | 
			
		||||
{% if not imported_blueprints -%}
 | 
			
		||||
function set_permissions()
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +60,7 @@ function on_force_created(event)
 | 
			
		|||
    local data = {}
 | 
			
		||||
    data['earned_samples'] = {{ dict_to_lua(starting_items) }}
 | 
			
		||||
    data["victory"] = 0
 | 
			
		||||
    data["death_link_tick"] = 0
 | 
			
		||||
    global.forcedata[event.force] = data
 | 
			
		||||
{%- if silo == 2 %}
 | 
			
		||||
    check_spawn_silo(force)
 | 
			
		||||
| 
						 | 
				
			
			@ -250,6 +254,17 @@ function chain_lookup(table, ...)
 | 
			
		|||
    return table
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function kill_players(force)
 | 
			
		||||
    CURRENTLY_DEATH_LOCK = 1
 | 
			
		||||
    local current_character = nil
 | 
			
		||||
    for _, player in ipairs(force.players) do
 | 
			
		||||
        current_character = player.character
 | 
			
		||||
        if current_character ~= nil then
 | 
			
		||||
            current_character.die()
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    CURRENTLY_DEATH_LOCK = 0
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function spawn_entity(surface, force, name, x, y, radius, randomize, avoid_ores)
 | 
			
		||||
    local prototype = game.entity_prototypes[name]
 | 
			
		||||
| 
						 | 
				
			
			@ -351,6 +366,20 @@ function spawn_entity(surface, force, name, x, y, radius, randomize, avoid_ores)
 | 
			
		|||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if DEATH_LINK == 1 then
 | 
			
		||||
    script.on_event(defines.events.on_entity_died, function(event)
 | 
			
		||||
        if CURRENTLY_DEATH_LOCK == 1 then -- don't re-trigger on same event
 | 
			
		||||
            return
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        local force = event.entity.force
 | 
			
		||||
        global.forcedata[force.name].death_link_tick = game.tick
 | 
			
		||||
        dumpInfo(force)
 | 
			
		||||
        kill_players(force)
 | 
			
		||||
    end, {LuaEntityDiedEventFilter = {["filter"] = "name", ["name"] = "character"}})
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- add / commands
 | 
			
		||||
commands.add_command("ap-sync", "Used by the Archipelago client to get progress information", function(call)
 | 
			
		||||
    local force
 | 
			
		||||
| 
						 | 
				
			
			@ -363,6 +392,7 @@ commands.add_command("ap-sync", "Used by the Archipelago client to get progress
 | 
			
		|||
    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")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for tech_name, tech in pairs(force.technologies) do
 | 
			
		||||
| 
						 | 
				
			
			@ -442,7 +472,7 @@ 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}))
 | 
			
		||||
    rcon.print(game.table_to_json({["slot_name"] = SLOT_NAME, ["seed_name"] = SEED_NAME, ["death_link"] = DEATH_LINK}))
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -453,5 +483,13 @@ end)
 | 
			
		|||
{% endif -%}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
commands.add_command("ap-deathlink", "Kill all players", function(call)
 | 
			
		||||
    local force = game.forces["player"]
 | 
			
		||||
    local source = call.parameter or "Archipelago"
 | 
			
		||||
    kill_players(force)
 | 
			
		||||
    game.print("Death was granted by " .. source)
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- data
 | 
			
		||||
progressive_technologies = {{ dict_to_lua(progressive_technology_table) }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue