Files
simple-updater/README.md
T
2026-09-08 15:49:48 +03:00

154 lines
6.2 KiB
Markdown

# 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": {
"<your-generated-hex-key>": "game_client",
"<another-generated-hex-key>": "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://<server-address>:<port>/<access-key>/...`.
### 2. Updater (client)
Edit the configuration block at the top of `updater.py`:
```python
GAME_NAME = "My Game"
UPDATER_NAME = "Launcher"
WINDOW_WIDTH = 450
WINDOW_HEIGHT = 300
BANNER_WIDTH = 430
BANNER_HEIGHT = 140
SERVER_ADDRESS = "update.mygame.com" # domain or IP
SERVER_PORT = "443" # 80/443 for HTTP/HTTPS, or a custom port for direct IP
USE_HTTPS = True
CLIENT_KEYS = {
"Main": "<same-hex-key-as-in-server-config.json>",
"Test": "<same-hex-key-as-in-server-config.json>",
}
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.
- `SERVER_PORT` / `USE_HTTPS` should match how your server is actually reachable. If nginx force-redirects port 80 to 443, set `SERVER_PORT = "443"` and `USE_HTTPS = True` directly rather than relying on the redirect — the updater downloads files one request at a time, so an extra 80→443 redirect on every file adds real overhead on large clients.
- `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.
- `BANNER_WIDTH` / `BANNER_HEIGHT` — the banner image is always scaled to exactly this size, so it doesn't matter what dimensions the source `banner.png` actually has; the layout won't shift.
Banner scaling uses [Pillow](https://pypi.org/project/Pillow/) if it's installed (`pip install Pillow`), for proper resizing. If Pillow isn't available, the updater falls back to Tkinter's built-in (cruder, integer-only) zoom/subsample — it'll still work, just less precisely scaled. Install Pillow for best results, and make sure it's bundled when compiling (see below).
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.
- The server never serves directory listings — a bare `/`, an unknown key, or a path that tries to escape the client folder (`..`) all get a 404, including the server's own script/config files.
- `updater.py` uses Tkinter for the UI, which ships with most standard Python installs on Windows.