Skip to content

Commit

Permalink
Factorio: Added ability to chat from within the game. (ArchipelagoMW#…
Browse files Browse the repository at this point in the history
…1068)

* Factorio: Added ability to chat from within the game.
This also allows using commands such as !hint from within the game.

* Factorio: Only prepend player names to chat in multiplayer.

* Factorio: Mirror chat sent from the FactorioClient UI to the Factorio server.

* Factorio: Remove local coordinates from outgoing chat.

* Factorio: Added setting to disable bridging chat out.
Added client command to toggle this setting at run-time.

* Factorio: Added in-game command to toggle chat bridging setting at run-time.

* .

* Factorio: Document toggle for chat bridging feature.

* (Removed superfluous type annotations.)

* (Removed hard to read regex.)

* Docs/Factorio: Fix display of multiline code snippets.
  • Loading branch information
recklesscoder authored Oct 27, 2022
1 parent 924f484 commit cfff12d
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 13 deletions.
53 changes: 51 additions & 2 deletions FactorioClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import subprocess
import time
import random
import typing

import ModuleUpdate
ModuleUpdate.update()
Expand Down Expand Up @@ -51,6 +52,9 @@ def _cmd_toggle_send_filter(self):
"""Toggle filtering of item sends that get displayed in-game to only those that involve you."""
self.ctx.toggle_filter_item_sends()

def _cmd_toggle_chat(self):
"""Toggle sending of chat messages from players on the Factorio server to Archipelago."""
self.ctx.toggle_bridge_chat_out()

class FactorioContext(CommonContext):
command_processor = FactorioCommandProcessor
Expand All @@ -71,6 +75,8 @@ def __init__(self, server_address, password):
self.energy_link_increment = 0
self.last_deplete = 0
self.filter_item_sends: bool = False
self.multiplayer: bool = False # whether multiple different players have connected
self.bridge_chat_out: bool = True

async def server_auth(self, password_requested: bool = False):
if password_requested and not self.password:
Expand All @@ -87,13 +93,15 @@ async def server_auth(self, password_requested: bool = False):
def on_print(self, args: dict):
super(FactorioContext, self).on_print(args)
if self.rcon_client:
self.print_to_game(args['text'])
if not args['text'].startswith(self.player_names[self.slot] + ":"):
self.print_to_game(args['text'])

def on_print_json(self, args: dict):
if self.rcon_client:
if not self.filter_item_sends or not self.is_uninteresting_item_send(args):
text = self.factorio_json_text_parser(copy.deepcopy(args["data"]))
self.print_to_game(text)
if not text.startswith(self.player_names[self.slot] + ":"):
self.print_to_game(text)
super(FactorioContext, self).on_print_json(args)

@property
Expand Down Expand Up @@ -130,6 +138,27 @@ def on_package(self, cmd: str, args: dict):
f"{Utils.format_SI_prefix(args['value'])}J remaining.")
self.rcon_client.send_command(f"/ap-energylink {gained}")

def on_user_say(self, text: str) -> typing.Optional[str]:
# Mirror chat sent from the UI to the Factorio server.
self.print_to_game(f"{self.player_names[self.slot]}: {text}")
return text

async def chat_from_factorio(self, user: str, message: str) -> None:
if not self.bridge_chat_out:
return

# Pass through commands
if message.startswith("!"):
await self.send_msgs([{"cmd": "Say", "text": message}])
return

# Omit messages that contain local coordinates
if "[gps=" in message:
return

prefix = f"({user}) " if self.multiplayer else ""
await self.send_msgs([{"cmd": "Say", "text": f"{prefix}{message}"}])

def toggle_filter_item_sends(self) -> None:
self.filter_item_sends = not self.filter_item_sends
if self.filter_item_sends:
Expand All @@ -139,6 +168,15 @@ def toggle_filter_item_sends(self) -> None:
logger.info(announcement)
self.print_to_game(announcement)

def toggle_bridge_chat_out(self) -> None:
self.bridge_chat_out = not self.bridge_chat_out
if self.bridge_chat_out:
announcement = "Chat is now bridged to Archipelago."
else:
announcement = "Chat is no longer bridged to Archipelago."
logger.info(announcement)
self.print_to_game(announcement)

def run_gui(self):
from kvui import GameManager

Expand Down Expand Up @@ -178,6 +216,7 @@ async def game_watcher(ctx: FactorioContext):
research_data = {int(tech_name.split("-")[1]) for tech_name in research_data}
victory = data["victory"]
await ctx.update_death_link(data["death_link"])
ctx.multiplayer = data.get("multiplayer", False)

if not ctx.finished_game and victory:
await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
Expand Down Expand Up @@ -281,8 +320,14 @@ async def factorio_server_watcher(ctx: FactorioContext):
elif re.match(r"^[0-9.]+ Script @[^ ]+\.lua:\d+: Player command toggle-ap-send-filter", msg):
factorio_server_logger.debug(msg)
ctx.toggle_filter_item_sends()
elif re.match(r"^[0-9.]+ Script @[^ ]+\.lua:\d+: Player command toggle-ap-chat$", msg):
factorio_server_logger.debug(msg)
ctx.toggle_bridge_chat_out()
else:
factorio_server_logger.info(msg)
match = re.match(r"^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d \[CHAT\] ([^:]+): (.*)$", msg)
if match:
await ctx.chat_from_factorio(match.group(1), match.group(2))
if ctx.rcon_client:
commands = {}
while ctx.send_index < len(ctx.items_received):
Expand Down Expand Up @@ -383,6 +428,7 @@ async def factorio_spinup_server(ctx: FactorioContext) -> bool:
async def main(args):
ctx = FactorioContext(args.connect, args.password)
ctx.filter_item_sends = initial_filter_item_sends
ctx.bridge_chat_out = initial_bridge_chat_out
ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop")

