LuneSession hält den autoritativen Matchzustand; Clients senden nur Absichten. Fremde Sitze, veraltete Befehle und Doppelkäufe sind geprüft, Disconnect führt zur Bot-Übernahme statt zum Kernverlust. Exporte und entpackbare Demos 0.16.0-m4 für Linux und Windows.
112 lines
4.1 KiB
Python
112 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
||
"""Build unpackable Champions of Luneraria demos for Linux and Windows.
|
||
|
||
Run: python3 tools/luneraria/build_luneraria_demo.py [--check-only]
|
||
Output: ../dist/luneraria/ChampionsOfLuneraria-Linux-x64-<ver>.zip
|
||
../dist/luneraria/ChampionsOfLuneraria-Windows-x64-<ver>.zip
|
||
Old Saumwacht releases under ../dist/Saumwacht-*.zip stay untouched.
|
||
"""
|
||
from pathlib import Path
|
||
import argparse, zipfile, subprocess, shutil, sys
|
||
|
||
PROJECT = Path(__file__).resolve().parents[2]
|
||
DIST = PROJECT.parent / "dist" / "luneraria"
|
||
VERSION = "0.16.0-m4"
|
||
INTRO = f"""CHAMPIONS OF LUNERARIA · PROTOTYP {VERSION}
|
||
|
||
START
|
||
Linux: ./ChampionsOfLuneraria.x86_64
|
||
Windows: ChampionsOfLuneraria.exe
|
||
Vulkan-Probleme: --rendering-driver opengl3
|
||
|
||
Godot und Blender werden zum Spielen nicht benötigt. Das ganze ZIP zuerst entpacken.
|
||
|
||
INHALT
|
||
Drei Lanes, zwei Teams à drei Rollen, Partieende nur durch einen zerstörten Ancient.
|
||
Planung → automatischer Kampf → Abrechnung. Helden, Items, Angriffschampions und
|
||
eine Bossgrotte mit entsandtem Boss.
|
||
|
||
NETZWERK (Direktverbindung, experimentell)
|
||
Im Setup: Hosten oder Beitreten mit Adresse und Port (Standard 24571).
|
||
Der Host ist autoritativ. Freie Plätze werden von Bots gespielt.
|
||
Ein Disconnect übernimmt ein Bot; er ist kein Kernverlust. Kein Matchmaking,
|
||
keine Hostmigration. Getestet: Protokollchecks und ein lokaler Zwei-Prozess-
|
||
Loopback – keine Internetpartie auf sechs Rechnern.
|
||
|
||
PLATTFORM
|
||
Geprüft: Fedora 44, Forward+ (Quellprojekt). Windows-Export vorhanden;
|
||
ein nativer Windows-11-Test steht aus (keine Hardware in dieser Umgebung).
|
||
|
||
QUELLEN
|
||
https://github.com/ (lokal) · Doku im Quellrepo unter docs/luneraria/
|
||
"""
|
||
|
||
PRESETS = [
|
||
("Luneraria-Linux", "linux", "ChampionsOfLuneraria.x86_64",
|
||
"ChampionsOfLuneraria-Linux-x64"),
|
||
("Luneraria-Windows", "windows", "ChampionsOfLuneraria.exe",
|
||
"ChampionsOfLuneraria-Windows-x64"),
|
||
]
|
||
|
||
|
||
def build(preset: str, folder: str, binary: str) -> Path | None:
|
||
out_dir = DIST / folder
|
||
out_dir.mkdir(parents=True, exist_ok=True)
|
||
cmd = ["godot", "--headless", "--path", str(PROJECT), "--export-release", preset,
|
||
str(out_dir / binary)]
|
||
print("+", " ".join(cmd))
|
||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||
if result.returncode != 0:
|
||
print(result.stdout[-2000:])
|
||
print(result.stderr[-2000:], file=sys.stderr)
|
||
return None
|
||
return out_dir / binary
|
||
|
||
|
||
def pack(folder: str, binary: str, zip_stem: str) -> Path:
|
||
out_dir = DIST / folder
|
||
zip_path = DIST / f"{zip_stem}-{VERSION}.zip"
|
||
readme = out_dir / "LIESMICH.txt"
|
||
readme.write_text(INTRO, encoding="utf-8")
|
||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||
for path in sorted(out_dir.iterdir()):
|
||
if path.suffix == ".zip":
|
||
continue
|
||
zf.write(path, path.name)
|
||
print("PACK", zip_path)
|
||
return zip_path
|
||
|
||
|
||
def main() -> int:
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument("--check-only", action="store_true")
|
||
args = parser.parse_args()
|
||
DIST.mkdir(parents=True, exist_ok=True)
|
||
# Fail early on missing templates / manifests instead of shipping a broken zip.
|
||
required = [
|
||
PROJECT / "assets-source/phase6/export-templates/linux_release.x86_64",
|
||
PROJECT / "assets-source/phase6/export-templates/windows_release_x86_64.exe",
|
||
PROJECT / "assets/luneraria/peoples_manifest.json",
|
||
PROJECT / "assets/luneraria/map_props_manifest.json",
|
||
PROJECT / "scenes/luneraria.tscn",
|
||
]
|
||
missing = [str(p) for p in required if not p.exists()]
|
||
if missing:
|
||
print("MISSING", missing)
|
||
return 1
|
||
if args.check_only:
|
||
print("CHECK_OK", VERSION)
|
||
return 0
|
||
ok = True
|
||
for preset, folder, binary, stem in PRESETS:
|
||
artifact = build(preset, folder, binary)
|
||
if artifact is None or not artifact.exists():
|
||
print("EXPORT_FAIL", preset)
|
||
ok = False
|
||
continue
|
||
pack(folder, binary, stem)
|
||
return 0 if ok else 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|