From 8a3a3e973f55158735db21cfe9180af01eef2042 Mon Sep 17 00:00:00 2001 From: "martin.fencl" Date: Mon, 19 Oct 2026 14:38:14 +0200 Subject: [PATCH] feat: add scripts for staging ValheimWebMap plugin DLL and setting WebMap configuration --- scripts/set_webmap_config.py | 62 ++++++++++++++++++++++++++++++++++++ scripts/stage_webmap.py | 21 ++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 scripts/set_webmap_config.py create mode 100644 scripts/stage_webmap.py diff --git a/scripts/set_webmap_config.py b/scripts/set_webmap_config.py new file mode 100644 index 0000000..ed76855 --- /dev/null +++ b/scripts/set_webmap_config.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Conservatively set BepInEx ValheimWebMap settings without removing other keys.""" +import os +import re +import sys +from pathlib import Path + +path = Path(sys.argv[1]) +port = sys.argv[2] +if not port.isdigit() or not 1 <= int(port) <= 65535: + raise SystemExit("Invalid port") +path.parent.mkdir(parents=True, exist_ok=True) +original = path.read_text(encoding="utf-8") if path.exists() else "" +if "\x00" in original: + raise SystemExit("Unexpected NUL in config") +lines = original.splitlines(keepends=True) +sections = {} +section = None +for ix, line in enumerate(lines): + m = re.match(r"^\s*\[([^\]]+)\]\s*$", line.strip()) + if m: + section = m.group(1).casefold() + if section in sections: + raise SystemExit("Duplicate section; refusing to edit") + sections[section] = ix + +settings = {"web": {"Port": port, "Host": "*"}, + "history": {"TrackSessions": "false", "TrackDeaths": "false"}} +for name, options in settings.items(): + if name not in sections: + if lines and not lines[-1].endswith("\n"): + lines[-1] += "\n" + lines.extend([f"\n[{name.capitalize()}]\n"] + [f"{key} = {value}\n" for key, value in options.items()]) + continue + start = sections[name] + end = next((ix for ix in sorted(sections.values()) if ix > start), len(lines)) + region = lines[start + 1:end] + for key, value in options.items(): + matches = [ix for ix, line in enumerate(region) if re.match(rf"^\s*{re.escape(key)}\s*=", line, flags=re.I)] + if len(matches) > 1: + raise SystemExit(f"Duplicate setting {name}.{key}; refusing to edit") + if matches: + region[matches[0]] = f"{key} = {value}\n" + else: + region.append(f"{key} = {value}\n") + lines[start + 1:end] = region + # positions after this section have moved; recalculate before next section + sections = {} + for ix, line in enumerate(lines): + m = re.match(r"^\s*\[([^\]]+)\]\s*$", line.strip()) + if m: + sections[m.group(1).casefold()] = ix +result = "".join(lines) +if result != original: + tmp = path.with_name(path.name + ".ansible-tmp") + with tmp.open("w", encoding="utf-8") as f: + f.write(result) + f.flush() + os.fsync(f.fileno()) + os.chmod(tmp, 0o640) + os.replace(tmp, path) +print("changed" if result != original else "unchanged") diff --git a/scripts/stage_webmap.py b/scripts/stage_webmap.py new file mode 100644 index 0000000..6b61062 --- /dev/null +++ b/scripts/stage_webmap.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +"""Inspect pinned-version Thunderstore ZIP, stage just the plugin DLL, no traversal.""" +import hashlib +import sys +import zipfile +from pathlib import Path + +archive, output = Path(sys.argv[1]), Path(sys.argv[2]) +with zipfile.ZipFile(archive) as z: + names = [name for name in z.namelist() if name.replace('\\', '/').endswith('plugins/ValheimWebMap/ValheimWebMap.dll')] + if len(names) != 1: + raise SystemExit(f"Expected exactly one plugins/ValheimWebMap/ValheimWebMap.dll; found {len(names)}") + info = z.getinfo(names[0]) + if info.file_size <= 0 or info.file_size > 10 * 1024 * 1024: + raise SystemExit("Unexpected DLL size") + data = z.read(info) + if not data.startswith(b'MZ'): + raise SystemExit("Invalid DLL/PE header") +output.parent.mkdir(parents=True, exist_ok=True) +output.write_bytes(data) +print(hashlib.sha256(data).hexdigest())