Factorio: Added ability to chat from within the game. (#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.
This commit is contained in:
recklesscoder 2022-10-28 00:45:26 +02:00 committed by GitHub
parent 924f484be0
commit cfff12d8d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 13 deletions

View File

@ -8,6 +8,7 @@ import re
import subprocess
import time
import random
import typing
import ModuleUpdate
ModuleUpdate.update()
@ -51,6 +52,9 @@ class FactorioCommandProcessor(ClientCommandProcessor):
"""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
@ -71,6 +75,8 @@ class FactorioContext(CommonContext):
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:
@ -87,13 +93,15 @@ class FactorioContext(CommonContext):
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
@ -130,6 +138,27 @@ class FactorioContext(CommonContext):
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:
@ -139,6 +168,15 @@ class FactorioContext(CommonContext):
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
@ -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}])
@ -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):
@ -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:
@ -438,6 +484,9 @@ if __name__ == '__main__':
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.")

View File

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

View File

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

View File

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

View File

@ -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
@ -148,10 +150,20 @@ 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
@ -159,13 +171,6 @@ In case any problems should occur, the Archipelago Client will create a file `Fa
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