Add persistent cross-game player bank (Refs #3)

This commit is contained in:
2026-10-12 20:45:41 -04:00
parent eda2c7e76d
commit 9cce0be438
19 changed files with 1646 additions and 15 deletions
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Test native bank storage/acknowledgments with ASan/UBSan, without the VM.
Uses deps/json/single_include/nlohmann/json.hpp if restored, or --json-header
pointing to the single header at the revision in the plugin's upstream.json.
"""
import argparse
import os
from pathlib import Path
import re
import shutil
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--json-header", help="Header path; {game} expands to t5/t6 for their respective pinned headers")
args = parser.parse_args()
with tempfile.TemporaryDirectory(prefix="tws-bank-") as temp:
directory = Path(temp)
for game in ("t5", "t6"):
plugin = ROOT / game / "plugins" / f"tws-{game}-gsc-utils"
header = Path(args.json_header.format(game=game)) if args.json_header else plugin / "deps/json/single_include/nlohmann/json.hpp"
if not header.is_file():
parser.error(f"Missing {header}; restore dependencies or pass --json-header")
shutil.copyfile(header, directory / "json.hpp")
(directory / "stdinc.hpp").write_text("#include <algorithm>\n#include <fstream>\n#include <sstream>\n#include <json.hpp>\n")
executable = directory / game
subprocess.run([os.environ.get("CXX", "c++"), "-std=c++20", "-Wall", "-Wextra", "-Werror",
"-fsanitize=address,undefined", "-I", str(directory), "-I", str(plugin / "src/utils"),
str(plugin / "src/utils/bank.cpp"), str(ROOT / "tools/tests/bank_cases.cpp"),
"-o", str(executable)], check=True)
work = directory / f"{game}-data"
subprocess.run([str(executable), str(work)], check=True)
# Separate OS processes prove snapshots/identities don't depend on VM or
# DLL memory. A newly booted producer recovers before its own start.
subprocess.run([str(executable), str(work), "produce"], check=True)
subprocess.run([str(executable), str(work), "recover"], check=True)
subprocess.run([str(executable), str(work), "empty"], check=True)
# Compile the actual registration adapter as well, substituting only
# engine interfaces. This does not validate the Windows ABI/VM linker.
stub = (ROOT / "tools/tests/http_component_stub.hpp").read_text()
stub = stub.replace("std::vector<script_value> get_keys() const", "void emplace_back(const script_value& value) { (*data)[std::to_string(data->size())] = value; }\n std::vector<script_value> get_keys() const")
(directory / "component_stub.hpp").write_text(stub)
component = re.sub(r'^#include[^\n]*\n', '', (plugin / "src/component/bank.cpp").read_text(), flags=re.MULTILINE)
source = directory / "component.cpp"
source.write_text('#include "component_stub.hpp"\n#include "bank.hpp"\n' + component)
subprocess.run([os.environ.get("CXX", "c++"), "-std=c++20", "-Wall", "-Wextra", "-Werror", "-fsyntax-only",
"-I", str(directory), "-I", str(plugin / "src/utils"), str(source)], check=True)
print(f"PASS {game}: native storage, acknowledgment, process restart and component checks", flush=True)