From 7fdf38b2ad76879a79a61242748f329b5e8bc7e5 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Thu, 9 Mar 2023 21:31:00 +0100 Subject: [PATCH] WebHost: automatically fill PATCH_TARGET -> HOST_ADDRESS and re-use it for rooms (#1518) --- Utils.py | 6 +++--- WebHost.py | 5 +++++ WebHostLib/__init__.py | 2 +- WebHostLib/autolauncher.py | 3 ++- WebHostLib/customserver.py | 8 +++++--- WebHostLib/downloads.py | 4 ++-- WebHostLib/templates/hostRoom.html | 4 ++-- WebHostLib/templates/macros.html | 2 +- docs/webhost configuration sample.yaml | 4 ++-- 9 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Utils.py b/Utils.py index 3ec3d4ff..059168c8 100644 --- a/Utils.py +++ b/Utils.py @@ -195,11 +195,11 @@ def get_public_ipv4() -> str: ip = socket.gethostbyname(socket.gethostname()) ctx = get_cert_none_ssl_context() try: - ip = urllib.request.urlopen("https://checkip.amazonaws.com/", context=ctx).read().decode("utf8").strip() + ip = urllib.request.urlopen("https://checkip.amazonaws.com/", context=ctx, timeout=10).read().decode("utf8").strip() except Exception as e: # noinspection PyBroadException try: - ip = urllib.request.urlopen("https://v4.ident.me", context=ctx).read().decode("utf8").strip() + ip = urllib.request.urlopen("https://v4.ident.me", context=ctx, timeout=10).read().decode("utf8").strip() except Exception: logging.exception(e) pass # we could be offline, in a local game, so no point in erroring out @@ -213,7 +213,7 @@ def get_public_ipv6() -> str: ip = socket.gethostbyname(socket.gethostname()) ctx = get_cert_none_ssl_context() try: - ip = urllib.request.urlopen("https://v6.ident.me", context=ctx).read().decode("utf8").strip() + ip = urllib.request.urlopen("https://v6.ident.me", context=ctx, timeout=10).read().decode("utf8").strip() except Exception as e: logging.exception(e) pass # we could be offline, in a local game, or ipv6 may not be available diff --git a/WebHost.py b/WebHost.py index d098f6e7..40d366a0 100644 --- a/WebHost.py +++ b/WebHost.py @@ -33,6 +33,11 @@ def get_app(): import yaml app.config.from_file(configpath, yaml.safe_load) logging.info(f"Updated config from {configpath}") + if not app.config["HOST_ADDRESS"]: + logging.info("Getting public IP, as HOST_ADDRESS is empty.") + app.config["HOST_ADDRESS"] = Utils.get_public_ipv4() + logging.info(f"HOST_ADDRESS was set to {app.config['HOST_ADDRESS']}") + db.bind(**app.config["PONY"]) db.generate_mapping(create_tables=True) return app diff --git a/WebHostLib/__init__.py b/WebHostLib/__init__.py index e8e5b59d..8bd3609c 100644 --- a/WebHostLib/__init__.py +++ b/WebHostLib/__init__.py @@ -51,7 +51,7 @@ app.config["PONY"] = { app.config["MAX_ROLL"] = 20 app.config["CACHE_TYPE"] = "flask_caching.backends.SimpleCache" app.config["JSON_AS_ASCII"] = False -app.config["PATCH_TARGET"] = "archipelago.gg" +app.config["HOST_ADDRESS"] = "" cache = Cache(app) Compress(app) diff --git a/WebHostLib/autolauncher.py b/WebHostLib/autolauncher.py index 4cf72433..484755b3 100644 --- a/WebHostLib/autolauncher.py +++ b/WebHostLib/autolauncher.py @@ -179,6 +179,7 @@ class MultiworldInstance(): self.ponyconfig = config["PONY"] self.cert = config["SELFLAUNCHCERT"] self.key = config["SELFLAUNCHKEY"] + self.host = config["HOST_ADDRESS"] def start(self): if self.process and self.process.is_alive(): @@ -187,7 +188,7 @@ class MultiworldInstance(): logging.info(f"Spinning up {self.room_id}") process = multiprocessing.Process(group=None, target=run_server_process, args=(self.room_id, self.ponyconfig, get_static_server_data(), - self.cert, self.key), + self.cert, self.key, self.host), name="MultiHost") process.start() # bind after start to prevent thread sync issues with guardian. diff --git a/WebHostLib/customserver.py b/WebHostLib/customserver.py index 1c35d778..584ca9fe 100644 --- a/WebHostLib/customserver.py +++ b/WebHostLib/customserver.py @@ -142,7 +142,8 @@ def get_static_server_data() -> dict: def run_server_process(room_id, ponyconfig: dict, static_server_data: dict, - cert_file: typing.Optional[str], cert_key_file: typing.Optional[str]): + cert_file: typing.Optional[str], cert_key_file: typing.Optional[str], + host: str): # establish DB connection for multidata and multisave db.bind(**ponyconfig) db.generate_mapping(check_tables=False) @@ -167,17 +168,18 @@ def run_server_process(room_id, ponyconfig: dict, static_server_data: dict, for wssocket in ctx.server.ws_server.sockets: socketname = wssocket.getsockname() if wssocket.family == socket.AF_INET6: - logging.info(f'Hosting game at [{get_public_ipv6()}]:{socketname[1]}') # Prefer IPv4, as most users seem to not have working ipv6 support if not port: port = socketname[1] elif wssocket.family == socket.AF_INET: - logging.info(f'Hosting game at {get_public_ipv4()}:{socketname[1]}') port = socketname[1] if port: + logging.info(f'Hosting game at {host}:{port}') with db_session: room = Room.get(id=ctx.room_id) room.last_port = port + else: + logging.exception("Could not determine port. Likely hosting failure.") with db_session: ctx.auto_shutdown = Room.get(id=room_id).timeout ctx.shutdown_task = asyncio.create_task(auto_shutdown(ctx, [])) diff --git a/WebHostLib/downloads.py b/WebHostLib/downloads.py index d9600d2d..02ea7320 100644 --- a/WebHostLib/downloads.py +++ b/WebHostLib/downloads.py @@ -26,7 +26,7 @@ def download_patch(room_id, patch_id): with zipfile.ZipFile(filelike, "a") as zf: with zf.open("archipelago.json", "r") as f: manifest = json.load(f) - manifest["server"] = f"{app.config['PATCH_TARGET']}:{last_port}" if last_port else None + manifest["server"] = f"{app.config['HOST_ADDRESS']}:{last_port}" if last_port else None with zipfile.ZipFile(new_file, "w") as new_zip: for file in zf.infolist(): if file.filename == "archipelago.json": @@ -64,7 +64,7 @@ def download_slot_file(room_id, player_id: int): if slot_data.game == "Minecraft": from worlds.minecraft import mc_update_output fname = f"AP_{app.jinja_env.filters['suuid'](room_id)}_P{slot_data.player_id}_{slot_data.player_name}.apmc" - data = mc_update_output(slot_data.data, server=app.config['PATCH_TARGET'], port=room.last_port) + data = mc_update_output(slot_data.data, server=app.config['HOST_ADDRESS'], port=room.last_port) return send_file(io.BytesIO(data), as_attachment=True, download_name=fname) elif slot_data.game == "Factorio": with zipfile.ZipFile(io.BytesIO(slot_data.data)) as zf: diff --git a/WebHostLib/templates/hostRoom.html b/WebHostLib/templates/hostRoom.html index 0a4ca70e..6f02dc09 100644 --- a/WebHostLib/templates/hostRoom.html +++ b/WebHostLib/templates/hostRoom.html @@ -25,8 +25,8 @@ The most likely failure reason is that the multiworld is too old to be loaded now. {% elif room.last_port %} You can connect to this room by using - '/connect {{ config['PATCH_TARGET'] }}:{{ room.last_port }}' + data-tooltip="This means address/ip is {{ config['HOST_ADDRESS'] }} and port is {{ room.last_port }}."> + '/connect {{ config['HOST_ADDRESS'] }}:{{ room.last_port }}' in the client.
{% endif %} diff --git a/WebHostLib/templates/macros.html b/WebHostLib/templates/macros.html index 927ec10f..11e333f0 100644 --- a/WebHostLib/templates/macros.html +++ b/WebHostLib/templates/macros.html @@ -22,7 +22,7 @@ {% for patch in room.seed.slots|list|sort(attribute="player_id") %} {{ patch.player_id }} - {{ patch.player_name }} + {{ patch.player_name }} {{ patch.game }} {% if patch.game == "Minecraft" %} diff --git a/docs/webhost configuration sample.yaml b/docs/webhost configuration sample.yaml index f007805b..70050b05 100644 --- a/docs/webhost configuration sample.yaml +++ b/docs/webhost configuration sample.yaml @@ -48,5 +48,5 @@ # TODO #JSON_AS_ASCII: false -# Patch target. This is the address encoded into the patch that will be used for client auto-connect. -#PATCH_TARGET: archipelago.gg \ No newline at end of file +# Host Address. This is the address encoded into the patch that will be used for client auto-connect. +#HOST_ADDRESS: archipelago.gg