WebHost: automatically fill PATCH_TARGET -> HOST_ADDRESS and re-use it for rooms (#1518)
This commit is contained in:
parent
2e76085cf1
commit
7fdf38b2ad
6
Utils.py
6
Utils.py
|
@ -195,11 +195,11 @@ def get_public_ipv4() -> str:
|
||||||
ip = socket.gethostbyname(socket.gethostname())
|
ip = socket.gethostbyname(socket.gethostname())
|
||||||
ctx = get_cert_none_ssl_context()
|
ctx = get_cert_none_ssl_context()
|
||||||
try:
|
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:
|
except Exception as e:
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
try:
|
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:
|
except Exception:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
pass # we could be offline, in a local game, so no point in erroring out
|
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())
|
ip = socket.gethostbyname(socket.gethostname())
|
||||||
ctx = get_cert_none_ssl_context()
|
ctx = get_cert_none_ssl_context()
|
||||||
try:
|
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:
|
except Exception as e:
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
pass # we could be offline, in a local game, or ipv6 may not be available
|
pass # we could be offline, in a local game, or ipv6 may not be available
|
||||||
|
|
|
@ -33,6 +33,11 @@ def get_app():
|
||||||
import yaml
|
import yaml
|
||||||
app.config.from_file(configpath, yaml.safe_load)
|
app.config.from_file(configpath, yaml.safe_load)
|
||||||
logging.info(f"Updated config from {configpath}")
|
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.bind(**app.config["PONY"])
|
||||||
db.generate_mapping(create_tables=True)
|
db.generate_mapping(create_tables=True)
|
||||||
return app
|
return app
|
||||||
|
|
|
@ -51,7 +51,7 @@ app.config["PONY"] = {
|
||||||
app.config["MAX_ROLL"] = 20
|
app.config["MAX_ROLL"] = 20
|
||||||
app.config["CACHE_TYPE"] = "flask_caching.backends.SimpleCache"
|
app.config["CACHE_TYPE"] = "flask_caching.backends.SimpleCache"
|
||||||
app.config["JSON_AS_ASCII"] = False
|
app.config["JSON_AS_ASCII"] = False
|
||||||
app.config["PATCH_TARGET"] = "archipelago.gg"
|
app.config["HOST_ADDRESS"] = ""
|
||||||
|
|
||||||
cache = Cache(app)
|
cache = Cache(app)
|
||||||
Compress(app)
|
Compress(app)
|
||||||
|
|
|
@ -179,6 +179,7 @@ class MultiworldInstance():
|
||||||
self.ponyconfig = config["PONY"]
|
self.ponyconfig = config["PONY"]
|
||||||
self.cert = config["SELFLAUNCHCERT"]
|
self.cert = config["SELFLAUNCHCERT"]
|
||||||
self.key = config["SELFLAUNCHKEY"]
|
self.key = config["SELFLAUNCHKEY"]
|
||||||
|
self.host = config["HOST_ADDRESS"]
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if self.process and self.process.is_alive():
|
if self.process and self.process.is_alive():
|
||||||
|
@ -187,7 +188,7 @@ class MultiworldInstance():
|
||||||
logging.info(f"Spinning up {self.room_id}")
|
logging.info(f"Spinning up {self.room_id}")
|
||||||
process = multiprocessing.Process(group=None, target=run_server_process,
|
process = multiprocessing.Process(group=None, target=run_server_process,
|
||||||
args=(self.room_id, self.ponyconfig, get_static_server_data(),
|
args=(self.room_id, self.ponyconfig, get_static_server_data(),
|
||||||
self.cert, self.key),
|
self.cert, self.key, self.host),
|
||||||
name="MultiHost")
|
name="MultiHost")
|
||||||
process.start()
|
process.start()
|
||||||
# bind after start to prevent thread sync issues with guardian.
|
# bind after start to prevent thread sync issues with guardian.
|
||||||
|
|
|
@ -142,7 +142,8 @@ def get_static_server_data() -> dict:
|
||||||
|
|
||||||
|
|
||||||
def run_server_process(room_id, ponyconfig: dict, 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
|
# establish DB connection for multidata and multisave
|
||||||
db.bind(**ponyconfig)
|
db.bind(**ponyconfig)
|
||||||
db.generate_mapping(check_tables=False)
|
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:
|
for wssocket in ctx.server.ws_server.sockets:
|
||||||
socketname = wssocket.getsockname()
|
socketname = wssocket.getsockname()
|
||||||
if wssocket.family == socket.AF_INET6:
|
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
|
# Prefer IPv4, as most users seem to not have working ipv6 support
|
||||||
if not port:
|
if not port:
|
||||||
port = socketname[1]
|
port = socketname[1]
|
||||||
elif wssocket.family == socket.AF_INET:
|
elif wssocket.family == socket.AF_INET:
|
||||||
logging.info(f'Hosting game at {get_public_ipv4()}:{socketname[1]}')
|
|
||||||
port = socketname[1]
|
port = socketname[1]
|
||||||
if port:
|
if port:
|
||||||
|
logging.info(f'Hosting game at {host}:{port}')
|
||||||
with db_session:
|
with db_session:
|
||||||
room = Room.get(id=ctx.room_id)
|
room = Room.get(id=ctx.room_id)
|
||||||
room.last_port = port
|
room.last_port = port
|
||||||
|
else:
|
||||||
|
logging.exception("Could not determine port. Likely hosting failure.")
|
||||||
with db_session:
|
with db_session:
|
||||||
ctx.auto_shutdown = Room.get(id=room_id).timeout
|
ctx.auto_shutdown = Room.get(id=room_id).timeout
|
||||||
ctx.shutdown_task = asyncio.create_task(auto_shutdown(ctx, []))
|
ctx.shutdown_task = asyncio.create_task(auto_shutdown(ctx, []))
|
||||||
|
|
|
@ -26,7 +26,7 @@ def download_patch(room_id, patch_id):
|
||||||
with zipfile.ZipFile(filelike, "a") as zf:
|
with zipfile.ZipFile(filelike, "a") as zf:
|
||||||
with zf.open("archipelago.json", "r") as f:
|
with zf.open("archipelago.json", "r") as f:
|
||||||
manifest = json.load(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:
|
with zipfile.ZipFile(new_file, "w") as new_zip:
|
||||||
for file in zf.infolist():
|
for file in zf.infolist():
|
||||||
if file.filename == "archipelago.json":
|
if file.filename == "archipelago.json":
|
||||||
|
@ -64,7 +64,7 @@ def download_slot_file(room_id, player_id: int):
|
||||||
if slot_data.game == "Minecraft":
|
if slot_data.game == "Minecraft":
|
||||||
from worlds.minecraft import mc_update_output
|
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"
|
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)
|
return send_file(io.BytesIO(data), as_attachment=True, download_name=fname)
|
||||||
elif slot_data.game == "Factorio":
|
elif slot_data.game == "Factorio":
|
||||||
with zipfile.ZipFile(io.BytesIO(slot_data.data)) as zf:
|
with zipfile.ZipFile(io.BytesIO(slot_data.data)) as zf:
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
The most likely failure reason is that the multiworld is too old to be loaded now.
|
The most likely failure reason is that the multiworld is too old to be loaded now.
|
||||||
{% elif room.last_port %}
|
{% elif room.last_port %}
|
||||||
You can connect to this room by using <span class="interactive"
|
You can connect to this room by using <span class="interactive"
|
||||||
data-tooltip="This means address/ip is {{ config['PATCH_TARGET'] }} and port is {{ room.last_port }}.">
|
data-tooltip="This means address/ip is {{ config['HOST_ADDRESS'] }} and port is {{ room.last_port }}.">
|
||||||
'/connect {{ config['PATCH_TARGET'] }}:{{ room.last_port }}'
|
'/connect {{ config['HOST_ADDRESS'] }}:{{ room.last_port }}'
|
||||||
</span>
|
</span>
|
||||||
in the <a href="{{ url_for("tutorial_landing")}}">client</a>.<br>
|
in the <a href="{{ url_for("tutorial_landing")}}">client</a>.<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
{% for patch in room.seed.slots|list|sort(attribute="player_id") %}
|
{% for patch in room.seed.slots|list|sort(attribute="player_id") %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ patch.player_id }}</td>
|
<td>{{ patch.player_id }}</td>
|
||||||
<td data-tooltip="Connect via TextClient"><a href="archipelago://{{ patch.player_name | e}}:@{{ config['PATCH_TARGET'] }}:{{ room.last_port }}">{{ patch.player_name }}</a></td>
|
<td data-tooltip="Connect via TextClient"><a href="archipelago://{{ patch.player_name | e}}:@{{ config['HOST_ADDRESS'] }}:{{ room.last_port }}">{{ patch.player_name }}</a></td>
|
||||||
<td>{{ patch.game }}</td>
|
<td>{{ patch.game }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if patch.game == "Minecraft" %}
|
{% if patch.game == "Minecraft" %}
|
||||||
|
|
|
@ -48,5 +48,5 @@
|
||||||
# TODO
|
# TODO
|
||||||
#JSON_AS_ASCII: false
|
#JSON_AS_ASCII: false
|
||||||
|
|
||||||
# Patch target. This is the address encoded into the patch that will be used for client auto-connect.
|
# Host Address. This is the address encoded into the patch that will be used for client auto-connect.
|
||||||
#PATCH_TARGET: archipelago.gg
|
#HOST_ADDRESS: archipelago.gg
|
||||||
|
|
Loading…
Reference in New Issue