game-stream: add Meccha Chameleon launcher
This commit is contained in:
parent
9d0356a357
commit
9bc01aaaf0
@ -4,6 +4,7 @@ kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- vault-serviceaccount.yaml
|
||||
- wolf-config-ensure-configmap.yaml
|
||||
- wolf-api-proxy-configmap.yaml
|
||||
- wolf-gatekeeper-configmap.yaml
|
||||
- wolf-service.yaml
|
||||
|
||||
144
services/game-stream/wolf-config-ensure-configmap.yaml
Normal file
144
services/game-stream/wolf-config-ensure-configmap.yaml
Normal file
@ -0,0 +1,144 @@
|
||||
# services/game-stream/wolf-config-ensure-configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: wolf-config-ensure
|
||||
namespace: game-stream
|
||||
data:
|
||||
wolf_config_ensure.py: |
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
CONFIG_PATH = Path(os.environ.get("WOLF_CFG_FILE", "/etc/wolf/cfg/config.toml"))
|
||||
APP_TITLE = "MECCHA CHAMELEON"
|
||||
APP_ID = "4704690"
|
||||
|
||||
APP_BLOCK = f"""
|
||||
|
||||
[[profiles.apps]]
|
||||
title = "{APP_TITLE}"
|
||||
render_node = "/dev/dri/renderD128"
|
||||
start_audio_server = true
|
||||
start_virtual_compositor = true
|
||||
icon_png_path = "https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/{APP_ID}/capsule_616x353.jpg"
|
||||
[profiles.apps.runner]
|
||||
type = "docker"
|
||||
name = "WolfSteam"
|
||||
image = "ghcr.io/games-on-whales/steam:edge"
|
||||
mounts = []
|
||||
env = [
|
||||
"PROTON_LOG=1",
|
||||
"RUN_SWAY=true",
|
||||
"GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*",
|
||||
"STEAM_STARTUP_FLAGS=steam://rungameid/{APP_ID}"
|
||||
]
|
||||
devices = []
|
||||
ports = []
|
||||
base_create_json = \"\"\"
|
||||
{{
|
||||
"HostConfig": {{
|
||||
"IpcMode": "host",
|
||||
"CapAdd": ["SYS_ADMIN", "SYS_NICE", "SYS_PTRACE", "NET_RAW", "MKNOD", "NET_ADMIN"],
|
||||
"SecurityOpt": ["seccomp=unconfined", "apparmor=unconfined"],
|
||||
"Ulimits": [{{"Name":"nofile", "Hard":10240, "Soft":10240}}],
|
||||
"Privileged": false,
|
||||
"DeviceCgroupRules": ["c 13:* rmw", "c 244:* rmw"]
|
||||
}}
|
||||
}}
|
||||
\"\"\"
|
||||
""".rstrip()
|
||||
|
||||
PROFILE_HEADER = re.compile(r"(?m)^\s*\[\[profiles\]\]\s*$")
|
||||
EMPTY_PROFILES = re.compile(r"(?m)^\s*profiles\s*=\s*\[\]\s*$\n?")
|
||||
|
||||
|
||||
def _seed_config() -> str:
|
||||
return f"""hostname = "wolf"
|
||||
support_hevc = true
|
||||
config_version = 2
|
||||
uuid = "{uuid.uuid4()}"
|
||||
|
||||
paired_clients = []
|
||||
gstreamer = {{}}
|
||||
|
||||
[[profiles]]
|
||||
id = "moonlight-profile-id"
|
||||
name = "Moonlight"
|
||||
{APP_BLOCK}
|
||||
|
||||
[[profiles]]
|
||||
id = "user"
|
||||
name = "User"
|
||||
{APP_BLOCK}
|
||||
"""
|
||||
|
||||
|
||||
def _has_app(section: str) -> bool:
|
||||
return re.search(rf'(?m)^\s*title\s*=\s*["\']{re.escape(APP_TITLE)}["\']\s*$', section) is not None
|
||||
|
||||
|
||||
def _ensure_app(text: str) -> str:
|
||||
if not text.strip():
|
||||
return _seed_config()
|
||||
|
||||
starts = [match.start() for match in PROFILE_HEADER.finditer(text)]
|
||||
if not starts:
|
||||
return EMPTY_PROFILES.sub("", text).rstrip() + "\n\n[[profiles]]\nid = \"moonlight-profile-id\"\nname = \"Moonlight\"" + APP_BLOCK + "\n"
|
||||
|
||||
parts = []
|
||||
cursor = 0
|
||||
changed = False
|
||||
for index, start in enumerate(starts):
|
||||
end = starts[index + 1] if index + 1 < len(starts) else len(text)
|
||||
parts.append(text[cursor:start])
|
||||
section = text[start:end]
|
||||
if _has_app(section):
|
||||
parts.append(section)
|
||||
else:
|
||||
parts.append(section.rstrip() + APP_BLOCK + "\n\n")
|
||||
changed = True
|
||||
cursor = end
|
||||
parts.append(text[cursor:])
|
||||
if not changed:
|
||||
return text
|
||||
return "".join(parts).rstrip() + "\n"
|
||||
|
||||
|
||||
def _validate_toml(text: str) -> None:
|
||||
try:
|
||||
import tomllib
|
||||
except Exception:
|
||||
return
|
||||
tomllib.loads(text)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
original = CONFIG_PATH.read_text(encoding="utf-8") if CONFIG_PATH.exists() else ""
|
||||
updated = _ensure_app(original)
|
||||
if updated == original:
|
||||
print(f"{APP_TITLE} already present in {CONFIG_PATH}")
|
||||
return 0
|
||||
|
||||
_validate_toml(updated)
|
||||
if CONFIG_PATH.exists():
|
||||
backup = CONFIG_PATH.with_suffix(CONFIG_PATH.suffix + ".pre-meccha-chameleon")
|
||||
backup.write_text(original, encoding="utf-8")
|
||||
|
||||
tmp_path = CONFIG_PATH.with_suffix(CONFIG_PATH.suffix + ".tmp")
|
||||
tmp_path.write_text(updated, encoding="utf-8")
|
||||
tmp_path.replace(CONFIG_PATH)
|
||||
print(f"ensured {APP_TITLE} Steam app {APP_ID} in {CONFIG_PATH}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as exc:
|
||||
print(f"failed to ensure {APP_TITLE}: {exc}", file=sys.stderr)
|
||||
raise
|
||||
@ -17,6 +17,8 @@ spec:
|
||||
labels:
|
||||
app: wolf
|
||||
atlas.bstein.dev/game-mode: wolf
|
||||
annotations:
|
||||
game-stream.bstein.dev/wolf-config-revision: "2026-07-13-meccha-chameleon"
|
||||
spec:
|
||||
hostNetwork: true
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
@ -39,6 +41,19 @@ spec:
|
||||
mountPath: /wolf-runtime
|
||||
- name: wolf-socket
|
||||
mountPath: /wolf-socket
|
||||
- name: wolf-config-ensure
|
||||
image: ghcr.io/games-on-whales/wolf:stable
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/usr/bin/python3", "/opt/wolf-config-ensure/wolf_config_ensure.py"]
|
||||
env:
|
||||
- name: WOLF_CFG_FILE
|
||||
value: /etc/wolf/cfg/config.toml
|
||||
volumeMounts:
|
||||
- name: wolf-state
|
||||
mountPath: /etc/wolf
|
||||
- name: wolf-config-ensure
|
||||
mountPath: /opt/wolf-config-ensure
|
||||
readOnly: true
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: titan-24
|
||||
tolerations:
|
||||
@ -175,6 +190,10 @@ spec:
|
||||
configMap:
|
||||
name: wolf-api-proxy
|
||||
defaultMode: 0555
|
||||
- name: wolf-config-ensure
|
||||
configMap:
|
||||
name: wolf-config-ensure
|
||||
defaultMode: 0555
|
||||
- name: wolfmanager-data
|
||||
hostPath:
|
||||
path: /etc/wolfmanager
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user