init
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
# 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"
|
||||
SERVER_ADDRESS = "update.mygame.com" # domain or IP
|
||||
SERVER_PORT = "3232"
|
||||
USE_HTTPS = False
|
||||
|
||||
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.
|
||||
- `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.
|
||||
Reference in New Issue
Block a user