compression added

This commit is contained in:
2026-09-09 01:42:52 +03:00
parent 02435cb5a6
commit b184ea76f4
4 changed files with 234 additions and 19 deletions
+48 -2
View File
@@ -12,9 +12,9 @@ Works with any game — just fill in the configuration blocks at the top of each
## 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.
2. You run `generate_manifest.py` on that folder. It walks the folder, hashes every file (MD5), and writes a `manifest.json` listing metadata for every file. It also gzip-compresses every file into a `<file>.gz` copy next to the original — if the compressed copy isn't actually smaller (common for already-compressed formats like `.jpg`, `.mp3`, `.zip`), the `.gz` copy is discarded and the manifest just records the original.
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.
4. Players run `updater.exe`. It downloads the server's `manifest.json`, compares hashes against their local files, and for anything missing or different: downloads the `.gz` copy if one exists, verifies it, decompresses it locally, and writes the result — or downloads the original directly if there's no compressed copy.
5. Once everything matches, the "Play" button launches the game executable.
## Setup
@@ -105,6 +105,19 @@ pyinstaller --onefile --noconsole --icon=icon.ico --add-data "banner.png;." --ad
Ship the resulting `updater.exe` alongside the game folder.
## Settings
On first run, the updater creates a `settings.ini` file next to itself with the selected client version:
```ini
[updater]
client = Main
```
Whenever the player changes the client version dropdown, it's saved immediately so the choice persists across restarts. If `settings.ini` is missing, corrupted, or contains an unrecognized client name, the updater silently falls back to `DEFAULT_CLIENT` and continues — it never crashes over a bad settings file.
This is currently the only setting. To add more later, extend `load_settings()` / `save_settings()` in `updater.py` with additional keys under the same `[updater]` section.
## 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.
@@ -145,9 +158,42 @@ IGNORE_EXTENSIONS = {
Add any player-specific files (settings, saves, screenshots, logs) here so the updater never overwrites or deletes them.
## Compression
`generate_manifest.py` gzip-compresses every distributable file into a `<file>.gz` copy alongside the original, unconditionally — no file-type filtering. If the compressed copy ends up the same size or larger than the original (common for already-compressed formats), it's discarded automatically and that file is just served uncompressed; there's nothing to configure per file type.
The manifest records both the original file's hash/size and, when a compressed copy exists, its hash/size too:
```json
{
"script.txt": {
"hash": "d3650c70e7d942736c3ca77daf381262",
"size": 60000,
"compressed": true,
"compressed_size": 177,
"compressed_hash": "852ab2df07a4c65cddd6ed3ec0282301"
},
"texture.bin": {
"hash": "1b6d041423b9863dee54e87fed1f0cdd",
"size": 20000,
"compressed": false
}
}
```
`updater.py` uses this to download the smaller `.gz` copy when one is available, verifies its hash, decompresses it in memory, and verifies the decompressed result against the original hash before writing it to disk. If anything about the compressed copy fails (missing, corrupted, hash mismatch), it automatically falls back to downloading the uncompressed original — a bad `.gz` on the server never blocks an update.
Two settings control this, and must be kept in sync between `generate_manifest.py` and `updater.py`:
- `COMPRESSED_SUFFIX` (default `.gz`) — the suffix used for compressed copies.
- `COMPRESSION_LEVEL` (default `6`, in `generate_manifest.py` only) — gzip level 1 (fastest, larger) to 9 (slowest, smallest).
Re-run `generate_manifest.py` after any change to the client files — it regenerates both the manifest and the `.gz` copies. Compressed output is generated with a fixed mtime so re-running it without any real file changes produces byte-identical `.gz` files and stable hashes.
## Notes / limitations
- File integrity is checked with MD5 — fine for detecting changed files, not intended as a cryptographic guarantee.
- Compression roughly doubles the disk space needed on the server for each client version (original + `.gz` copies), in exchange for faster downloads for compressible files.
- 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.