141 lines
4.6 KiB
Python
Executable File
141 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
RUNTIME_ROOT = Path.home() / ".timmy" / "evennia"
|
|
GAME_DIR = RUNTIME_ROOT / "timmy_world"
|
|
VENV_DIR = RUNTIME_ROOT / "venv"
|
|
EVENNIA_BIN = VENV_DIR / "bin" / "evennia"
|
|
PYTHON_BIN = VENV_DIR / "bin" / "python3"
|
|
OPERATOR_USER = os.environ.get("TIMMY_EVENNIA_OPERATOR", "Alexander")
|
|
OPERATOR_EMAIL = os.environ.get("TIMMY_EVENNIA_OPERATOR_EMAIL", "alexpaynex@gmail.com")
|
|
OPERATOR_PASSWORD = os.environ.get("TIMMY_EVENNIA_OPERATOR_PASSWORD", "timmy-local-dev")
|
|
TIMMY_PASSWORD = os.environ.get("TIMMY_EVENNIA_TIMMY_PASSWORD", "timmy-world-dev")
|
|
|
|
|
|
def ensure_venv():
|
|
if not PYTHON_BIN.exists():
|
|
subprocess.run([sys.executable, "-m", "venv", str(VENV_DIR)], check=True)
|
|
subprocess.run([str(PYTHON_BIN), "-m", "pip", "install", "--upgrade", "pip"], check=True)
|
|
subprocess.run([str(PYTHON_BIN), "-m", "pip", "install", "evennia"], check=True)
|
|
|
|
|
|
def ensure_game_dir():
|
|
if not GAME_DIR.exists():
|
|
RUNTIME_ROOT.mkdir(parents=True, exist_ok=True)
|
|
subprocess.run([str(EVENNIA_BIN), "--init", "timmy_world"], cwd=RUNTIME_ROOT, check=True)
|
|
|
|
|
|
def run_evennia(args, env=None, timeout=600):
|
|
result = subprocess.run([str(EVENNIA_BIN), *args], cwd=GAME_DIR, env=env, timeout=timeout, capture_output=True, text=True)
|
|
if result.returncode != 0:
|
|
raise RuntimeError(result.stderr or result.stdout)
|
|
return result.stdout
|
|
|
|
|
|
def ensure_db_and_owner():
|
|
run_evennia(["migrate"])
|
|
env = os.environ.copy()
|
|
env.update(
|
|
{
|
|
"EVENNIA_SUPERUSER_USERNAME": OPERATOR_USER,
|
|
"EVENNIA_SUPERUSER_EMAIL": OPERATOR_EMAIL,
|
|
"EVENNIA_SUPERUSER_PASSWORD": OPERATOR_PASSWORD,
|
|
}
|
|
)
|
|
run_evennia(["shell", "-c", "from evennia.accounts.models import AccountDB; print(AccountDB.objects.count())"], env=env)
|
|
|
|
|
|
def ensure_server_started():
|
|
run_evennia(["start"], timeout=240)
|
|
|
|
|
|
def run_shell(code: str):
|
|
env = os.environ.copy()
|
|
env["PYTHONPATH"] = str(REPO_ROOT) + os.pathsep + env.get("PYTHONPATH", "")
|
|
return run_evennia(["shell", "-c", code], env=env)
|
|
|
|
|
|
def ensure_timmy_and_world():
|
|
code = f'''
|
|
import sys
|
|
sys.path.insert(0, {str(REPO_ROOT)!r})
|
|
from evennia.accounts.models import AccountDB
|
|
from evennia.accounts.accounts import DefaultAccount
|
|
from evennia import DefaultRoom, DefaultExit, create_object
|
|
from evennia.utils.search import search_object
|
|
from evennia_tools.layout import ROOMS, EXITS, OBJECTS
|
|
from typeclasses.objects import Object
|
|
|
|
acc = AccountDB.objects.filter(username__iexact="Timmy").first()
|
|
if not acc:
|
|
acc, errs = DefaultAccount.create(username="Timmy", password={TIMMY_PASSWORD!r})
|
|
|
|
room_map = {{}}
|
|
for room in ROOMS:
|
|
found = search_object(room.key, exact=True)
|
|
obj = found[0] if found else None
|
|
if obj is None:
|
|
obj, errs = DefaultRoom.create(room.key, description=room.desc)
|
|
else:
|
|
obj.db.desc = room.desc
|
|
room_map[room.key] = obj
|
|
|
|
for ex in EXITS:
|
|
source = room_map[ex.source]
|
|
dest = room_map[ex.destination]
|
|
found = [obj for obj in source.contents if obj.key == ex.key and getattr(obj, "destination", None) == dest]
|
|
if not found:
|
|
DefaultExit.create(ex.key, source, dest, description=f"Exit to {{dest.key}}.", aliases=list(ex.aliases))
|
|
|
|
for spec in OBJECTS:
|
|
location = room_map[spec.location]
|
|
found = [obj for obj in location.contents if obj.key == spec.key]
|
|
if not found:
|
|
obj = create_object(typeclass=Object, key=spec.key, location=location)
|
|
else:
|
|
obj = found[0]
|
|
obj.db.desc = spec.desc
|
|
|
|
char = list(acc.characters)[0]
|
|
char.location = room_map["Gate"]
|
|
char.home = room_map["Gate"]
|
|
char.save()
|
|
print("WORLD_OK")
|
|
print("TIMMY_LOCATION", char.location.key)
|
|
'''
|
|
return run_shell(code)
|
|
|
|
|
|
def verify():
|
|
status = run_evennia(["status"], timeout=120)
|
|
info = run_evennia(["info"], timeout=120)
|
|
web = subprocess.run(
|
|
[sys.executable, "-c", "import urllib.request; r=urllib.request.urlopen('http://127.0.0.1:4001', timeout=10); print(r.status); print(r.headers.get('Content-Type'))"],
|
|
timeout=30,
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
)
|
|
return {"status": status.strip(), "info": info.strip(), "web": web.stdout.strip()}
|
|
|
|
|
|
def main():
|
|
ensure_venv()
|
|
ensure_game_dir()
|
|
ensure_db_and_owner()
|
|
ensure_server_started()
|
|
provision = ensure_timmy_and_world()
|
|
proof = verify()
|
|
print(json.dumps({"provision_stdout": provision, "verification": proof}, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|