if gui_enabled:
Expand Down Expand Up @@ -438,6 +484,9 @@ def _handle_color(self, node: JSONMessagePart):
if not isinstance(options["factorio_options"]["filter_item_sends"], bool):
logging.warning(f"Warning: Option filter_item_sends should be a bool.")
initial_filter_item_sends = bool(options["factorio_options"]["filter_item_sends"])
if not isinstance(options["factorio_options"]["bridge_chat_out"], bool):
logging.warning(f"Warning: Option bridge_chat_out should be a bool.")
initial_bridge_chat_out = bool(options["factorio_options"]["bridge_chat_out"])

if not os.path.exists(os.path.dirname(executable)):
raise FileNotFoundError(f"Path {os.path.dirname(executable)} does not exist or could not be accessed.")
Expand Down
1 change: 1 addition & 0 deletions Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def get_default_options() -> OptionsType:
"factorio_options": {
"executable": os.path.join("factorio", "bin", "x64", "factorio"),
"filter_item_sends": False,
"bridge_chat_out": True,
},
"sni_options": {
"sni": "SNI",
Expand Down
2 changes: 2 additions & 0 deletions host.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ factorio_options:
# server_settings: "factorio\\data\\server-settings.json"
# Whether to filter item send messages displayed in-game to only those that involve you.
filter_item_sends: false
# Whether to send chat messages from players on the Factorio server to Archipelago.
bridge_chat_out: true
minecraft_options:
forge_directory: "Minecraft Forge server"
max_heap_size: "2G"
Expand Down
6 changes: 6 additions & 0 deletions worlds/factorio/data/mod_template/control.lua
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ function on_player_created(event)
{%- if silo == 2 %}
check_spawn_silo(game.players[event.player_index].force)
{%- endif %}
dumpInfo(player.force)
end
script.on_event(defines.events.on_player_created, on_player_created)

Expand Down Expand Up @@ -491,6 +492,7 @@ commands.add_command("ap-sync", "Used by the Archipelago client to get progress
["death_link"] = DEATH_LINK,
["energy"] = chain_lookup(forcedata, "energy"),
["energy_bridges"] = chain_lookup(forcedata, "energy_bridges"),
["multiplayer"] = #game.players > 1,
}

for tech_name, tech in pairs(force.technologies) do
Expand Down Expand Up @@ -600,5 +602,9 @@ commands.add_command("toggle-ap-send-filter", "Toggle filtering of item sends th
log("Player command toggle-ap-send-filter") -- notifies client
end)

commands.add_command("toggle-ap-chat", "Toggle sending of chat messages from players on the Factorio server to Archipelago.", function(call)
log("Player command toggle-ap-chat") -- notifies client
end)

-- data
progressive_technologies = {{ dict_to_lua(progressive_technology_table) }}
27 changes: 16 additions & 11 deletions worlds/factorio/docs/setup_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ This allows you to host your own Factorio game.

For additional client features, issue the `/help` command in the Archipelago Client. Once connected to the AP server,
you can also issue the `!help` command to learn about additional commands like `!hint`.
For more information about the commands you can use, see the [Commands Guide](/tutorial/Archipelago/commands/en) and
[Other Settings](#other-settings).

## Allowing Other People to Join Your Game

Expand All @@ -148,24 +150,27 @@ you can also issue the `!help` command to learn about additional commands like `
- Type `/toggle-ap-send-filter` in-game
- Type `/toggle_send_filter` in the Archipelago Client
- In your `host.yaml` set
```
factorio_options:
filter_item_sends: true
```
```
factorio_options:
filter_item_sends: true
```
- By default, in-game chat is bridged to Archipelago. If you prefer to be able to speak privately, you can disable this
feature by doing one of the following:
- Type `/toggle-ap-chat` in-game
- Type `/toggle_chat` in the Archipelago Client
- In your `host.yaml` set
```
factorio_options:
bridge_chat_out: false
```
Note that this will also disable `!` commands from within the game, and that it will not affect incoming chat.

## Troubleshooting

In case any problems should occur, the Archipelago Client will create a file `FactorioClient.txt` in the `/logs`. The
contents of this file may help you troubleshoot an issue on your own and is vital for requesting help from other people
in Archipelago.

## Commands in game

Once you have connected to the server successfully using the Archipelago Factorio Client you should see a message
stating you can get help using Archipelago commands by typing `!help`. Commands cannot currently be sent from within
the Factorio session, but you can send them from the Archipelago Factorio Client. For more information about the commands
you can use see the [commands guide](/tutorial/Archipelago/commands/en).

## Additional Resources

- Alternate Tutorial by
Expand Down

0 comments on commit cfff12d

Please sign in to comment.