From 1f7858e1fab14531d0295fd0b4d20e1a4a03d99f Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 21 Feb 2026 19:19:54 -0500 Subject: [PATCH] Codex skill for development added. Tested by using it to set up Play Palace. --- .codex/skills/linux-game-manager-dev/SKILL.md | 81 +++++++ .../linux-game-manager-dev/agents/openai.yaml | 4 + .../references/core-map.md | 76 +++++++ .../references/game-extension.md | 134 ++++++++++++ .../references/skill-maintenance.md | 53 +++++ .../references/tooling-prereqs.md | 38 ++++ .../scripts/audit_game_catalog.py | 202 ++++++++++++++++++ .install/Play Palace.sh | 69 ++++++ .launch/Play Palace.game | 18 ++ 9 files changed, 675 insertions(+) create mode 100644 .codex/skills/linux-game-manager-dev/SKILL.md create mode 100644 .codex/skills/linux-game-manager-dev/agents/openai.yaml create mode 100644 .codex/skills/linux-game-manager-dev/references/core-map.md create mode 100644 .codex/skills/linux-game-manager-dev/references/game-extension.md create mode 100644 .codex/skills/linux-game-manager-dev/references/skill-maintenance.md create mode 100644 .codex/skills/linux-game-manager-dev/references/tooling-prereqs.md create mode 100644 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py create mode 100644 .install/Play Palace.sh create mode 100644 .launch/Play Palace.game diff --git a/.codex/skills/linux-game-manager-dev/SKILL.md b/.codex/skills/linux-game-manager-dev/SKILL.md new file mode 100644 index 0000000..6c01fc2 --- /dev/null +++ b/.codex/skills/linux-game-manager-dev/SKILL.md @@ -0,0 +1,81 @@ +--- +name: linux-game-manager-dev +description: Develop and maintain Linux Game Manager in this repository. Use when adding or removing games, editing `.install`/`.launch`/`.update` scripts, changing `linux-game-manager.sh` installer/launcher/removal/update/CLI behavior, validating accessibility-sensitive flows, or refreshing project-local skill docs after functionality changes. +--- + +# Linux Game Manager Development + +Use this skill to make safe, consistent changes to Linux Game Manager and keep the skill accurate as the codebase evolves. + +## Fast Routing + +1. If the task adds/removes/changes a game, read `references/game-extension.md`. +2. If the task changes orchestration/CLI/config behavior, read `references/core-map.md`. +3. If the task changes behavior and you are updating this skill, read `references/skill-maintenance.md`. +4. Run `python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py` before and after major game-catalog changes. + +## Core Workflow + +1. Inspect relevant behavior and identify affected surfaces (`.install`, `.launch`, `.update`, `linux-game-manager.sh`, `speech`, docs). +2. Implement with existing patterns and helper functions instead of inventing new flows. +3. Validate runtime behavior with CLI/manual checks: + - `./linux-game-manager.sh -i` + - `./linux-game-manager.sh -r` + - `./linux-game-manager.sh -u` + - `./linux-game-manager.sh -t` +4. Validate catalog consistency: + - `python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py` +5. If bash files were edited: + - Verify `shellcheck` is installed. + - If missing, stop and prompt the user to install it (see `references/tooling-prereqs.md`). + - Run shellcheck on edited files and fix all reported errors. +6. If functionality changed, refresh this skill using `references/skill-maintenance.md`. + +## Non-Negotiable Rules + +1. Keep installer and launcher base filenames identical: + - `.install/.sh` + - `.launch/.game` +2. Ensure launcher scripts include a concrete `installPath`-based path so removal can locate game data. +3. Define `run_update()` in any `.update/.sh` script. +4. Preserve accessibility-first interaction: + - Keep dialog/yad wrapper usage (`ui_*` helpers) consistent. + - Do not introduce keyboard traps. + - For GUI applications, do not add new `speech-dispatcher`/`spd-say` dependencies. +5. Keep new script variables camelCase, function names snake_case, class names PascalCase. +6. For ambiguous "disable game" requests, default to disabling installer visibility only and preserve launcher playability unless the user explicitly requests complete disable. + +## Required Validation + +1. Run catalog audit: + - `python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py` +2. Run manual command checks relevant to change scope: + - install flow: `./linux-game-manager.sh -i` + - launcher flow: `./linux-game-manager.sh` + - removal flow: `./linux-game-manager.sh -r` + - update flow: `./linux-game-manager.sh -u` +3. Run shellcheck on every edited bash script and fix all issues. + - If shellcheck is unavailable, prompt the user to install it before continuing. +4. Validate this skill structure after edits: + - `python3 /home/storm/.codex/skills/.system/skill-creator/scripts/quick_validate.py .codex/skills/linux-game-manager-dev` + +## Mandatory Skill Maintenance + +Treat this skill as stale immediately after any change to: +- `linux-game-manager.sh` +- Any file in `.install/`, `.launch/`, `.update/`, or `speech/` +- `README.md` +- Any change to game lifecycle expectations (install path rules, update hooks, CLI options, helper function contracts) + +When stale, perform the full refresh workflow in `references/skill-maintenance.md` before completing the task. + +## References + +- `references/core-map.md`: Architecture and extension map. +- `references/game-extension.md`: Exact workflow for adding/removing/extending games. +- `references/skill-maintenance.md`: Critical instructions to keep this skill accurate after code changes. +- `references/tooling-prereqs.md`: Required tools and shellcheck installation prompts by distro. + +## Scripts + +- `scripts/audit_game_catalog.py`: Reports installer/launcher/update consistency and disabled entries. diff --git a/.codex/skills/linux-game-manager-dev/agents/openai.yaml b/.codex/skills/linux-game-manager-dev/agents/openai.yaml new file mode 100644 index 0000000..08b1e84 --- /dev/null +++ b/.codex/skills/linux-game-manager-dev/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Linux Game Manager Dev" + short_description: "Add games and extend linux-game-manager safely" + default_prompt: "Use the project workflows to add games, modify manager behavior, validate accessibility-friendly flows, and keep this skill synchronized with code changes." diff --git a/.codex/skills/linux-game-manager-dev/references/core-map.md b/.codex/skills/linux-game-manager-dev/references/core-map.md new file mode 100644 index 0000000..8daf19b --- /dev/null +++ b/.codex/skills/linux-game-manager-dev/references/core-map.md @@ -0,0 +1,76 @@ +# Linux Game Manager Core Map + +Last refreshed: 2026-02-21 + +## Top-Level Structure + +- `linux-game-manager.sh`: Main orchestrator, UI wrappers, CLI option handling, install/remove/update flows, cache and settings behavior. +- `.install/`: Per-game installer scripts sourced by `game_installer`. +- `.launch/`: Per-game launch scripts (`.game`) and runnable entries (`.sh`, usually symlinks). +- `.update/`: Optional per-game update scripts expected to define `run_update()`. +- `speech/speak_window_title.sh`: Accessibility helper for announcing focused window titles. +- `README.md`: Project summary and high-level behavior notes. +- `.files/`: Game-specific auxiliary assets/scripts used by some installers or launchers. + +## Catalog Snapshot + +- Installers: 38 (`.install/*.sh`) +- Launcher definitions: 38 (`.launch/*.game`) +- Launcher runnable entries: 26 (`.launch/*.sh`, both symlinks and files) +- Update scripts: 5 (`.update/*.sh`) + +Regenerate this snapshot with: + +```bash +python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py +``` + +## Main Runtime Flows + +1. **Install flow** +- `game_installer` builds menu from `.install/*.sh`. +- Selected installer is sourced in the current shell. +- If `.launch/.game` exists and `.launch/.sh` does not, the manager creates a symlink. + +2. **Launch flow** +- `game_launcher` enumerates `.launch/*.sh` entries. +- Entries with first line `#//` are skipped. +- Selected launcher is sourced/executed. + +3. **Removal flow** +- `game_removal` resolves launcher entry to real script. +- It extracts the first `installPath`-containing line from launcher script to infer directory to remove. +- If no `installPath` reference is found, only launcher entry is removed. + +4. **Update flow** +- `game_update` lists `.update/*.sh`. +- Selected updater is sourced. +- `run_update()` is called and must exist. + +## CLI Surface + +- `-i`: install game +- `-r`: remove game +- `-u`: update game +- `-t`: show number of available games +- `-C`: clear cache +- `-D`: create desktop launcher +- `-L`: show license +- `-h`: show help +- `-N`: enable no-cache mode +- `-R`: force redownload mode + +## Config and State + +- Cache: `${XDG_CACHE_HOME:-$HOME/.cache}/linux-game-manager` +- Config base: `${XDG_CONFIG_HOME:-$HOME/.config}/storm-games/linux-game-manager` +- Settings overrides: `settings.conf` in config base +- Default install root: `${HOME}/.local/games` + +## Contributor Pitfalls + +- Mismatched `.install/.sh` vs `.launch/.game` names create orphaned or invisible entries. +- Missing `installPath` reference in launcher prevents full uninstall. +- Missing `run_update()` in `.update` scripts breaks update flow. +- Bypassing shared download helpers risks cache and validation regressions. +- For `uv`-based Python games that use `accessible-output2`, include a host copy of `libspeechd.so.2` and export `LD_LIBRARY_PATH` from the launcher to avoid distro packaging differences. diff --git a/.codex/skills/linux-game-manager-dev/references/game-extension.md b/.codex/skills/linux-game-manager-dev/references/game-extension.md new file mode 100644 index 0000000..d2d7edb --- /dev/null +++ b/.codex/skills/linux-game-manager-dev/references/game-extension.md @@ -0,0 +1,134 @@ +# Game Extension Workflow + +Use this workflow whenever adding, renaming, removing, or substantially changing a game. + +## Add a New Game + +1. Create installer script: +- Path: `.install/.sh` +- Keep first line active (do not start with `#//`). +- Reuse manager helpers (`check_architecture`, `check_dependencies`, `download`, `download_named`, `get_installer`, `ui_*`). +- Install into a deterministic path under `${installPath}`. + +2. Create launcher definition: +- Path: `.launch/.game` +- Keep base filename identical to installer (``). +- Include an explicit install-path variable based on `installPath`, for example: + +```bash +gamePath="${installPath}/MyGame" +``` + +- Run the game from that path. + +3. Optional update script: +- Path: `.update/.sh` +- Implement `run_update()` (required by update flow). + +4. Let installer create runnable launcher entry: +- The manager auto-creates `.launch/.sh` symlink when installer completes and `.game` exists. + +## Rename a Game Safely + +1. Rename `.install/.sh` to `.install/.sh`. +2. Rename `.launch/.game` to `.launch/.game`. +3. Rename `.update/.sh` if present. +4. Remove stale `.launch/.sh` if still present. +5. Re-run catalog audit and manual checks. + +## Remove a Game From Repo + +1. Delete `.install/.sh`. +2. Delete `.launch/.game`. +3. Delete `.launch/.sh` if tracked. +4. Delete `.update/.sh` if present. +5. Run catalog audit to confirm no orphan entries remain. + +## Disable Policy (Important) + +When a request says "disable " without more detail: + +1. Default behavior: disable installs only. +- Add `#//` to first line of `.install/.sh`. +- Do not disable `.launch/.game` by default. +- This preserves playability for users who already have the game installed. + +2. If user explicitly requests complete disable: +- Add `#//` to first line of both: + - `.install/.sh` + - `.launch/.game` + +3. If wording is ambiguous but could imply full disable/removal: +- Ask a short clarification question before changing launcher/removal behavior. + +## Manual Validation Checklist + +1. Run installer flow: + +```bash +./linux-game-manager.sh -i +``` + +2. Confirm launcher files exist: + +```bash +ls ".launch/.game" ".launch/.sh" +``` + +3. Run launcher flow: + +```bash +./linux-game-manager.sh +``` + +4. Run removal flow: + +```bash +./linux-game-manager.sh -r +``` + +5. If updater exists, run update flow: + +```bash +./linux-game-manager.sh -u +``` + +6. Run consistency audit: + +```bash +python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py +``` + +## Script Authoring Rules + +- Use camelCase variable names. +- Use snake_case for function names. +- Keep scripts robust when sourced in the manager shell. +- Avoid introducing unnecessary colorized output. +- For edited bash scripts, run shellcheck and fix all errors. +- If `shellcheck` is not installed, prompt the user to install it first (see `references/tooling-prereqs.md`). + +## Pattern: uv + Speech Dispatcher Host Libraries + +Use this pattern for Python games installed with `uv` when runtime speech support depends on system `libspeechd`. + +1. In installer script: +- Check dependencies: `git` and `uv`. +- Clone game repository into `${installPath}/`. +- Run `uv sync` in the project directory that contains `pyproject.toml`. +- Locate `libspeechd.so.2` from host system using: + - `ldconfig -p` when available. + - Fallback paths for common distros: + - `/usr/lib/libspeechd.so.2` (Arch-style) + - `/usr/lib/x86_64-linux-gnu/libspeechd.so.2` (Debian/Ubuntu-style) + - `/usr/lib64/libspeechd.so.2` and related `/lib*` fallbacks +- Copy resolved library into a game-local directory such as: + - `${installPath}///.host-libs/libspeechd.so.2` +- Fail with a clear message if library is not found. + +2. In launcher script: +- Export `LD_LIBRARY_PATH` with the game-local `.host-libs` directory prepended. +- Launch via `uv run ...` from the same directory used for `uv sync`. + +3. Removal safety: +- Ensure first `installPath` line in launcher points to a path that lets `game_removal` infer the game root correctly. diff --git a/.codex/skills/linux-game-manager-dev/references/skill-maintenance.md b/.codex/skills/linux-game-manager-dev/references/skill-maintenance.md new file mode 100644 index 0000000..df7ec76 --- /dev/null +++ b/.codex/skills/linux-game-manager-dev/references/skill-maintenance.md @@ -0,0 +1,53 @@ +# Skill Maintenance (Critical) + +This skill is only trustworthy if it is refreshed immediately after behavior changes. + +## Mandatory Refresh Triggers + +Run this maintenance workflow whenever any of these change: + +- `linux-game-manager.sh` +- Anything in `.install/` +- Anything in `.launch/` +- Anything in `.update/` +- Anything in `speech/` +- `README.md` +- Any game lifecycle contract (install path usage, symlink behavior, update function contracts, CLI flags) + +## Refresh Workflow + +1. Run catalog audit and capture output: + +```bash +python3 .codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py +``` + +2. Re-check core flow definitions in `linux-game-manager.sh`: + +```bash +rg -n "game_installer|game_launcher|game_removal|game_update|getopts|help\\(" linux-game-manager.sh +``` + +3. Update skill references: +- Update `references/core-map.md` catalog snapshot if counts changed. +- Update `references/core-map.md` flow descriptions if behavior changed. +- Update `references/game-extension.md` if onboarding or naming rules changed. +- Update this file if refresh triggers or process changed. + +4. Validate the skill structure: + +```bash +python3 /home/storm/.codex/skills/.system/skill-creator/scripts/quick_validate.py .codex/skills/linux-game-manager-dev +``` + +5. If any bash scripts changed, run shellcheck on edited files and fix all errors. + - If `shellcheck` is missing, pause and prompt the user to install it using `references/tooling-prereqs.md`. + +## Completion Criteria + +Do not consider maintenance complete until all are true: + +1. Catalog audit shows no critical mismatches, and any warnings are reviewed. +2. All changed behavior is reflected in skill reference files. +3. Skill passes `quick_validate.py`. +4. Any edited bash scripts are shellcheck-clean. diff --git a/.codex/skills/linux-game-manager-dev/references/tooling-prereqs.md b/.codex/skills/linux-game-manager-dev/references/tooling-prereqs.md new file mode 100644 index 0000000..b5bb39a --- /dev/null +++ b/.codex/skills/linux-game-manager-dev/references/tooling-prereqs.md @@ -0,0 +1,38 @@ +# Tooling Prerequisites + +## Required Tool for Bash Changes + +- `shellcheck` is mandatory whenever editing any bash/sh file in this repository. +- If `shellcheck` is not available, prompt the user to install it before continuing validation. + +Check availability: + +```bash +command -v shellcheck +``` + +## Prompt Template + +Use this exact style when missing: + +`shellcheck is required for bash/sh edits in linux-game-manager. Please install it, then I will continue validation.` + +## Install Commands by Common Distro Family + +- Arch/Manjaro: + - `sudo pacman -S shellcheck` +- Debian/Ubuntu/Linux Mint/Pop!_OS: + - `sudo apt update && sudo apt install -y shellcheck` +- Fedora/RHEL/CentOS Stream: + - `sudo dnf install -y ShellCheck` +- openSUSE: + - `sudo zypper install -y ShellCheck` +- Alpine: + - `sudo apk add shellcheck` + +If distro is unknown, ask the user what distribution they are on and provide the matching package command. + +## Notes for LGM Collaboration + +- Do not skip shellcheck for “small” bash changes. +- Fix all shellcheck errors; warnings may be suppressed only when required by sourced-global patterns and with a short comment. diff --git a/.codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py b/.codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py new file mode 100644 index 0000000..1e0dd2a --- /dev/null +++ b/.codex/skills/linux-game-manager-dev/scripts/audit_game_catalog.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +"""Audit Linux Game Manager catalog consistency.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + + +def read_first_line(file_path: Path) -> str: + try: + with file_path.open("r", encoding="utf-8") as handle: + return handle.readline().strip() + except OSError: + return "" + + +def has_run_update(file_path: Path) -> bool: + try: + content = file_path.read_text(encoding="utf-8") + except OSError: + return False + + return "run_update()" in content or "run_update ()" in content + + +def collect_names(file_paths: list[Path], suffix: str) -> list[str]: + return sorted(file_path.name[: -len(suffix)] for file_path in file_paths) + + +def count_disabled(file_paths: list[Path]) -> list[str]: + disabledNames = [] + for file_path in file_paths: + if read_first_line(file_path).startswith("#//"): + disabledNames.append(file_path.name) + return sorted(disabledNames) + + +def build_report(repo_root: Path) -> dict: + installDir = repo_root / ".install" + launchDir = repo_root / ".launch" + updateDir = repo_root / ".update" + + missingDirs = [ + str(path.relative_to(repo_root)) + for path in [installDir, launchDir, updateDir] + if not path.exists() + ] + if missingDirs: + raise FileNotFoundError( + f"Expected directories not found: {', '.join(missingDirs)}" + ) + + installPaths = sorted(installDir.glob("*.sh")) + launchGamePaths = sorted(launchDir.glob("*.game")) + launchShPaths = sorted(launchDir.glob("*.sh")) + updatePaths = sorted(updateDir.glob("*.sh")) + + installerNames = collect_names(installPaths, ".sh") + launcherGameNames = collect_names(launchGamePaths, ".game") + launcherShNames = collect_names(launchShPaths, ".sh") + + launcherSymlinkCount = sum(1 for path in launchShPaths if path.is_symlink()) + launcherFileCount = len(launchShPaths) - launcherSymlinkCount + + missingLauncherGameForInstaller = sorted( + set(installerNames) - set(launcherGameNames) + ) + missingInstallerForLauncherGame = sorted( + set(launcherGameNames) - set(installerNames) + ) + missingLauncherShForLauncherGame = sorted( + set(launcherGameNames) - set(launcherShNames) + ) + orphanLauncherSh = sorted(set(launcherShNames) - set(launcherGameNames)) + + updateWithoutRunFunction = sorted( + path.name for path in updatePaths if not has_run_update(path) + ) + + report = { + "repoRoot": str(repo_root), + "criticalMismatches": [ + "missingLauncherGameForInstaller", + "updateScriptsMissingRunUpdate", + ], + "warningMismatches": [ + "missingInstallerForLauncherGame", + "missingLauncherShForLauncherGame", + "orphanLauncherSh", + ], + "counts": { + "installers": len(installPaths), + "launcherGameDefinitions": len(launchGamePaths), + "launcherRunnableEntries": len(launchShPaths), + "launcherRunnableSymlinks": launcherSymlinkCount, + "launcherRunnableRegularFiles": launcherFileCount, + "updateScripts": len(updatePaths), + }, + "disabledEntries": { + "installers": count_disabled(installPaths), + "launchers": count_disabled(launchShPaths), + }, + "mismatches": { + "missingLauncherGameForInstaller": missingLauncherGameForInstaller, + "missingInstallerForLauncherGame": missingInstallerForLauncherGame, + "missingLauncherShForLauncherGame": missingLauncherShForLauncherGame, + "orphanLauncherSh": orphanLauncherSh, + "updateScriptsMissingRunUpdate": updateWithoutRunFunction, + }, + } + return report + + +def print_human_report(report: dict) -> None: + counts = report["counts"] + mismatches = report["mismatches"] + criticalMismatches = report["criticalMismatches"] + warningMismatches = report["warningMismatches"] + disabledEntries = report["disabledEntries"] + + print(f"Repository: {report['repoRoot']}") + print("Counts:") + print(f" Installers: {counts['installers']}") + print(f" Launcher .game files: {counts['launcherGameDefinitions']}") + print(f" Launcher .sh entries: {counts['launcherRunnableEntries']}") + print(f" Symlinks: {counts['launcherRunnableSymlinks']}") + print(f" Regular files: {counts['launcherRunnableRegularFiles']}") + print(f" Update scripts: {counts['updateScripts']}") + + print("Disabled entries:") + print(f" Installers: {len(disabledEntries['installers'])}") + if disabledEntries["installers"]: + print(" " + ", ".join(disabledEntries["installers"])) + print(f" Launchers: {len(disabledEntries['launchers'])}") + if disabledEntries["launchers"]: + print(" " + ", ".join(disabledEntries["launchers"])) + + print("Critical checks:") + for key in criticalMismatches: + values = mismatches[key] + print(f" {key}: {len(values)}") + if values: + print(" " + ", ".join(values)) + + print("Warnings:") + for key in warningMismatches: + values = mismatches[key] + print(f" {key}: {len(values)}") + if values: + print(" " + ", ".join(values)) + + criticalTotal = sum(len(mismatches[key]) for key in criticalMismatches) + if criticalTotal == 0: + print("Result: OK (no critical mismatches)") + else: + print("Result: ATTENTION REQUIRED (critical mismatches detected)") + + +def parse_args() -> argparse.Namespace: + defaultRepoRoot = Path(__file__).resolve().parents[4] + parser = argparse.ArgumentParser( + description="Audit installer/launcher/update consistency for linux-game-manager." + ) + parser.add_argument( + "--repo-root", + default=str(defaultRepoRoot), + help=f"Repository root path (default: {defaultRepoRoot})", + ) + parser.add_argument( + "--json", + action="store_true", + help="Emit JSON instead of human-readable text", + ) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + repoRoot = Path(args.repo_root).resolve() + + try: + report = build_report(repoRoot) + except FileNotFoundError as error: + print(str(error), file=sys.stderr) + return 2 + + if args.json: + print(json.dumps(report, indent=2, sort_keys=True)) + else: + print_human_report(report) + + criticalTotal = sum( + len(report["mismatches"][bucket]) for bucket in report["criticalMismatches"] + ) + return 1 if criticalTotal else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.install/Play Palace.sh b/.install/Play Palace.sh new file mode 100644 index 0000000..cad24f5 --- /dev/null +++ b/.install/Play Palace.sh @@ -0,0 +1,69 @@ +check_dependencies git uv + +# shellcheck disable=SC2154 # installPath is exported by linux-game-manager.sh +gameRoot="${installPath}/PlayPalace11" +desktopPath="${gameRoot}/clients/desktop" +hostLibDir="${desktopPath}/.host-libs" +speechdLibPath="" +libCandidates=( + "/usr/lib/libspeechd.so.2" + "/usr/lib64/libspeechd.so.2" + "/usr/lib/x86_64-linux-gnu/libspeechd.so.2" + "/lib/x86_64-linux-gnu/libspeechd.so.2" + "/lib64/libspeechd.so.2" +) + +if [[ -e "${gameRoot}" ]]; then + ui_msgbox "Game Installer" "Game Installer" "\"${gameRoot}\" already exists. Remove it first or choose a different game." + exit 1 +fi + +mkdir -p "${installPath}" || { + ui_msgbox "Game Installer" "Game Installer" "Could not create ${installPath}." + exit 1 +} + +if ! git -C "${installPath}" clone "https://github.com/XGDevGroup/PlayPalace11.git"; then + ui_msgbox "Game Installer" "Game Installer" "Could not clone Play Palace from GitHub." + exit 1 +fi + +if ! pushd "${desktopPath}" > /dev/null; then + ui_msgbox "Game Installer" "Game Installer" "Could not find desktop client path at ${desktopPath}." + exit 1 +fi + +if ! uv sync; then + popd > /dev/null || exit 1 + ui_msgbox "Game Installer" "Game Installer" "uv sync failed for Play Palace." + exit 1 +fi +popd > /dev/null || exit 1 + +if command -v ldconfig > /dev/null 2>&1; then + speechdLibPath="$(ldconfig -p 2> /dev/null | awk '/libspeechd\.so\.2/{print $NF; exit}')" +fi + +if [[ -z "${speechdLibPath}" ]]; then + for libPath in "${libCandidates[@]}"; do + if [[ -r "${libPath}" ]]; then + speechdLibPath="${libPath}" + break + fi + done +fi + +if [[ -z "${speechdLibPath}" ]]; then + ui_msgbox "Game Installer" "Game Installer" "Could not find libspeechd.so.2.\nInstall speech-dispatcher (Arch) or a libspeechd package (Debian/Ubuntu) and try again." + exit 1 +fi + +mkdir -p "${hostLibDir}" || { + ui_msgbox "Game Installer" "Game Installer" "Could not create ${hostLibDir}." + exit 1 +} + +if ! cp -Lf "${speechdLibPath}" "${hostLibDir}/libspeechd.so.2"; then + ui_msgbox "Game Installer" "Game Installer" "Could not copy ${speechdLibPath} into ${hostLibDir}." + exit 1 +fi diff --git a/.launch/Play Palace.game b/.launch/Play Palace.game new file mode 100644 index 0000000..aaa0757 --- /dev/null +++ b/.launch/Play Palace.game @@ -0,0 +1,18 @@ +check_dependencies uv + +# shellcheck disable=SC2154 # installPath is exported by linux-game-manager.sh +gamePath="${installPath}/PlayPalace11/clients" +desktopPath="${gamePath}/desktop" +hostLibDir="${desktopPath}/.host-libs" + +if ! [[ -d "${desktopPath}" ]]; then + ui_msgbox "Linux Game Manager" "Linux Game Manager" "Play Palace is not installed at ${desktopPath}." + exit 1 +fi + +if [[ -d "${hostLibDir}" ]]; then + export LD_LIBRARY_PATH="${hostLibDir}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}" +fi + +pushd "${desktopPath}" || exit 1 +uv run python client.py