# Universal Game Updater A simple, self-hosted updater for distributing game client files. Consists of three parts: - **`updater.py`** — the client-side app (compiled to `updater.exe`) that players run. Checks the server for changed files and downloads only what's needed. - **`server.py`** — a lightweight HTTP server that serves client files and manifests. - **`generate_manifest.py`** — a script you run on your build machine to generate the manifest the server distributes. - **`generate_key.py`** — a helper to generate access keys (see [Access keys](#access-keys) below). Works with any game — just fill in the configuration blocks at the top of each script. ## How it works 1. You put your game's files in a folder on the server (e.g. `game_client/`). 2. You run `generate_manifest.py` on that folder. It walks the folder, hashes every file (MD5), and writes a `manifest.json` listing `relative/path: hash` for every file. 3. `server.py` serves that folder over HTTP, keyed by an access key defined in `config.json`. 4. Players run `updater.exe`. It downloads the server's `manifest.json`, compares hashes against their local files, and downloads anything missing or different. 5. Once everything matches, the "Play" button launches the game executable. ## Setup ### 1. Server Install Python 3 (no external dependencies needed — everything uses the standard library). Generate an access key for each client version you want to distribute: ```bash python generate_key.py ``` This prints a random 64-character hex string. Generate one per client (e.g. one for `main`, one for `test`). Edit `config.json`: ```json { "host": "0.0.0.0", "port": 3232, "clients": { "": "game_client", "": "game_client_test" } } ``` - `host` / `port` — what the server listens on. - `clients` — maps an access key to the folder (relative to `server.py`) that key serves. Put your game files in the folders referenced above (`game_client/`, etc.), then generate the manifest for each: ```bash python generate_manifest.py game_client python generate_manifest.py game_client_test ``` Re-run this after every update to the client files, then start (or restart) the server: ```bash python server.py ``` Files become available at `http://://...`. ### 2. Updater (client) Edit the configuration block at the top of `updater.py`: ```python GAME_NAME = "My Game" SERVER_ADDRESS = "update.mygame.com" # domain or IP SERVER_PORT = "3232" USE_HTTPS = False CLIENT_KEYS = { "Main": "", "Test": "", } DEFAULT_CLIENT = "Main" GAME_EXE_RELATIVE_PATH = os.path.join("system", "game.exe") BANNER_RELATIVE_PATH = "banner.png" ICON_RELATIVE_PATH = "icon.ico" ``` - `SERVER_ADDRESS` accepts either a domain name or a raw IP — nothing else needs to change. - `CLIENT_KEYS` maps a friendly name shown in the UI to the actual access key sent in the request URL. Keys must match `config.json` on the server exactly. - `GAME_EXE_RELATIVE_PATH` is where the updater looks for the game's executable, relative to the folder the updater itself is placed in. Then compile with your favorite tool, e.g. [PyInstaller](https://pyinstaller.org/): ```bash pyinstaller --onefile --noconsole --icon=icon.ico --add-data "banner.png;." --add-data "icon.ico;." updater.py ``` Ship the resulting `updater.exe` alongside the game folder. ## Access keys Client folders are addressed by a long random hex key instead of a plain name like `main`, so the update path isn't obvious or easy to stumble on by guessing. **This is obscurity, not real security.** The key has to live inside `updater.exe` for it to work, and anyone motivated enough to disassemble the executable or inspect network traffic can recover it. Don't rely on it to gate anything sensitive. If a key leaks and you want to rotate it: 1. Generate a new key with `generate_key.py`. 2. Update it in the server's `config.json`. 3. Update it in `updater.py`'s `CLIENT_KEYS`. 4. Rebuild and redistribute `updater.exe`. No folder renaming or manifest regeneration needed. ## Ignoring files during manifest generation Edit the configuration block at the top of `generate_manifest.py`: ```python IGNORE_FILES = { MANIFEST_FILENAME, "updater.exe", "Option.ini", "WindowsInfo.ini", } IGNORE_FOLDERS = { "screenshots", "logs", "replays", } IGNORE_EXTENSIONS = { ".log", } ``` Add any player-specific files (settings, saves, screenshots, logs) here so the updater never overwrites or deletes them. ## Notes / limitations - File integrity is checked with MD5 — fine for detecting changed files, not intended as a cryptographic guarantee. - The server has no built-in rate limiting or HTTPS; put it behind a reverse proxy (nginx, Caddy) if you need either. - `updater.py` uses Tkinter for the UI, which ships with most standard Python installs on Windows.