From 02c20d904ff9acf6a60b8cc0a1031c07a6f009e9 Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Wed, 24 Dec 2025 20:47:37 +0530 Subject: [PATCH 01/29] Update README, server config, and ignore large assets --- .gitattributes | 2 + .gitignore | 6 + README.md | 175 +++++--- dist/game.js | 33 +- pixi.lock | 928 +++++++++++++++++++++++++++++++++++++++++++ pixi.toml | 19 + server.py | 189 +++++---- vcsky/sha256sums.txt | 143 +++++++ 8 files changed, 1343 insertions(+), 152 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 pixi.lock create mode 100644 pixi.toml create mode 100644 vcsky/sha256sums.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..997504b4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# SCM syntax highlighting & preventing 3-way merges +pixi.lock merge=binary linguist-language=YAML linguist-generated=true -diff diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d2a832c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# pixi environments +.pixi/* +!.pixi/config.toml +vcsky/fetched +vcbr/ +__pycache__ \ No newline at end of file diff --git a/README.md b/README.md index e75f3dec..7251178a 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,98 @@ Web-based port of GTA: Vice City running in browser via WebAssembly. +You can check which files I made changes in commit section. + +## History & Restoration + +This project is a community effort to preserve the incredible HTML5 port of GTA: Vice City. + +* **The Ban**: Rockstar Games issued a takedown for the original web port, shutting down the official servers and CDNs (`cdn.dos.zone`). +* **Deobfuscation**: The source code was deobfuscated to allow for self-hosting and study. +* **WebAssembly Magic**: Unlike standard PC mods (e.g., *Vice City: Reviced*), this port runs entirely in the browser using WebAssembly. It streams assets on-the-fly, making it playable on almost any device with a browser and keyboard, without installation. +* **Restoring Functionality**: After the shutdown, the game would hang on a black screen because it couldn't fetch core data files (`.wasm`, `.data`) or assets from the dead CDNs. This repository solves that by: + 1. Providing a server that serves these critical files locally. + 2. Implementing a caching system to download and save surviving assets. + 3. Removing dependencies on the defunct infrastructure. + +**Stability Test**: Watch the server in action here: [GTA VC Server Stability Test](https://www.youtube.com/watch?v=C8nK81N4iBs) + +## Quick Start + +1. **Clone the repository**: + ```bash + git clone --depth 1 https://github.com/Th3w33knd/reVCDOS + cd reVCDOS + ``` + +2. **Download Assets**: + > [!WARNING] + > These files contain copyrighted materials from Rockstar Games. You must own the original game to use them legally. + + * **vcbr** (Core Data): [Download](https://gofile.io/d/ceuXTa) (or [Older Version](https://gofile.io/d/U63PZO)) + * **vcsky** (Assets): [Download](https://gofile.io/d/9QsvMn) + + **Important**: Extract them so your folder structure looks exactly like this: + ```text + reVCDOS/ + ├── vcbr/ + │ ├── vc-sky-en-v6.data + │ ├── vc-sky-en-v6.wasm + │ ├── vc-sky-ru-v6.data + │ └── vc-sky-ru-v6.wasm + ├── vcsky/ + │ ├── sha256sums.txt + │ ├── fetched/ + │ │ ├── audio/ <-- From download + │ │ ├── data/ <-- From download + │ │ └── ... + │ └── ... + ├── server.py + └── ... + ``` + +3. **Install Pixi** (if not already installed): + * **Windows (PowerShell)**: + ```powershell + powershell -ExecutionPolicy Bypass -c "irm -useb https://pixi.sh/install.ps1 | iex" + ``` + * **Linux & macOS**: + ```bash + curl -fsSL https://pixi.sh/install.sh | bash + ``` + +3. **Start the game**: + * **Standard Mode**: + ```bash + pixi run start + ``` + * **Cheat Mode** (Opens browser with cheats enabled): + ```bash + pixi run cheat + ``` + +4. **Play**: Open the link shown in the terminal (usually `http://localhost:8000`). If you used `pixi run cheat`, the browser should open automatically. + ## Requirements - Python 3.8+ - Dependencies from `requirements.txt` - +- **Pixi** (Recommended for package management) ## Setup & Running -### Option 1: Using Docker (Recommended) +### Option 1: Using Pixi (Recommended) + +This project uses [Pixi](https://pixi.sh/) for dependency management and task running. + +1. **Install Pixi**: Follow the instructions at [pixi.sh](https://pixi.sh/). +2. **Start the server**: + ```bash + pixi run start + ``` + This command automatically installs dependencies and starts the server with the correct configuration. + +### Option 2: Using Docker The easiest way to get started is using Docker Compose: ```bash @@ -32,12 +115,8 @@ IN_PORT=3000 AUTH_LOGIN=admin AUTH_PASSWORD=secret CUSTOM_SAVES=1 docker compose | `AUTH_LOGIN` | HTTP Basic Auth username | | `AUTH_PASSWORD` | HTTP Basic Auth password | | `CUSTOM_SAVES` | Enable local saves (set to `1`) | -| `VCSKY_LOCAL` | Serve vcsky from local directory (set to `1`) | -| `VCBR_LOCAL` | Serve vcbr from local directory (set to `1`) | -| `VCSKY_URL` | Custom vcsky proxy URL | -| `VCBR_URL` | Custom vcbr proxy URL | -### Option 2: Local Installation +### Option 3: Local Installation (Manual) 1. Install Python dependencies: ```bash @@ -46,11 +125,26 @@ pip install -r requirements.txt 2. Start the server: ```bash -python server.py +python server.py --custom_saves ``` Server starts at `http://localhost:8000` +## Server Behavior & Caching + +The server (`server.py`) has been updated with a smart caching strategy to optimize performance and bandwidth. + +* **`vcbr` Resources (Core Game Data)**: + * These files **MUST** be present locally in the `vcbr/` directory. + * The server will *only* look for them locally. It does not download them from a CDN. + * Ensure you have the correct `.data` and `.wasm` files in `vcbr/`. + +* **`vcsky` Resources (Additional Assets)**: + * The server uses a **Cache-First** strategy. + * **Check Local**: It first checks if the requested file exists in the local `vcsky/` directory. + * **Download & Cache**: If the file is missing locally, it automatically downloads it from the CDN (`https://cdn.dos.zone/vcsky/`), saves it to the local `vcsky/` directory, and then serves it. + * **Serve**: Subsequent requests for the same file are served directly from the local disk. + ## Server Options | Option | Type | Default | Description | @@ -59,36 +153,21 @@ Server starts at `http://localhost:8000` | `--custom_saves` | flag | disabled | Enable local save files (saves router) | | `--login` | string | none | HTTP Basic Auth username | | `--password` | string | none | HTTP Basic Auth password | -| `--vcsky_local` | flag | disabled | Serve vcsky from local `vcsky/` directory | -| `--vcbr_local` | flag | disabled | Serve vcbr from local `vcbr/` directory | -| `--vcsky_url` | string | `https://cdn.dos.zone/vcsky/` | Custom vcsky proxy URL | -| `--vcbr_url` | string | `https://br.cdn.dos.zone/vcsky/` | Custom vcbr proxy URL | +| `--vcsky_local` | flag | enabled | (Legacy) Prioritize local `vcsky` files (now default behavior) | +| `--vcbr_local` | flag | enabled | (Legacy) Prioritize local `vcbr` files (now default behavior) | **Examples:** ```bash # Start on custom port python server.py --port 3000 -# Enable local saves +# Enable local saves (Recommended) python server.py --custom_saves # Enable HTTP Basic Authentication python server.py --login admin --password secret123 - -# Use local vcsky and vcbr files (offline mode) -python server.py --vcsky_local --vcbr_local - -# Use custom proxy URLs -python server.py --vcsky_url https://my-cdn.example.com/vcsky/ --vcbr_url https://my-cdn.example.com/vcbr/ - -# All options combined -python server.py --port 3000 --custom_saves --login admin --password secret123 --vcsky_local --vcbr_local ``` -> **Note:** HTTP Basic Auth is only enabled when both `--login` and `--password` are provided. - -> **Note:** By default, vcsky and vcbr are proxied from DOS Zone CDN. Use `--vcsky_local` and `--vcbr_local` flags to serve files from local directories instead. - ## URL Parameters | Parameter | Values | Description | @@ -103,46 +182,33 @@ python server.py --port 3000 --custom_saves --login admin --password secret123 - ## Project Structure ``` -├── server.py # FastAPI proxy server +├── server.py # FastAPI caching server +├── pixi.toml # Pixi project configuration ├── requirements.txt # Python dependencies ├── additions/ # Server extensions │ ├── auth.py # HTTP Basic Auth middleware │ └── saves.py # Local saves router ├── dist/ # Game client files │ ├── index.html # Main page -│ ├── game.js # Game loader +│ ├── game.js # Game loader (updated with ownership check) │ ├── index.js # Module loader │ ├── GamepadEmulator.js # Touch controls │ ├── idbfs.js # IndexedDB filesystem │ ├── jsdos-cloud-sdk.js # Cloud saves (DOS Zone) │ ├── jsdos-cloud-sdk-local.js # Local saves (--custom_saves) │ └── modules/ # WASM modules -│ ├── runtime.js # WASM runtime initialization -│ ├── loader.js # Asset/package loading -│ ├── fs.js # Virtual filesystem -│ ├── audio.js # Audio system -│ ├── graphics.js # Rendering pipeline -│ ├── events.js # Input events handling -│ ├── fetch.js # Network requests (Real-time asset streaming) -│ ├── syscalls.js # System calls -│ ├── main.js # Main entry point -│ ├── cheats.js # Cheat engine (F3) -│ ├── asm_consts/ # Language-specific ASM constants -│ │ ├── en.js -│ │ └── ru.js -│ └── packages/ # Language-specific data packages -│ ├── en.js -│ └── ru.js -├── vcbr/ # Brotli-compressed game data (optional) -│ ├── vc-sky-en-v6.data.br -│ ├── vc-sky-en-v6.wasm.br -│ ├── vc-sky-ru-v6.data.br -│ └── vc-sky-ru-v6.wasm.br -└── vcsky/ # Additional assets (optional) - ├── data/ - ├── audio/ - ├── models/ - └── anim/ +├── vcbr/ # Core game data (REQUIRED LOCALLY) +│ ├── vc-sky-en-v6.data +│ ├── vc-sky-en-v6.wasm +│ ├── vc-sky-ru-v6.data +│ └── vc-sky-ru-v6.wasm +└── vcsky/ # Additional assets (Cached locally on demand) + ├── sha256sums.txt + └── fetched/ # Downloaded assets + ├── data/ + ├── audio/ + ├── models/ + └── anim/ ``` ## Features @@ -153,6 +219,7 @@ python server.py --port 3000 --custom_saves --login admin --password secret123 - - 🌍 English/Russian language support - 🔧 Built-in cheat engine (memory scanner, cheats) - 📱 Mobile touch controls +- 🔒 **Original Game Verification**: You must provide an original game file to verify ownership and play the full version. ## Local Saves diff --git a/dist/game.js b/dist/game.js index 5c6a6620..9f794140 100644 --- a/dist/game.js +++ b/dist/game.js @@ -8,7 +8,7 @@ var wasm_content; const params = new URLSearchParams(window.location.search); // Base URLs -const replaceFetch = {from: "https://cdn.dos.zone/vcsky/", to: "/vcsky/"} +const replaceFetch = { from: "https://cdn.dos.zone/vcsky/", to: "/vcsky/" } const replaceBR = "/vcbr/" const autoFullScreen = true; @@ -48,7 +48,7 @@ let haveOriginalGame = false; downloading: "Downloading", enterKey: "enter your key", clickToContinue: "Click to continue...", - enterJsDosKey: "Enter js-dos key (5 len)", + enterJsDosKey: "Enter js-dos key", portBy: "HTML5 port by:", ruTranslate: "", demoOffDisclaimer: "Due to the unexpectedly high popularity of the project, resulting in significant traffic costs, and in order to avoid any risk of the project being shut down due to rights holder claims, we have disabled the demo version. You can still run the full version by providing the original game resources.", @@ -71,7 +71,7 @@ let haveOriginalGame = false; downloading: "Загрузка", enterKey: "введите ваш ключ", clickToContinue: "Нажмите для продолжения...", - enterJsDosKey: "Введите ключ js-dos (5 букв)", + enterJsDosKey: "Введите ключ js-dos", portBy: "Авторы HTML5 порта:", ruTranslate: `
@@ -86,9 +86,9 @@ let haveOriginalGame = false; let currentLanguage = navigator.language.split("-")[0] === "ru" ? "ru" : "en"; if (params.get("lang") === "ru") { currentLanguage = "ru"; - // } - // if (params.get("lang") === "en") { - }else{ + // } + // if (params.get("lang") === "en") { + } else { currentLanguage = "en"; } @@ -98,15 +98,15 @@ let haveOriginalGame = false; })(); if (params.get('lang') === 'ru') { - data_content = `${replaceBR}vc-sky-ru-v6.data.br`; - wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; -// } else if (params.get('lang') === 'ru') { -// data_content = `${replaceBR}vc-sky-ru-v6.data.br`; -// wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; + data_content = `${replaceBR}vc-sky-ru-v6.data`; + wasm_content = `${replaceBR}vc-sky-ru-v6.wasm`; + // } else if (params.get('lang') === 'ru') { + // data_content = `${replaceBR}vc-sky-ru-v6.data.br`; + // wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; } else { - data_content = `${replaceBR}vc-sky-en-v6.data.br`; - wasm_content = `${replaceBR}vc-sky-en-v6.wasm.br`; -// data_content = "index.old.data"; + data_content = `${replaceBR}vc-sky-en-v6.data`; + wasm_content = `${replaceBR}vc-sky-en-v6.wasm`; + // data_content = "index.old.data"; } // data_content = `${replaceBR}vc-sky-ru-v6.data.br`; // wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; @@ -213,6 +213,7 @@ function setStatus(text) { async function loadGame(data) { var Module = { + ALLOW_MEMORY_GROWTH: 1, mainCalled: () => { try { Module.FS.unlink("/vc-assets/local/revc.ini"); @@ -521,7 +522,7 @@ const clickToPlayButton = document.getElementById('click-to-play-button'); clickToPlayButton.textContent = t('clickToPlayDemo'); clickToPlayButton.classList.add('disabled'); const demoOffDisclaimer = document.getElementById('demo-off-disclaimer'); -demoOffDisclaimer.textContent = "* " +t('demoOffDisclaimer'); +demoOffDisclaimer.textContent = "* " + t('demoOffDisclaimer'); const cloudSavesLink = document.getElementById('cloud-saves-link'); cloudSavesLink.textContent = t('cloudSaves'); cloudSavesStatus.textContent = t('enterKey'); @@ -554,7 +555,7 @@ function ownerShipNotConfirmed() { localStorage.removeItem('vcsky.haveOriginalGame'); disclaimerCheckbox.checked = false; clickToPlayButton.textContent = t('clickToPlayDemo'); - demoOffDisclaimer.textContent = "* " +t('demoOffDisclaimer'); + demoOffDisclaimer.textContent = "* " + t('demoOffDisclaimer'); haveOriginalGame = false; clickToPlayButton.classList.add('disabled'); }; diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 00000000..2078dcba --- /dev/null +++ b/pixi.lock @@ -0,0 +1,928 @@ +version: 6 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + options: + pypi-prerelease-mode: if-necessary-or-explicit + packages: + win-64: + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyha7b4d00_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.3.0-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.127.0-hdf53b3f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.20-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-core-0.127.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/httptools-0.7.1-py312he06e257_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py312h05f76fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.5-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.41.5-py312hdabe01f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-extra-types-2.10.6-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.12.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.12-h0159041_1_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.21-pyh332efcf_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.17.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.50.0-pyhfdc7a7d_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.20.1-pyhd0b5f5c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.20.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.20.1-h058c98f_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.40.0-pyh6dadd2b_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.40.0-hcfb189c_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_33.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/watchfiles-1.1.1-py312hb0142fd_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/websockets-15.0.1-py312he5662c2_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda +packages: +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda + sha256: cc9fbc50d4ee7ee04e49ee119243e6f1765750f0fd0b4d270d5ef35461b643b1 + md5: 52be5139047efadaeeb19c6a5103f92a + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + size: 14222 + timestamp: 1762868213144 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + size: 18074 + timestamp: 1733247158254 +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda + sha256: 830fc81970cd9d19869909b9b16d241f4d557e4f201a1030aa6ed87c6aa8b930 + md5: 9958d4a1ee7e9c768fe8f4fb51bd07ea + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.10 + - typing_extensions >=4.5 + - python + constrains: + - trio >=0.32.0 + - uvloop >=0.21 + license: MIT + license_family: MIT + size: 144702 + timestamp: 1764375386926 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda + sha256: a4fffdf1c9b9d3d0d787e20c724cff3a284dfa3773f9ce609c93b1cfd0ce8933 + md5: bc58fdbced45bb096364de0fba1637af + depends: + - brotli-bin 1.2.0 hfd05255_1 + - libbrotlidec 1.2.0 hfd05255_1 + - libbrotlienc 1.2.0 hfd05255_1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 20342 + timestamp: 1764017988883 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda + sha256: e76966232ef9612de33c2087e3c92c2dc42ea5f300050735a3c646f33bce0429 + md5: 6abd7089eb3f0c790235fe469558d190 + depends: + - libbrotlidec 1.2.0 hfd05255_1 + - libbrotlienc 1.2.0 hfd05255_1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 22714 + timestamp: 1764017952449 +- conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda + sha256: d882712855624641f48aa9dc3f5feea2ed6b4e6004585d3616386a18186fe692 + md5: 1077e9333c41ff0be8edd1a5ec0ddace + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: bzip2-1.0.6 + license_family: BSD + size: 55977 + timestamp: 1757437738856 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda + sha256: 686a13bd2d4024fc99a22c1e0e68a7356af3ed3304a8d3ff6bb56249ad4e82f0 + md5: f98fb7db808b94bc1ec5b0e62f9f1069 + depends: + - __win + license: ISC + size: 152827 + timestamp: 1762967310929 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + sha256: 083a2bdad892ccf02b352ecab38ee86c3e610ba9a4b11b073ea769d55a115d32 + md5: 96a02a5c1a65470a7e4eedb644c872fd + depends: + - python >=3.10 + license: ISC + size: 157131 + timestamp: 1762976260320 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyha7b4d00_1.conda + sha256: c3bc9a49930fa1c3383a1485948b914823290efac859a2587ca57a270a652e08 + md5: 6cd3ccc98bacfcc92b2bd7f236f01a7e + depends: + - python >=3.10 + - colorama + - __win + - python + license: BSD-3-Clause + license_family: BSD + size: 96620 + timestamp: 1764518654675 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 +- conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.8.0-pyhcf101f3_0.conda + sha256: ef1e7b8405997ed3d6e2b6722bd7088d4a8adf215e7c88335582e65651fb4e05 + md5: d73fdc05f10693b518f52c994d748c19 + depends: + - python >=3.10,<4.0.0 + - sniffio + - python + constrains: + - aioquic >=1.2.0 + - cryptography >=45 + - httpcore >=1.0.0 + - httpx >=0.28.0 + - h2 >=4.2.0 + - idna >=3.10 + - trio >=0.30 + - wmi >=1.5.1 + license: ISC + size: 196500 + timestamp: 1757292856922 +- conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda + sha256: c37320864c35ef996b0e02e289df6ee89582d6c8e233e18dc9983375803c46bb + md5: 3bc0ac31178387e8ed34094d9481bfe8 + depends: + - dnspython >=2.0.0 + - idna >=2.0.0 + - python >=3.10 + license: Unlicense + size: 46767 + timestamp: 1756221480106 +- conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.3.0-hd8ed1ab_0.conda + sha256: 6a518e00d040fcad016fb2dde29672aa3476cd9ae33ea5b7b257222e66037d89 + md5: 2452e434747a6b742adc5045f2182a8e + depends: + - email-validator >=2.3.0,<2.3.1.0a0 + license: Unlicense + size: 7077 + timestamp: 1756221480651 +- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda + sha256: ee6cf346d017d954255bbcbdb424cddea4d14e4ed7e9813e429db1d795d01144 + md5: 8e662bd460bda79b1ea39194e3c4c9ab + depends: + - python >=3.10 + - typing_extensions >=4.6.0 + license: MIT and PSF-2.0 + size: 21333 + timestamp: 1763918099466 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.127.0-hdf53b3f_0.conda + sha256: 77147d9ddda0800add14b5abe8d77c115b6b3e958cbb15bee95b55fd5651e00b + md5: 1678dba10f7e88544accb358457a5b12 + depends: + - fastapi-core ==0.127.0 pyhcf101f3_0 + - email_validator + - fastapi-cli + - httpx + - jinja2 + - pydantic-settings + - pydantic-extra-types + - python-multipart + - uvicorn-standard + license: MIT + size: 4803 + timestamp: 1766347289226 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.20-pyhcf101f3_0.conda + sha256: 284cae62b2061a9f423b468f720deeff98783eccff6bf3b32965afb21a53e349 + md5: e2b464522fa49c5948c4da6c8d8ea9b3 + depends: + - python >=3.10 + - rich-toolkit >=0.14.8 + - tomli >=2.0.0 + - typer >=0.15.1 + - uvicorn-standard >=0.15.0 + - python + license: MIT + size: 18993 + timestamp: 1766435117562 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-core-0.127.0-pyhcf101f3_0.conda + sha256: 30ae43ffefee4dbea1d7154fe5089604bbb1fd64993d3d6ed0ea3e5f2168143f + md5: cc48c785ff6a6afb136f60c14a89cf44 + depends: + - python >=3.10 + - annotated-doc >=0.0.2 + - starlette >=0.40.0,<0.51.0 + - typing_extensions >=4.8.0 + - pydantic >=2.7.0 + - python + constrains: + - email_validator >=2.0.0 + - fastapi-cli >=0.0.8 + - httpx >=0.23.0,<1.0.0 + - jinja2 >=3.1.5 + - pydantic-extra-types >=2.0.0 + - pydantic-settings >=2.0.0 + - python-multipart >=0.0.18 + - uvicorn-standard >=0.12.0 + license: MIT + size: 89110 + timestamp: 1766347289224 +- conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + sha256: f64b68148c478c3bfc8f8d519541de7d2616bf59d44485a5271041d40c061887 + md5: 4b69232755285701bc86a5afe4d9933a + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + size: 37697 + timestamp: 1745526482242 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + sha256: 84c64443368f84b600bfecc529a1194a3b14c3656ee2e832d15a20e0329b6da3 + md5: 164fc43f0b53b6e3a7bc7dce5e4f1dc9 + depends: + - python >=3.10 + - hyperframe >=6.1,<7 + - hpack >=4.1,<5 + - python + license: MIT + license_family: MIT + size: 95967 + timestamp: 1756364871835 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba + md5: 0a802cb9888dd14eeefc611f05c40b6e + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 30731 + timestamp: 1737618390337 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + sha256: 04d49cb3c42714ce533a8553986e1642d0549a05dc5cc48e0d43ff5be6679a5b + md5: 4f14640d58e2cc0aa0819d9d8ba125bb + depends: + - python >=3.9 + - h11 >=0.16 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=4.0,<5.0 + - certifi + - python + license: BSD-3-Clause + license_family: BSD + size: 49483 + timestamp: 1745602916758 +- conda: https://conda.anaconda.org/conda-forge/win-64/httptools-0.7.1-py312he06e257_1.conda + sha256: 4fa4e6d0abdb9016d918972b09044da768353ba29fb5d4419cb4b9220b50c80d + md5: b047a840c8eed126a47a8dd144ee8f38 + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 75736 + timestamp: 1762504249570 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 63082 + timestamp: 1733663449209 +- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 + md5: 8e6923fc12f1fe8f8c4e5c9f343256ac + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 17397 + timestamp: 1737618427549 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + sha256: ae89d0299ada2a3162c2614a9d26557a92aa6a77120ce142f8e0109bbf0342b0 + md5: 53abe63df7e10a6ba605dc5f9f961d36 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + size: 50721 + timestamp: 1760286526795 +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + sha256: fc9ca7348a4f25fed2079f2153ecdcf5f9cf2a0bc36c4172420ca09e1849df7b + md5: 04558c96691bed63104678757beb4f8d + depends: + - markupsafe >=2.0 + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + size: 120685 + timestamp: 1764517220861 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda + sha256: 5097303c2fc8ebf9f9ea9731520aa5ce4847d0be41764edd7f6dee2100b82986 + md5: 444b0a45bbd1cb24f82eedb56721b9c4 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 82042 + timestamp: 1764017799966 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda + sha256: 3239ce545cf1c32af6fffb7fc7c75cb1ef5b6ea8221c66c85416bb2d46f5cccb + md5: 450e3ae947fc46b60f1d8f8f318b40d4 + depends: + - libbrotlicommon 1.2.0 hfd05255_1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 34449 + timestamp: 1764017851337 +- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda + sha256: 3226df6b7df98734440739f75527d585d42ca2bfe912fbe8d1954c512f75341a + md5: ccd93cfa8e54fd9df4e83dbe55ff6e8c + depends: + - libbrotlicommon 1.2.0 hfd05255_1 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 252903 + timestamp: 1764017901735 +- conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda + sha256: 844ab708594bdfbd7b35e1a67c379861bcd180d6efe57b654f482ae2f7f5c21e + md5: 8c9e4f1a0e688eef2e95711178061a0f + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - expat 2.7.3.* + license: MIT + license_family: MIT + size: 70137 + timestamp: 1763550049107 +- conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda + sha256: ddff25aaa4f0aa535413f5d831b04073789522890a4d8626366e43ecde1534a3 + md5: ba4ad812d2afc22b9a34ce8327a0930f + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 44866 + timestamp: 1760295760649 +- conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda + sha256: 55764956eb9179b98de7cc0e55696f2eff8f7b83fc3ebff5e696ca358bca28cc + md5: c15148b2e18da456f5108ccb5e411446 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + constrains: + - xz 5.8.1.* + license: 0BSD + size: 104935 + timestamp: 1749230611612 +- conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.conda + sha256: d6d86715a1afe11f626b7509935e9d2e14a4946632c0ac474526e20fc6c55f99 + md5: be65be5f758709fc01b01626152e96b0 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: blessing + size: 1292859 + timestamp: 1766319616777 +- conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda + sha256: ba945c6493449bed0e6e29883c4943817f7c79cbff52b83360f7b341277c6402 + md5: 41fbfac52c601159df6c01f875de31b9 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 55476 + timestamp: 1727963768015 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + sha256: 7b1da4b5c40385791dbc3cc85ceea9fad5da680a27d5d3cb8bfaa185e304a89e + md5: 5b5203189eb668f042ac2b0826244964 + depends: + - mdurl >=0.1,<1 + - python >=3.10 + license: MIT + license_family: MIT + size: 64736 + timestamp: 1754951288511 +- conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py312h05f76fc_0.conda + sha256: db1d772015ef052fedb3b4e7155b13446b49431a0f8c54c56ca6f82e1d4e258f + md5: 9a50d5e7b4f2bf5db9790bbe9421cdf8 + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + size: 28388 + timestamp: 1759055474173 +- conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14465 + timestamp: 1733255681319 +- conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda + sha256: 6d72d6f766293d4f2aa60c28c244c8efed6946c430814175f959ffe8cab899b3 + md5: 84f8fb4afd1157f59098f618cd2437e4 + depends: + - ca-certificates + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + size: 9440812 + timestamp: 1762841722179 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.5-pyhcf101f3_1.conda + sha256: 868569d9505b7fe246c880c11e2c44924d7613a8cdcc1f6ef85d5375e892f13d + md5: c3946ed24acdb28db1b5d63321dbca7d + depends: + - typing-inspection >=0.4.2 + - typing_extensions >=4.14.1 + - python >=3.10 + - typing-extensions >=4.6.1 + - annotated-types >=0.6.0 + - pydantic-core ==2.41.5 + - python + license: MIT + license_family: MIT + size: 340482 + timestamp: 1764434463101 +- conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.41.5-py312hdabe01f_1.conda + sha256: 06f5d122ac1c29679a6d588aa066c8684a087de12f84f3e81d90c205664eb62c + md5: 2e338a10e31828590cf031076bb143b6 + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.12.* *_cp312 + license: MIT + license_family: MIT + size: 1970249 + timestamp: 1762989032818 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-extra-types-2.10.6-pyh3cfb1c2_0.conda + sha256: 0a03f1e0771be4bcc5b174b1b45453127d3cf006ab5801fb457d1b7b9421d1ad + md5: c60c737e23715462044d9dba67fdf10c + depends: + - pydantic >=2.5.2 + - python >=3.10 + constrains: + - pycountry >=23 + - phonenumbers >=8,<9 + - pendulum >=3.0.0,<4.0.0 + - pytz >=2024.1 + - semver >=3.0.2,<4 + - tzdata >=2024a + - python-ulid >=1,<3 + license: MIT + license_family: MIT + size: 34430 + timestamp: 1759937803985 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.12.0-pyh3cfb1c2_0.conda + sha256: 17d552dd19501909d626ff50cd23753d56e03ab670ce9096f1c4068e1eb90f2a + md5: 0a3042ce18b785982c64a8567cc3e512 + depends: + - pydantic >=2.7.0 + - python >=3.10 + - python-dotenv >=0.21.0 + - typing-inspection >=0.4.0 + license: MIT + license_family: MIT + size: 43752 + timestamp: 1762786342653 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + sha256: 5577623b9f6685ece2697c6eb7511b4c9ac5fb607c9babc2646c811b428fd46a + md5: 6b6ece66ebcae2d5f326c77ef2c5a066 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 889287 + timestamp: 1750615908735 +- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.12-h0159041_1_cpython.conda + build_number: 1 + sha256: 9b163b0426c92eee1881d5c838e230a750a3fa372092db494772886ab91c2548 + md5: 42ae551e4c15837a582bea63412dc0b4 + depends: + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.1,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - liblzma >=5.8.1,<6.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - python_abi 3.12.* *_cp312 + license: Python-2.0 + size: 15883484 + timestamp: 1761175152489 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + sha256: aa98e0b1f5472161318f93224f1cfec1355ff69d2f79f896c0b9e033e4a6caf9 + md5: 083725d6cd3dc007f06d04bcf1e613a2 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + size: 26922 + timestamp: 1761503229008 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.21-pyh332efcf_0.conda + sha256: 679ead525c7a7d4f16baa425b312248061ab76f168412cd2d1c162722b9cb587 + md5: c087c0029ffe0d4dc4c87ab38634fac0 + depends: + - python >=3.10 + license: Apache-2.0 + license_family: Apache + size: 28483 + timestamp: 1765965605555 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + build_number: 8 + sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 + md5: c3efd25ac4d74b1584d2f7a57195ddf1 + constrains: + - python 3.12.* *_cpython + license: BSD-3-Clause + license_family: BSD + size: 6958 + timestamp: 1752805918820 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_0.conda + sha256: 54d04e61d17edffeba1e5cad45f10f272a016b6feec1fa8fa6af364d84a7b4fc + md5: 4a68f80fbf85499f093101cc17ffbab7 + depends: + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + size: 180635 + timestamp: 1758891847871 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda + sha256: edfb44d0b6468a8dfced728534c755101f06f1a9870a7ad329ec51389f16b086 + md5: a247579d8a59931091b16a1e932bbed6 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.10 + - typing_extensions >=4.0.0,<5.0.0 + - python + license: MIT + license_family: MIT + size: 200840 + timestamp: 1760026188268 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.17.1-pyhcf101f3_0.conda + sha256: 95d4361bf603164b6782932340daa23192c26314eb7cbcac56fe85a63f14510e + md5: 94ef593007f79f05af94219888147f50 + depends: + - python >=3.10 + - rich >=13.7.1 + - click >=8.1.7 + - typing_extensions >=4.12.2 + - python + license: MIT + license_family: MIT + size: 31034 + timestamp: 1765985144059 +- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda + sha256: 1d6534df8e7924d9087bd388fbac5bd868c5bf8971c36885f9f016da0657d22b + md5: 83ea3a2ddb7a75c1b09cea582aa4f106 + depends: + - python >=3.10 + license: MIT + license_family: MIT + size: 15018 + timestamp: 1762858315311 +- conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + sha256: dce518f45e24cd03f401cb0616917773159a210c19d601c5f2d4e0e5879d30ad + md5: 03fe290994c5e4ec17293cfb6bdce520 + depends: + - python >=3.10 + license: Apache-2.0 + license_family: Apache + size: 15698 + timestamp: 1762941572482 +- conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.50.0-pyhfdc7a7d_0.conda + sha256: ab9ab67faa3cf12f45f5ced316e2c50dc72b4046cd275612fae756fe9d4cf82c + md5: 68bcb398c375177cf117cf608c274f9d + depends: + - anyio >=3.6.2,<5 + - python >=3.10 + - typing_extensions >=4.10.0 + - python + license: BSD-3-Clause + license_family: BSD + size: 64760 + timestamp: 1762016292582 +- conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda + sha256: 4581f4ffb432fefa1ac4f85c5682cc27014bcd66e7beaa0ee330e927a7858790 + md5: 7cb36e506a7dba4817970f8adb6396f9 + depends: + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: TCL + license_family: BSD + size: 3472313 + timestamp: 1763055164278 +- conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda + sha256: cb77c660b646c00a48ef942a9e1721ee46e90230c7c570cdeb5a893b5cce9bff + md5: d2732eb636c264dc9aa4cbee404b1a53 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + size: 20973 + timestamp: 1760014679845 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.20.1-pyhd0b5f5c_0.conda + sha256: 478396f445c0387addb75d5fe0ce85910c8d2fec4cec4b1c1ab85c50c5b1b64e + md5: 44582b13b4e5cfe2e4afe91e8f39fc48 + depends: + - typer-slim-standard ==0.20.1 h058c98f_0 + - python >=3.10 + - python + license: MIT + size: 79811 + timestamp: 1766174446628 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.20.1-pyhcf101f3_0.conda + sha256: 4a54079e0725595f9fd0bfd288ea73d547fea4d01361dda8e6a00bf872a76299 + md5: 28c060dea221d570c4e246708573f8a3 + depends: + - python >=3.10 + - click >=8.0.0 + - typing_extensions >=3.7.4.3 + - python + constrains: + - typer 0.20.1.* + - rich >=10.11.0 + - shellingham >=1.3.0 + license: MIT + size: 47918 + timestamp: 1766174446623 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.20.1-h058c98f_0.conda + sha256: e50e784e7a5eed6a33bc2daa6bc5115fb6bf994ad33e45f8306e6d549c0bb5fc + md5: 674a9e95fa013a1e3df79e4f1960065f + depends: + - typer-slim ==0.20.1 pyhcf101f3_0 + - rich + - shellingham + license: MIT + size: 5322 + timestamp: 1766174446628 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c + md5: edd329d7d3a4ab45dcf905899a7a6115 + depends: + - typing_extensions ==4.15.0 pyhcf101f3_0 + license: PSF-2.0 + license_family: PSF + size: 91383 + timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_1.conda + sha256: 70db27de58a97aeb7ba7448366c9853f91b21137492e0b4430251a1870aa8ff4 + md5: a0a4a3035667fc34f29bfbd5c190baa6 + depends: + - python >=3.10 + - typing_extensions >=4.12.0 + license: MIT + license_family: MIT + size: 18923 + timestamp: 1764158430324 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 + md5: 0caa1af407ecff61170c9437a808404d + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + size: 51692 + timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.conda + sha256: 50fad5db6734d1bb73df1cf5db73215e326413d4b2137933f70708aa1840e25b + md5: 338201218b54cadff2e774ac27733990 + license: LicenseRef-Public-Domain + size: 119204 + timestamp: 1765745742795 +- conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda + sha256: 3005729dce6f3d3f5ec91dfc49fc75a0095f9cd23bab49efb899657297ac91a5 + md5: 71b24316859acd00bdb8b38f5e2ce328 + constrains: + - vc14_runtime >=14.29.30037 + - vs2015_runtime >=14.29.30037 + license: LicenseRef-MicrosoftWindowsSDK10 + size: 694692 + timestamp: 1756385147981 +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.40.0-pyh6dadd2b_0.conda + sha256: 10a53144032ab6671c9a4f4f801448093cd2c4b73d40d13a00837a18824d602e + md5: 518f06ec23263844563845074bd619f3 + depends: + - __win + - click >=7.0 + - h11 >=0.8 + - python >=3.10 + - typing_extensions >=4.0 + - python + license: BSD-3-Clause + size: 53657 + timestamp: 1766332935113 +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.40.0-hcfb189c_0.conda + sha256: d4e1dfb669277498e4731502bfab1bbe2bb5a701a735b559723a60bfce0c5b4e + md5: db6123b8bd102ba091cc774d74a00bd2 + depends: + - __win + - uvicorn ==0.40.0 pyh6dadd2b_0 + - websockets >=10.4 + - httptools >=0.6.3 + - watchfiles >=0.13 + - python-dotenv >=0.13 + - pyyaml >=5.1 + - colorama >=0.4 + license: BSD-3-Clause + size: 4175 + timestamp: 1766332935118 +- conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_33.conda + sha256: 7036945b5fff304064108c22cbc1bb30e7536363782b0456681ee6cf209138bd + md5: 2d1c042360c09498891809a3765261be + depends: + - vc14_runtime >=14.42.34433 + track_features: + - vc14 + license: BSD-3-Clause + license_family: BSD + size: 19070 + timestamp: 1765216452130 +- conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda + sha256: 7e8f7da25d7ce975bbe7d7e6d6e899bf1f253e524a3427cc135a79f3a79c457c + md5: fb8e4914c5ad1c71b3c519621e1df7b8 + depends: + - ucrt >=10.0.20348.0 + - vcomp14 14.44.35208 h818238b_33 + constrains: + - vs2015_runtime 14.44.35208.* *_33 + license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime + license_family: Proprietary + size: 684323 + timestamp: 1765216366832 +- conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.conda + sha256: f79edd878094e86af2b2bc1455b0a81e02839a784fb093d5996ad4cf7b810101 + md5: 4cb6942b4bd846e51b4849f4a93c7e6d + depends: + - ucrt >=10.0.20348.0 + constrains: + - vs2015_runtime 14.44.35208.* *_33 + license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime + license_family: Proprietary + size: 115073 + timestamp: 1765216325898 +- conda: https://conda.anaconda.org/conda-forge/win-64/watchfiles-1.1.1-py312hb0142fd_0.conda + sha256: 5333e9a859c2e2c233b3fe9797e644d4b7eb88d2f12be4d9aa313fb491a3684e + md5: ccad8991c8fe2f56362e7294a6a0b131 + depends: + - anyio >=3.0.0 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + size: 303368 + timestamp: 1760457029394 +- conda: https://conda.anaconda.org/conda-forge/win-64/websockets-15.0.1-py312he5662c2_2.conda + sha256: 0602be65ad87b9e0af926b1dc1b9f5796eb94007c06efb69b8b2b773a23f8426 + md5: 782b3611b3b26550b558b458305a6db3 + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.12.* *_cp312 + license: BSD-3-Clause + license_family: BSD + size: 412013 + timestamp: 1756476363212 +- conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda + sha256: 80ee68c1e7683a35295232ea79bcc87279d31ffeda04a1665efdb43cbd50a309 + md5: 433699cba6602098ae8957a323da2664 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + size: 63944 + timestamp: 1753484092156 diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 00000000..9a1fd9c6 --- /dev/null +++ b/pixi.toml @@ -0,0 +1,19 @@ +[workspace] +authors = ["Th3W33kndXO "] +channels = ["conda-forge"] +name = "GTA VC Server" +platforms = ["win-64"] +version = "0.1.0" + +[tasks] +local = "python server.py --custom_saves" +start = "python server.py --vcsky_local --custom_saves" +cheat = "python server.py --vcsky_local --custom_saves --cheats --open" + +[dependencies] +python = "3.12.*" +fastapi = ">=0.127.0,<0.128" +uvicorn = ">=0.40.0,<0.41" +httpx = ">=0.28.1,<0.29" +brotli = ">=1.2.0,<2" +python-multipart = ">=0.0.21,<0.0.22" diff --git a/server.py b/server.py index 10bcd71f..e9fd10ee 100644 --- a/server.py +++ b/server.py @@ -1,22 +1,28 @@ import os import argparse -from fastapi import FastAPI, Request, HTTPException -from fastapi.responses import StreamingResponse, FileResponse, Response -from fastapi.staticfiles import StaticFiles -from starlette.background import BackgroundTask import httpx -import additions.saves as saves +import shutil +from fastapi import FastAPI, Response +from fastapi.responses import FileResponse +from fastapi.staticfiles import StaticFiles from additions.auth import BasicAuthMiddleware +import additions.saves as saves + +# --- CONFIGURATION --- +CDN_VCSKY = "https://cdn.dos.zone/vcsky/" +DIR_VCBR = "vcbr" +DIR_VCSKY = "vcsky" parser = argparse.ArgumentParser() parser.add_argument("--port", type=int, default=8000) parser.add_argument("--custom_saves", action="store_true") parser.add_argument("--login", type=str) parser.add_argument("--password", type=str) -parser.add_argument("--vcsky_local", action="store_true", help="Serve vcsky from local directory instead of proxy") -parser.add_argument("--vcbr_local", action="store_true", help="Serve vcbr from local directory instead of proxy") -parser.add_argument("--vcsky_url", type=str, default="https://cdn.dos.zone/vcsky/", help="Custom vcsky proxy URL") -parser.add_argument("--vcbr_url", type=str, default="https://br.cdn.dos.zone/vcsky/", help="Custom vcbr proxy URL") +# Defaulting to True to prioritize local files +parser.add_argument("--vcsky_local", action="store_true", default=True) +parser.add_argument("--vcbr_local", action="store_true", default=True) +parser.add_argument("--cheats", action="store_true", help="Enable cheats in URL") +parser.add_argument("--open", action="store_true", help="Open browser on start") args = parser.parse_args() app = FastAPI() @@ -27,86 +33,94 @@ if args.custom_saves: app.include_router(saves.router) -VCSKY_BASE_URL = args.vcsky_url -VCBR_BASE_URL = args.vcbr_url - -def request_to_url(request: Request, path: str, base_url: str): - query_string = str(request.url.query) if request.url.query else "" - url = f"{base_url}{path}" - if query_string: - url = f"{url}?{query_string}" - return url - -async def _proxy_request(request: Request, url: str): - client = httpx.AsyncClient(timeout=None) - headers = {k: v for k, v in request.headers.items() if k.lower() not in ["host", "content-length"]} - - req = client.build_request(request.method, url, headers=headers) - r = await client.send(req, stream=True) - - excluded_headers = {"content-length", "transfer-encoding", "connection", "keep-alive", "upgrade", "content-encoding", "x-content-encoding"} - response_headers = {k: v for k, v in r.headers.items() if k.lower() not in excluded_headers} - - response_headers["Cross-Origin-Opener-Policy"] = "same-origin" - response_headers["Cross-Origin-Embedder-Policy"] = "require-corp" - - return StreamingResponse( - r.aiter_bytes(), - status_code=r.status_code, - headers=response_headers, - background=BackgroundTask(client.aclose) - ) - -# vcsky routes - either local or proxy -if args.vcsky_local: - app.mount("/vcsky", StaticFiles(directory="vcsky"), name="vcsky") -else: - @app.api_route("/vcsky/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]) - async def vc_sky_proxy(request: Request, path: str): - return await _proxy_request(request, request_to_url(request, path, VCSKY_BASE_URL)) - -# vcbr routes - either local or proxy -if args.vcbr_local: - @app.get("/vcbr/{file_path:path}") - async def serve_vcbr_local(file_path: str): - file_location = os.path.join("vcbr", file_path) - if not os.path.isfile(file_location): - raise HTTPException(status_code=404, detail="File not found") - - headers = { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp" - } - - media_type = "application/octet-stream" - if file_path.endswith(".wasm.br"): - media_type = "application/wasm" - headers["Content-Encoding"] = "br" - elif file_path.endswith(".data.br"): - media_type = "application/octet-stream" - headers["Content-Encoding"] = "br" - elif file_path.endswith(".wasm"): - media_type = "application/wasm" - - return FileResponse(file_location, media_type=media_type, headers=headers) -else: - @app.api_route("/vcbr/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]) - async def vc_br_proxy(request: Request, path: str): - return await _proxy_request(request, request_to_url(request, path, VCBR_BASE_URL)) +# Ensure directories +os.makedirs(DIR_VCBR, exist_ok=True) +os.makedirs(DIR_VCSKY, exist_ok=True) + +def serve_file(path): + """Helper to serve files with correct headers""" + media_type = "application/octet-stream" + if path.endswith(".wasm"): media_type = "application/wasm" + elif path.endswith(".html"): media_type = "text/html" + elif path.endswith(".js"): media_type = "application/javascript" + elif path.endswith(".css"): media_type = "text/css" + elif path.endswith(".mp3"): media_type = "audio/mpeg" + + headers = { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + "Cache-Control": "no-cache" # Force browser to check server so we can fill cache + } + if path.endswith(".br"): headers["Content-Encoding"] = "br" + + return FileResponse(path, media_type=media_type, headers=headers) + +async def fetch_and_cache(rel_path, local_root, remote_base): + # Sanitize path + clean_path = rel_path.lstrip("/") + local_path = os.path.join(local_root, clean_path) + + # 1. SERVE LOCAL IF EXISTS + if os.path.exists(local_path): + # Don't log every tiny hit to keep console clean + if not clean_path.endswith(".dff") and not clean_path.endswith(".txd"): + print(f"[{local_root.upper()}] HIT: {clean_path}") + return serve_file(local_path) + + # 2. DOWNLOAD IF MISSING + print(f"[{local_root.upper()}] MISS: {clean_path} -> Downloading...") + + remote_url = f"{remote_base}{clean_path}" + temp_path = local_path + ".tmp" + + try: + os.makedirs(os.path.dirname(local_path), exist_ok=True) + + async with httpx.AsyncClient() as client: + async with client.stream("GET", remote_url) as r: + if r.status_code == 200: + with open(temp_path, "wb") as f: + async for chunk in r.aiter_bytes(): + f.write(chunk) + + # Atomic move: Only rename if download finished successfully + shutil.move(temp_path, local_path) + print(f"[{local_root.upper()}] SAVED: {clean_path}") + return serve_file(local_path) + else: + print(f"[{local_root.upper()}] 404 Remote: {remote_url}") + return Response(content="Not Found", status_code=404) + except Exception as e: + # Cleanup temp file if error + if os.path.exists(temp_path): + os.remove(temp_path) + print(f"Error fetching {clean_path}: {e}") + return Response(content="Server Error", status_code=500) + +@app.get("/vcbr/{path:path}") +async def route_vcbr(path: str): + # VCBR is strictly local now as you have the files + local_path = os.path.join(DIR_VCBR, path) + if os.path.exists(local_path): + return serve_file(local_path) + return Response(status_code=404) + +@app.get("/vcsky/{path:path}") +async def route_vcsky(path: str): + if path == "sha256sums.txt": + return serve_file(os.path.join(DIR_VCSKY, path)) + return await fetch_and_cache(path, os.path.join(DIR_VCSKY, "fetched"), CDN_VCSKY) @app.get("/") async def read_index(): if os.path.exists("dist/index.html"): with open("dist/index.html", "r", encoding="utf-8") as f: content = f.read() - - # Inject custom_saves status custom_saves_val = "1" if args.custom_saves else "0" content = content.replace( 'new URLSearchParams(window.location.search).get("custom_saves") === "1"', f'"{custom_saves_val}" === "1"' ) - return Response(content, media_type="text/html", headers={ "Cross-Origin-Opener-Policy": "same-origin", "Cross-Origin-Embedder-Policy": "require-corp" @@ -117,7 +131,18 @@ async def read_index(): if __name__ == "__main__": import uvicorn - print(f"Starting server on http://localhost:{args.port}") - print(f"vcsky: {'local' if args.vcsky_local else 'proxy'} ({VCSKY_BASE_URL if not args.vcsky_local else 'vcsky/'})") - print(f"vcbr: {'local' if args.vcbr_local else 'proxy'} ({VCBR_BASE_URL if not args.vcbr_local else 'vcbr/'})") - uvicorn.run(app, host="0.0.0.0", port=args.port) + import webbrowser + import threading + + url = f"http://localhost:{args.port}" + if args.cheats: + url += "/?cheats=1" + + print(f"GTA VC Caching Server Running at {url}") + + if args.open: + def open_browser(): + webbrowser.open(url) + threading.Timer(1.5, open_browser).start() + + uvicorn.run(app, host="localhost", port=args.port) diff --git a/vcsky/sha256sums.txt b/vcsky/sha256sums.txt new file mode 100644 index 00000000..4efb3258 --- /dev/null +++ b/vcsky/sha256sums.txt @@ -0,0 +1,143 @@ +cuts.img=1a020de37e57dc07a106f97351b59ae5be6b54e6436c1f3965663ba0a6f9cfab +LOADSC4.TXD=2e0d2ebaf4a3b1f04ad583a16bc34a9920552e73ccf131ea012cecfcd9ac6202 +SPLASH3.TXD=4086d9c3d165b6f3cdc073945e74e9d9580af22766295aa92266c11f0f7cd661 +LOADSC11.TXD=5e439a9a83919f508087b2dbee3bf2b39ce43526a9bbb8317ec56a4aff3636e2 +LOADSC8.TXD=05424524532c74556d176c2bac6628ec1dd75b98fa939e971f5a051c54cab18c +LOADSC0.TXD=39cc0161d24f5c026b6af771df976bc285bdd3c4f078bd5f2075601843ec311d +LOADSC9.TXD=ef01ca8040c74867ab8fab4dc32226d01e67147a645415a7e49f33bcef3b421a +intro4.txd=c8ad9f78b3d9afa580cb87d48cac9ae889d7b19455ab33d612c9b93c24acbd15 +LOADSC5.TXD=a3b9dae96aabda8ea7fa46a72e09a94212a70c99aef4d7008aec250137e5ebf5 +intro2.txd=9676d49053b1248ed024aa9bb9d4360f5355f9993f4f6da9ee13ae0b29c98498 +NEWS.TXD=ff14271636df3fefb701ad7f96b11dae798ea7dcff5d5e11361044edef7092b2 +LOADSC12.TXD=7a74a1697283bc087a4b52d13df968472531647ab85056498b0da76de4ab175d +LOADSC3.TXD=9a70208ba6b51b9144f4a648afbd16dbf34cf785eb4d92585aeaf51368e40a50 +LOADSC2.TXD=78c539942518f91049393a78b1cc7e4a008fa0c340ba58e99396168fe448e434 +outro.txd=c7fe54f2f2ea2ee5e452db5882f7c2c6a628925cbfbb8dbbeef030cbfa736f04 +LOADSC6.TXD=07fc2b573b1a1849a2f6b5207965c54e7a9f6e44afa980e5eb42e837db54b941 +LOADSC7.TXD=dc5bf45b7fb36310b9bfe445b4d52ecf120823ff30270456f9341226ec11caae +LOADSC13.TXD=d4bf760b306c0d1a218a8f88caf1af72fa5379d34b1cc02c1c869bba26578cda +SPLASH1.TXD=e540b9bb976b384563708b47b4ca5876db4b9157a5217681efde9564dca70522 +INTRO3.TXD=aad3b9db3102ad9943908c73b2f16bbf3fe94aca2721105aa0f58eab13d3f9c8 +SPLASH2.TXD=61bc8d0b5e150c6f0ac6634ad183b99c32faf431bd8d1ed10f2caf6560315c7b +LOADSC1.TXD=b9dbeeccaa1e0565b3473542306dda3d6e170f894294d48c445916662e4b677b +intro1.txd=77fb4dc78afb05c71bf6b8b6d112e93e7dc78b5771bfec8276534486929ee1b2 +LOADSC10.TXD=5c60109f740f83efb280d47aa08a2887fc85f747ac73ea3813e5511a2f476755 +testapp.exe=d6ed749f6e5e05d637caf60503da5ed6cdb5a5ee1bb1f00426cc2fedb0de1a8b +Mss32.dll=d82c6770d80c04318cdba0a504443f233fb2347b428bc672adaa01f71f27b649 +gta-vc.exe=8c2318d61bffaaff5e638449543dbf99ea932dca85f297c7190d2efd525b848f +downtown.ipl=f6a3f0eed5cfe384ce6e5031cb2e9a3f3c5fac8b349fbd9f380d6172141e567e +downtown.col=da7780bce3c27275f86017e240f9f15febf401bb01a01fff79296bee259ff507 +downtown.ide=05f54c2a58667020281854e58cb05876f84e41c1a5cb531506b835518b3212fa +cull.ipl=66ad5fc5108a3292b952ccf0c2ac2e293a17f6bce6609fab5d59206b2d4e773f +nbeachbt.ide=eadfa58adf515754ecec6217e10105d62b6ea22313e57d3fca440aaabe11cfec +nbeachbt.col=7178c9d8f41d916e0ddfe683389c4afb336c1191f80fbdb48a6f53d54378d4e7 +nbeachbt.ipl=4d8893d5d875c4dd1b7ab0a4fbb3c3a74668549d97e483a77742c2bdc415c80f +hotel.ide=b3a7b569481ce2e9e1c5ec1690f05afdc344d6af23289c416d0b4eadfde46836 +hotel.ipl=77040ae4d1e828ae1a8d28b0754221287bcc7f86d345d9c7bf7d306d4f113696 +hotel.col=69fe0794cdd6b0f88aee4535f87903d47232529d4453f24f099c688777c05ee2 +concerth.col=057249a0b552b67b1cb4490e582100bb656925eb5902ecd6bc2557b0f141d4fc +concerth.ide=253f3777bc1254d45e48dcd75d11da666a871abf35f567ca6f0ca735149768a3 +concerth.ipl=96fa0a110fc8137fbb9b4c7df8d86f78d8b27c783015ae9b10961a58105613c3 +CLUB.col=0f4b2e96be249dd72fe02f38906db5bfcee13eb397a08d9a4147d510c2298976 +club.ide=18ed4b95e3b67c53ff9cba02ab495baeb32b147487b5baf734dd4c6baee143fc +CLUB.ipl=28122d3e93f1d8567f1d92052186a7b4144521e01509cf37c87fe3237445cde9 +generic.ide=34171d2eb92797fda97803cc30650dc281e0a283f035dca8221ef6cbb56691d7 +airportN.ide=02f52d18a47eebd9909d8a710e0e4ca0672289cf916924dc58377a5ebfaa024d +airportN.col=2483409b8e6ed252e705b7d7794fdf93df8fe765b850b6ebf993fff1e13c09cd +airportN.ipl=6518be29a7f98da30bfb19a1f9706d0a0567ec8b2530ccf80612f8a3600cf152 +washintn.col=ef20d676db355812dbbcf51cdf6322a5cd58a90a563fd35e3938acfe1e661f7b +washintn.ide=7da9826735e86c9a294a5fd3bd2848169ad080ca65e826f57ac4c6d9d73b4d03 +washintn.ipl=a4c048c1a7e0833f9e01bc17d79c7176f173b6bfb467693402d8a5693b029817 +haiti.col=5ceef1ba2dc0e67d38e6e70460ac9ad19b4dbd83c9a8ba9b7553bffc705cc206 +haiti.ide=d79a117e8e5248cf9d08dfa9df53daa16c2ed55fe30e5044ee3eef0429c39909 +haiti.ipl=42f045fc058d0e711f59d4ae50eb8f4319379162c8aaa0da9353bab990e723c8 +oceandN.ipl=cf07c3a757b362572080e2dbb2b6041df972fd02cadafc310a050b27bb208021 +oceandN.ide=2e87615c632bd7e58da554cc0772c29306cca3697c0e89ee6c62c2c741793018 +oceandN.col=4e0fc44db57e48f0809c529160c6c0bd7152026e2fdf7820e0c51d0af313c5bc +bridge.ide=adbb664083752f786fdbae715c8a8af2666b70c79356cda674db14890ca8e57f +bridge.ipl=b32c3d05914e4723303df682f56ceba78957576df6c36daa785f16f4ab1f4f86 +bridge.col=9e878a45737f72ef0394950910fab6065fc068892e151af19a31edeaa3e90ce7 +docks.ipl=f8ce3b51b76016d02b767de655dc4b9872094bfa74e9b86498df4aab3ddee004 +docks.ide=cac3379ee49fbcb7aabd9fa2002e68d53c96bae9e0031e0d50198e129cc9b745 +docks.col=e2dd1a62e6d3723e33cc42e73863cb2bd09e35046ecdfd26c09932089b8ab445 +washints.col=d470237fb1b924cf0e114df1beb2fb74e3a5612f0f9e4764b6318a398671ee00 +washints.ipl=2f56f4abc2281c42498156e7e8ec4c38be8a78f35b0f5bf1445e3c76702fd01b +washints.ide=e27a27f9f6d467b153caa0396870a199fb802347e1c05275618a480b2e11a64b +bar.ide=b245d6e14ddc50063db84b198ae9e1ba9cf82ce0ed25f9de4077d8bb68029fe6 +yacht.ide=206d0f75d2acb234c043f7bc2e905ac93abad05c116a97adb78c9a5c625f7ea0 +yacht.ipl=4351545987afe60e3932cf12e90d8233b4014dd7e20c266ed20cbdc475c767d4 +yacht.col=cee03f74a2550821812cb5924c2ad1aa6141bfa454b7fa28ad6f156dbf791b83 +haitiN.ide=d080cf155ac73bd863f6fab27b0cba84c37c43d91dee6d4bb9972c80c99c71f7 +haitin.ipl=cb41d0e2f489ded559fd0535347f635b184da25717d80aecd7ba789c8501824e +haitin.col=a6328eabd7a1609cca41f6cac4ee927140284c00e2cc1478ae1bab4c3adff466 +nbeachw.ipl=a7cc5eb3e8cb32f4a7edb6f7670de17f0735cacc644143fdfaf0a97c3864ed4c +nbeachw.col=1e35c374a579223b0be6531289f03ccc451cc6ab60b140fc8844fc09230eff05 +nbeachw.ide=348054d962b607758d6ee7efc98bb97c433b5743b26859ed9146b33e4080d469 +bank.col=0c183ec2065980b7b8f0dcfe30af470faab2f46b79205f9e5cc44532d6027f9a +bank.ide=a39f4b008f6ce9cbf7098540da9952e21efa8ffa04fbf65df7c2b08c4f1338a9 +bank.ipl=b47b269c60ca89d97f33fea0adbea09e4f08f815a4a1497b32c64ac6f7438d5d +starisl.col=5fdf060a54a8cb7e5367f1737b21db68a43bc62eca56222ed689c51a8089d438 +starisl.ipl=f1cf04b6381784839ff23e5b0f97860733c48618c87c6b2f392465448d6740ea +starisl.ide=6b4e617b25e01eaff038e8dda074ddbb9ea539475769dfcf5051f1518fa974f3 +golf.ipl=744a0323c479b789b97665f369807d44aa42ee246246dcffb3217cdc5379d5f6 +golf.col=2ec9841f38ad38ef749c498c6166e7ea469fa1ccb0462771f16da8d37f734b99 +golf.ide=5dd15fd6f2fa4deeeb1c9d7ac7447fbbca0c8d395fb06fbbe043a61f7a4581ac +lawyers.col=6b1b261e141384a4460c9bfd865a9d0a99d6b1538a56ce313fa4c87cbaeb25a2 +lawyers.ide=1eaf1f3b9107572a015045451936a8442a66f0693a7982c601294deb31920a28 +lawyers.ipl=b196f2fea07a0fd78db9661716f4bc070e21157e23bbbca748f10b19fe80d03f +nbeach.ide=2d5f7d0b5f12c88d4c22aa9ec640f0f1293c1d5469f1df1d947fa95c7ad21670 +nbeach.col=4ad577271612680443c5080e97c5055d0b361a59b81f43774044dda78fd1df51 +nbeach.ipl=a565e0246d35fd9138cb088de483f542da8e4a6b962e372935bf2308af5c81f3 +cisland.ipl=f43194becca9e0f4be3399bd8734dfe5eff7c79dab2f2a773b546aff8dd299c3 +cisland.ide=b1cdcebf58ca6445e1d757ba494da96685ed4095c28494af3172ba090dd19af4 +cisland.col=b6be49ab67e872b1e1dec4579a1ba41403ae47ca4e6e616b927809d8b5b4710d +littleha.col=5f3f7dec25211a163be97d85c261df942057007ff5988710c5d0a0469f169116 +littleha.ipl=b54c19e7784f543809dd7fb349c254c9524f8115b059fb1304796aec07cad85c +littleha.ide=bf84206d298602b0fda3a2ed8787bc91bcdcf90f317c200a3a96c335c9538541 +oceandrv.col=3c9b3f141c762deaf57d47cc6445d5dcc201cda55b426606080888af44a5056e +oceandrv.ipl=cc3406d23b2d74a4702278b243e033d13f19674a9f19deab1163da60b28b9d4e +oceandrv.ide=b1d194d91a9f0feca62af638772702d57578103ba3276092f9ea3ee3bba9f7c9 +airport.col=c6d559a3d4e101b7366be922340f29a87f2fbf7ab9b3eacb8b27c4137e6686a0 +airport.ipl=f2ea82fb0e443005efe640646f5b3d4cd7bc7deb346ddef12204a7b9c49f733c +airport.ide=349b065a7d358459be29e0e7a0b2f1dd7aadbafea61a481875fa48c4b5726692 +mall.col=543c390649cb58ff5d78f70cfc66e67b9b41e8cf64317ec6cde0ff1432b5debf +mall.ide=637ef6691d28ee9508769f3fcd437a8e4b3be7d7dd7d285f04906e0cae151bba +mall.ipl=b0901bb83d4c9aa7974e68bc9c2b478a2d2f36cb0f3259d3c07c96f21ef987cd +downtows.ipl=8fc8b8f30b7241cf11cb57a0815f8148a076052d990a099962484982111bfbb0 +downtows.ide=ccbda23d491130880075f99a7f3f186e81e92545bbae3a8f6b18024bd47e5f55 +downtows.col=6e69da16a4d784153766fa00019a6257200fd1db808ba8e3a0226466220860ee +paths.ipl=4ed810384d173035482ee5a5284510a252786b9cdc4408b3cfaeafa9180ef09a +mansion.ide=8c47789ea91a805a67d8faa492abcd2436635c7f82259138f3a824008c1b9536 +mansion.ipl=fa9c36cbdf88afd9ec0748b29707c83b3871b36b483a2694dc3f4fcc23ed8c1a +mansion.col=83396023a4ca0b35af13831da3d5da36399fc5bb9d780aad38272dc5d139ff6f +stripclb.ipl=b5b77974272b84b4544a6208d463a1354aa95fe13a6b770dca271fe1091912ad +stripclb.ide=978745cc51a8d470de3283275d6160ce29ec0b88783c8c870ccfb3f137bbb6e1 +stripclb.col=b2396c5f2c27feb97ace5abad5d72347469c7f60435deeffcf2234cda0e2573d +stadint.col=edc79da8dba1574ecd7ea421ca38d72c17328dddde402ec8632973a359030394 +stadint.ide=0a9dd607339355eeeb0dd9496bdff67979f50df5f5a940c1c9c56169a1779bb3 +stadint.ipl=a58fde5b8d0f0e89edb12d8999b1f4cc607074af7bc7181e71e6b6615cd277cc +islandsf.ipl=0158872bbef2cf8b7d50b5c871b18c86268a72c6f636732f1f2ab1ae6ce16717 +islandsf.col=434ab97dbef1ea1b05ccb46ff256a5a97c365e5829f4a27496b300d046732b87 +islandsf.ide=b792bafdf881e4961c471d645c84111514441d17680a67c7786a8e6247d5841a +main.scm=0fb3fd45b6a6dc21870cb48d9abe983510284b2b09b0c3d0eeac51d5dff338c7 +default.ide=e02b25143a28d3fcb7cd458932e18be2c13ed03a3fbe9f2f1715047d75518ed9 +occlu.ipl=f2cff324ee36e36dde09a1e9299c13e5cf05156bfd4db546e2de50d5e3f4b84c +vehicles.col=18b056a3ac6c8dd3a323ebcbcde7290de8bac9dd2a3c4f3082264ee57018b737 +generic.col=e099499c818452ca383eccbdd153c164b6f6a30ddacb403e26e2ce2a5c468cf5 +weapons.col=fbcb1727ad6dc7b19ecf3f351de8f154118d8b893fb91560b696095e2c7a89d0 +peds.col=d6faa88b9227e6f6fe286f69cfd8d7348229bda267b21125b7daea4d3dd23d10 +INTRO.TXD=6e72a05e72e082ef4e88cd88ce5bb3fd407c0203e5ecd16642fdd7a7db23786e +gta3.img=12547d034a5b114b891582e36b7214794e5eafe1ad75a653bf2d9b3e2e18849a +fronten1.txd=85ddb0328bfc081645deab79dc6d439d776b781f5e25b58a63faf03afdcc4d55 +fonts.txd=789de34eb6982e7f38e452dbf7758f2bf88aa3d0799719ec7ad13090e19469a4 +fronten2.txd=1ee528b092dcebc6883fdc52af90765bcbaabe24b39d843756889d34348f5134 +MISC.TXD=964b3f91c47f47449a3d441f8379de4504fe4590a5f2c3a7304abc7837fe947f +generic.txd=8f28f214bdc9f103f86ee7bf8a29ed5829ee26a64cce2c0eeb7b1906a37983c2 +air_vlo.DFF=044445014cd8e10aae0ac3ce8a1f1a68eede09b553e28b963912fb24624697c9 +wheels.TXD=964b3f91c47f47449a3d441f8379de4504fe4590a5f2c3a7304abc7837fe947f +zonecylb.DFF=84fbaa94b224107554b26bf672ff8c5bf110d0b7a06c422411c98ba9383161e4 +wheels.DFF=f8869c8e8f56a2b5b073e66c4817baadb13175d4f67ea30aaada1e7ecb3cfa0d +arrow.DFF=cfc1bd58828c675b5c428141b4e0eb18f9590c6b2d4b09446830401d253c7768 +particle.txd=a66a90c1dbcddfcfcf68e26d0c3e87c8b8f1e6b720a7ff21b971eaceee0b4870 +hud.txd=51a73732cb6e57ead9beceaaa39db06676c69394505d0aa7c00ade7a2910518d +gta-vc.exe=04e4db72629eaa786fdd182ac224f4fd6d68806f5f4fe5c1fb5f756dc4da11d7 From 44e4837e7e8d7e91a8835dea5738f42dfcf5b19e Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Wed, 24 Dec 2025 20:53:34 +0530 Subject: [PATCH 02/29] updated readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7251178a..831330e4 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,8 @@ Do what you want. Not affiliated with Rockstar Games. **Russian translation:** [GamesVoice](https://www.gamesvoice.ru/) +**Some more files were required for being Fully Local:** [Th3w33knd](https://github.com/Th3w33knd) + ## Support me If you find this project useful: From 6f11e7f1ab5cbbe87c85a1a29d50fb7b6321d2ea Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Wed, 24 Dec 2025 21:39:08 +0530 Subject: [PATCH 03/29] Note on vcbr --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 831330e4..d62aa34c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ This project is a community effort to preserve the incredible HTML5 port of GTA: 2. Implementing a caching system to download and save surviving assets. 3. Removing dependencies on the defunct infrastructure. +> [!NOTE] +> We still do not have the source code for the files in `vcbr` (the compiled WebAssembly modules), which contain the core logic of the game. This project wraps and serves those existing binaries. + **Stability Test**: Watch the server in action here: [GTA VC Server Stability Test](https://www.youtube.com/watch?v=C8nK81N4iBs) ## Quick Start From 5f7cf1b4bc77748866909919667973df3f852c50 Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Wed, 24 Dec 2025 21:46:05 +0530 Subject: [PATCH 04/29] Add changelog and fix server asset path logic --- README.md | 7 +++++++ server.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d62aa34c..37900f67 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ Web-based port of GTA: Vice City running in browser via WebAssembly. +## Recent Updates (Dec 2025) + +* **New Cheat Command**: Added `pixi run cheat` to automatically launch the game with cheats enabled and open the browser. +* **Asset Management**: Updated server logic to correctly handle `vcsky/fetched` assets without duplication. +* **Documentation**: Added comprehensive "Quick Start", "Download Assets" (with links), and "Project Structure" sections. +* **Fixes**: Solved directory nesting issues and added `vcbr` to `.gitignore` to prevent large file commit errors. + You can check which files I made changes in commit section. ## History & Restoration diff --git a/server.py b/server.py index e9fd10ee..ab4e0839 100644 --- a/server.py +++ b/server.py @@ -109,7 +109,7 @@ async def route_vcbr(path: str): async def route_vcsky(path: str): if path == "sha256sums.txt": return serve_file(os.path.join(DIR_VCSKY, path)) - return await fetch_and_cache(path, os.path.join(DIR_VCSKY, "fetched"), CDN_VCSKY) + return await fetch_and_cache(path, DIR_VCSKY, CDN_VCSKY) @app.get("/") async def read_index(): From 2dd123c5f3808ddf946b46a18ff9ce86cc63e765 Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 07:07:14 +0530 Subject: [PATCH 05/29] Fix merge logic: Restore smart caching behavior and remove duplicate routes --- README.md | 8 +++--- pixi.toml | 4 +-- server.py | 79 ++++++++----------------------------------------------- 3 files changed, 17 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 21322fa5..a110c03b 100644 --- a/README.md +++ b/README.md @@ -159,12 +159,12 @@ By default the `index.php` and `.htaccess` will get the job done. | `--custom_saves` | flag | disabled | Enable local save files (saves router) | | `--login` | string | none | HTTP Basic Auth username | | `--password` | string | none | HTTP Basic Auth password | -| `--vcsky_local` | flag | enabled | Serve vcsky from local `vcsky/` directory | -| `--vcbr_local` | flag | enabled | Serve vcbr from local `vcbr/` directory | +| `--vcsky_local` | flag | disabled | Serve vcsky from local `vcsky/` directory (Strict Local) | +| `--vcbr_local` | flag | disabled | Serve vcbr from local `vcbr/` directory (Strict Local) | | `--vcsky_url` | string | `https://cdn.dos.zone/vcsky/` | Custom vcsky proxy URL | | `--vcbr_url` | string | `https://br.cdn.dos.zone/vcsky/` | Custom vcbr proxy URL | -| `--vcsky_cache` | flag | disabled | Cache vcsky files locally while proxying | -| `--vcbr_cache` | flag | disabled | Cache vcbr files locally while proxying | +| `--vcsky_cache` | flag | enabled | Cache vcsky files locally while proxying (Smart Cache) | +| `--vcbr_cache` | flag | enabled | Cache vcbr files locally while proxying (Smart Cache) | | `--cheats` | flag | disabled | Enable cheats in URL | | `--open` | flag | disabled | Open browser on start | diff --git a/pixi.toml b/pixi.toml index 9a1fd9c6..a8e4da48 100644 --- a/pixi.toml +++ b/pixi.toml @@ -7,8 +7,8 @@ version = "0.1.0" [tasks] local = "python server.py --custom_saves" -start = "python server.py --vcsky_local --custom_saves" -cheat = "python server.py --vcsky_local --custom_saves --cheats --open" +start = "python server.py --vcsky_cache --vcbr_cache --custom_saves" +cheat = "python server.py --vcsky_cache --vcbr_cache --custom_saves --cheats --open" [dependencies] python = "3.12.*" diff --git a/server.py b/server.py index b6bcc6a0..d5f82e88 100644 --- a/server.py +++ b/server.py @@ -19,13 +19,13 @@ def request_to_url(request: Request, path: str, base_url: str): parser.add_argument("--custom_saves", action="store_true") parser.add_argument("--login", type=str) parser.add_argument("--password", type=str) -# Defaulting local flags to True to prioritize local files (User Preference) -parser.add_argument("--vcsky_local", action="store_true", default=True, help="Serve vcsky from local directory instead of proxy") -parser.add_argument("--vcbr_local", action="store_true", default=True, help="Serve vcbr from local directory instead of proxy") +# Defaulting local flags to False to allow network fallback (Smart Cache) +parser.add_argument("--vcsky_local", action="store_true", default=False, help="Serve vcsky from local directory instead of proxy") +parser.add_argument("--vcbr_local", action="store_true", default=False, help="Serve vcbr from local directory instead of proxy") parser.add_argument("--vcsky_url", type=str, default=VCSKY_BASE_URL, help="Custom vcsky proxy URL") parser.add_argument("--vcbr_url", type=str, default=VCBR_BASE_URL, help="Custom vcbr proxy URL") -parser.add_argument("--vcsky_cache", action="store_true", help="Cache vcsky files locally. If files are not found in the local directory, they will be downloaded from the specified URL and saved to the local directory.") -parser.add_argument("--vcbr_cache", action="store_true", help="Cache vcbr files locally. If files are not found in the local directory, they will be downloaded from the specified URL and saved to the local directory.") +parser.add_argument("--vcsky_cache", action="store_true", default=True, help="Cache vcsky files locally. If files are not found in the local directory, they will be downloaded from the specified URL and saved to the local directory.") +parser.add_argument("--vcbr_cache", action="store_true", default=True, help="Cache vcbr files locally. If files are not found in the local directory, they will be downloaded from the specified URL and saved to the local directory.") parser.add_argument("--cheats", action="store_true", help="Enable cheats in URL") parser.add_argument("--open", action="store_true", help="Open browser on start") args = parser.parse_args() @@ -46,81 +46,24 @@ def request_to_url(request: Request, path: str, base_url: str): @app.api_route("/vcsky/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]) async def vc_sky_proxy(request: Request, path: str): local_path = os.path.join("vcsky", path) - if args.vcsky_local: - if response := get_local_file(local_path, request): - return response - # If local is forced but file missing, and we are NOT caching/proxying, 404. - # But if the user wants "download on demand" behavior like before, they should use --vcsky_cache WITHOUT --vcsky_local? - # Wait, my previous behavior was: check local, if missing download. - # Upstream behavior: - # if vcsky_local: serve local, else 404. - # if NOT vcsky_local: proxy (and optionally cache). - - # To match my previous "smart" behavior (local if exists, else download), I should probably use: - # --vcsky_cache (enabled) AND NOT --vcsky_local (disabled). - # Because proxy_and_cache checks local cache first! - - # Let's check additions/cache.py logic if possible. - # Assuming proxy_and_cache checks local file first. - pass - - # Fallback to proxy/cache logic if not strictly local or if we want hybrid - # Actually, if I set default vcsky_local=True, it will NEVER download. - # My previous behavior was: Check local, if missing -> download. - # Upstream `proxy_and_cache` likely does: Check local, if hit return, else download. - - # So the correct configuration for "Offline first, download if missing" is actually: - # --vcsky_cache (True) - # --vcsky_local (False) -> This flag seems to mean "Strictly Local, no network". - - # However, the user has downloaded the files. So Strict Local is fine for them. - # But for the "Quick Start" experience where they might miss a file, "Cache" mode is better. - - # I will stick to args.vcsky_local = True as default because the user HAS the files. - # If they want caching behavior, they can run with --vcsky_cache (and no --vcsky_local). - - # But wait, if I set vcsky_local=True, it raises 404 if missing. - # My previous code downloaded it. - # The user might prefer the download behavior. - - # I will modify the logic slightly to support "Local then Proxy" if requested, or just stick to the flags. - # I'll stick to the flags to be consistent with upstream. - # I'll set default vcsky_local=True because the user has the files. - raise HTTPException(status_code=404, detail="File not found") - - # This part is unreachable if vcsky_local is True and file found, or if it raised 404. - # If vcsky_local is False: - url = request_to_url(request, path, args.vcsky_url) - if args.vcsky_cache: - return await proxy_and_cache(request, url, local_path) - return await proxy_and_cache(request, url, disable_cache=True) - -# Redefining to support the hybrid flow if I want to merge them? -# No, let's stick to upstream logic. -# If I want "Check local, then download", I should use `proxy_and_cache` with `vcsky_local=False` and `vcsky_cache=True`. -# But `proxy_and_cache` implementation is unknown to me (I can't see it right now). -# Assuming it does check local. - -@app.api_route("/vcsky/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]) -async def vc_sky_proxy_wrapper(request: Request, path: str): - # Wrapper to handle the logic - local_path = os.path.join("vcsky", path) - - # 1. If strictly local requested + # 1. Strict Local Mode (No Network) if args.vcsky_local: if response := get_local_file(local_path, request): return response raise HTTPException(status_code=404, detail="File not found") - # 2. Proxy/Cache mode + # 2. Smart Cache Mode (Local -> Network -> Cache) + # If caching is enabled (default), proxy_and_cache checks local first. url = request_to_url(request, path, args.vcsky_url) if args.vcsky_cache: return await proxy_and_cache(request, url, local_path) + + # 3. Proxy Only Mode (No Local Cache) return await proxy_and_cache(request, url, disable_cache=True) @app.api_route("/vcbr/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]) -async def vc_br_proxy_wrapper(request: Request, path: str): +async def vc_br_proxy(request: Request, path: str): local_path = os.path.join("vcbr", path) if args.vcbr_local: if response := get_local_file(local_path, request): From 055d985ab9224c5506e095b94b881a83f6c67df3 Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 07:09:49 +0530 Subject: [PATCH 06/29] Update README with merge and caching details --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a110c03b..a0d2ca03 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Web-based port of GTA: Vice City running in browser via WebAssembly. * **Asset Management**: Updated server logic to correctly handle `vcsky/fetched` assets without duplication. * **Documentation**: Added comprehensive "Quick Start", "Download Assets" (with links), and "Project Structure" sections. * **Fixes**: Solved directory nesting issues and added `vcbr` to `.gitignore` to prevent large file commit errors. +* **Upstream Sync**: Merged latest changes from `Lolendor/reVCDOS` while preserving custom features (cheats, offline mode). +* **Smart Caching**: Restored "Smart Cache" behavior (check local, then download) as the default. You can check which files I made changes in commit section. From 1de669ef727d27f2c317f817b219ba14b22a4ef1 Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 07:30:24 +0530 Subject: [PATCH 07/29] Add Online and Offline modes to Pixi configuration --- README.md | 23 +++++++++++++++-------- pixi.toml | 2 ++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a0d2ca03..d519755c 100644 --- a/README.md +++ b/README.md @@ -75,11 +75,19 @@ This project is a community effort to preserve the incredible HTML5 port of GTA: ``` 4. **Start the game**: - * **Standard Mode**: + * **Online Mode** (Recommended): ```bash - pixi run start + pixi run online ``` - * **Cheat Mode** (Opens browser with cheats enabled): + *Checks local files first. If missing, downloads from CDN and caches them.* + + * **Offline Mode** (Strict): + ```bash + pixi run offline + ``` + *Uses ONLY local files. No network connection. Useful if you have all assets downloaded.* + + * **Cheat Mode**: ```bash pixi run cheat ``` @@ -99,11 +107,10 @@ This project is a community effort to preserve the incredible HTML5 port of GTA: This project uses [Pixi](https://pixi.sh/) for dependency management and task running. 1. **Install Pixi**: Follow the instructions at [pixi.sh](https://pixi.sh/). -2. **Start the server**: - ```bash - pixi run start - ``` - This command automatically installs dependencies and starts the server with the correct configuration. +2. **Run a task**: + * `pixi run online` - Smart caching (download if missing). + * `pixi run offline` - Strict local files only. + * `pixi run cheat` - Online mode + cheats enabled + auto-open browser. ### Option 2: Using Docker The easiest way to get started is using Docker Compose: diff --git a/pixi.toml b/pixi.toml index a8e4da48..f42e4a2d 100644 --- a/pixi.toml +++ b/pixi.toml @@ -7,6 +7,8 @@ version = "0.1.0" [tasks] local = "python server.py --custom_saves" +offline = "python server.py --vcsky_local --vcbr_local --custom_saves" +online = "python server.py --vcsky_cache --vcbr_cache --custom_saves" start = "python server.py --vcsky_cache --vcbr_cache --custom_saves" cheat = "python server.py --vcsky_cache --vcbr_cache --custom_saves --cheats --open" From 1fb81a42b752d389b4fc44f3bd936d20a44e670c Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 07:32:06 +0530 Subject: [PATCH 08/29] Add detailed Game Modes section to README --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index d519755c..82c97bf9 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,33 @@ This project is a community effort to preserve the incredible HTML5 port of GTA: - Dependencies from `requirements.txt` - **Pixi** (Recommended for package management) +## Game Modes + +The server supports different modes to suit your setup: + +### 1. Online Mode (Recommended) +**Command:** `pixi run online` + +* **Behavior**: "Smart Caching". The server checks your local `vcsky/` folder first. + * **If file exists**: Serves it locally (Fast, Offline-capable). + * **If file missing**: Downloads it from the CDN and saves it to `vcsky/` for future use. +* **Best for**: Most users. It ensures you can play immediately even if you haven't manually downloaded every single asset. + +### 2. Offline Mode (Strict) +**Command:** `pixi run offline` + +* **Behavior**: "Strict Local". The server serves **ONLY** files present in your `vcsky/` and `vcbr/` folders. + * **If file missing**: Returns a 404 Error. No network requests are made. +* **Best for**: Users who have manually downloaded the full asset packs and want to ensure no background network activity. Ideal for air-gapped or fully offline setups. + +### 3. Cheat Mode +**Command:** `pixi run cheat` + +* **Behavior**: Same as **Online Mode**, but automatically: + 1. Enables the built-in cheat engine. + 2. Opens your default web browser to the game URL (`http://localhost:8000/?cheats=1`). +* **Best for**: Jumping straight into the action with cheats enabled (Press **F3** in-game). + ## Setup & Running ### Option 1: Using Pixi (Recommended) From d11778d2db5ab74618a3bc3f79b4e45662b2571b Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 07:35:17 +0530 Subject: [PATCH 09/29] Clarify Cheat Mode behavior and usage in README --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 82c97bf9..f1563cd8 100644 --- a/README.md +++ b/README.md @@ -122,10 +122,12 @@ The server supports different modes to suit your setup: ### 3. Cheat Mode **Command:** `pixi run cheat` -* **Behavior**: Same as **Online Mode**, but automatically: - 1. Enables the built-in cheat engine. - 2. Opens your default web browser to the game URL (`http://localhost:8000/?cheats=1`). -* **Best for**: Jumping straight into the action with cheats enabled (Press **F3** in-game). +* **Behavior**: **Online Mode + Cheats + Auto-open**. + * Starts the server in Online Mode (Smart Caching). + * Automatically opens your default web browser. + * Enables the built-in cheat engine. +* **How to use**: Once the game loads, press **F3** to open the cheat menu (Memory Scanner, Noclip, etc.). +* **Best for**: Jumping straight into the action with cheats enabled. ## Setup & Running From e5eb0ab62720db0d2981e469715e899a694345ed Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 07:56:06 +0530 Subject: [PATCH 10/29] changed brotli package --- pixi.lock | 71 ++++++++----------------------------------------------- pixi.toml | 2 +- 2 files changed, 11 insertions(+), 62 deletions(-) diff --git a/pixi.lock b/pixi.lock index 2078dcba..c3a89a25 100644 --- a/pixi.lock +++ b/pixi.lock @@ -10,8 +10,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py312hc6d9e41_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda @@ -33,9 +32,6 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda @@ -115,33 +111,21 @@ packages: license_family: MIT size: 144702 timestamp: 1764375386926 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-1.2.0-h2d644bc_1.conda - sha256: a4fffdf1c9b9d3d0d787e20c724cff3a284dfa3773f9ce609c93b1cfd0ce8933 - md5: bc58fdbced45bb096364de0fba1637af +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py312hc6d9e41_1.conda + sha256: 2bb6f384a51929ef2d5d6039fcf6c294874f20aaab2f63ca768cbe462ed4b379 + md5: e8e7a6346a9e50d19b4daf41f367366f depends: - - brotli-bin 1.2.0 hfd05255_1 - - libbrotlidec 1.2.0 hfd05255_1 - - libbrotlienc 1.2.0 hfd05255_1 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - size: 20342 - timestamp: 1764017988883 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.2.0-hfd05255_1.conda - sha256: e76966232ef9612de33c2087e3c92c2dc42ea5f300050735a3c646f33bce0429 - md5: 6abd7089eb3f0c790235fe469558d190 - depends: - - libbrotlidec 1.2.0 hfd05255_1 - - libbrotlienc 1.2.0 hfd05255_1 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 + constrains: + - libbrotlicommon 1.2.0 hfd05255_1 license: MIT license_family: MIT - size: 22714 - timestamp: 1764017952449 + size: 335482 + timestamp: 1764018063640 - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda sha256: d882712855624641f48aa9dc3f5feea2ed6b4e6004585d3616386a18186fe692 md5: 1077e9333c41ff0be8edd1a5ec0ddace @@ -388,41 +372,6 @@ packages: license_family: BSD size: 120685 timestamp: 1764517220861 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.2.0-hfd05255_1.conda - sha256: 5097303c2fc8ebf9f9ea9731520aa5ce4847d0be41764edd7f6dee2100b82986 - md5: 444b0a45bbd1cb24f82eedb56721b9c4 - depends: - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - size: 82042 - timestamp: 1764017799966 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.2.0-hfd05255_1.conda - sha256: 3239ce545cf1c32af6fffb7fc7c75cb1ef5b6ea8221c66c85416bb2d46f5cccb - md5: 450e3ae947fc46b60f1d8f8f318b40d4 - depends: - - libbrotlicommon 1.2.0 hfd05255_1 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - size: 34449 - timestamp: 1764017851337 -- conda: https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.2.0-hfd05255_1.conda - sha256: 3226df6b7df98734440739f75527d585d42ca2bfe912fbe8d1954c512f75341a - md5: ccd93cfa8e54fd9df4e83dbe55ff6e8c - depends: - - libbrotlicommon 1.2.0 hfd05255_1 - - ucrt >=10.0.20348.0 - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - license: MIT - license_family: MIT - size: 252903 - timestamp: 1764017901735 - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda sha256: 844ab708594bdfbd7b35e1a67c379861bcd180d6efe57b654f482ae2f7f5c21e md5: 8c9e4f1a0e688eef2e95711178061a0f diff --git a/pixi.toml b/pixi.toml index f42e4a2d..c079e8f2 100644 --- a/pixi.toml +++ b/pixi.toml @@ -17,5 +17,5 @@ python = "3.12.*" fastapi = ">=0.127.0,<0.128" uvicorn = ">=0.40.0,<0.41" httpx = ">=0.28.1,<0.29" -brotli = ">=1.2.0,<2" python-multipart = ">=0.0.21,<0.0.22" +brotli-python = ">=1.2.0,<2" From aa0bf3a0ef863c3dd8ee74373000ad53d25cd27e Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 08:45:24 +0530 Subject: [PATCH 11/29] feat: Tailscale access, localization fixes, and changelog --- README.md | 41 ++++++++++++++++++ dist/index.html | 110 +++++++++++++++++++++++++++++++++--------------- server.py | 2 +- 3 files changed, 118 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index f1563cd8..2ab738d4 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,34 @@ Enable with `?cheats=1`, press **F3** to open menu: - All classic GTA VC cheats - AirBreak (noclip mode) +## Remote Access (Tailscale / LAN) + +The server is configured to listen on `0.0.0.0`, meaning it accepts connections from other devices on your network (LAN or VPN like Tailscale). + +### 1. Connecting +Simply use your host computer's IP address and the port (default 8000). +* **LAN**: `http://192.168.1.x:8000` +* **Tailscale**: `http://100.x.y.z:8000` + +### 2. "Secure Context" Issues (Browser Blocking) +Modern browsers restrict high-performance features (like `SharedArrayBuffer`, required by this game) to **Secure Contexts** (HTTPS or localhost). Connecting via a plain HTTP IP address (like `http://100.x.y.z:8000`) may cause the game to hang or crash. + +#### Solution A: Tailscale Serve (Recommended) +Use Tailscale's built-in HTTPS feature to create a secure tunnel. +1. Run the game server: `pixi run online` +2. In a separate terminal, run: + ```bash + tailscale serve https:443 / http://127.0.0.1:8000 + ``` +3. Open the provided HTTPS URL (e.g., `https://your-pc.tailnet.ts.net`) on your remote device. + +#### Solution B: Browser Flag (Workaround) +Force your browser to treat the insecure IP as secure. +1. On the **client device** (e.g., your phone or laptop), open Chrome/Edge. +2. Go to `chrome://flags/#unsafely-treat-insecure-origin-as-secure`. +3. Enable the flag and enter your server's full URL (e.g., `http://100.100.10.10:8000`). +4. Relaunch the browser. + ## License Do what you want. Not affiliated with Rockstar Games. @@ -314,3 +342,16 @@ Do what you want. Not affiliated with Rockstar Games. If you find this project useful: - **TON / USDT (TON)** `UQAyBchGEKi9NnNQ3AKMQMuO-SGEhMIAKFAbkwwrsiOPj9Gy` + +## Changelog + +### v1.1.0 - Tailscale & Localization Update +* **Remote Access**: Server now binds to .0.0.0 to allow connections from LAN and Tailscale. +* **Documentation**: Added guide for connecting via Tailscale/LAN and handling Secure Contexts. +* **Localization**: Fixed Russian text in the main menu (Subscribe for news and releases). +* **Credits**: Added comprehensive credits for Deobfuscation, Translation, and Offline Support. + +### v1.0.0 - Initial Enhancements +* **Offline Mode**: Game assets are cached locally for offline play. +* **Cheats**: Added cheat menu (F3) and support for classic GTA VC cheats. +* **Smart Caching**: Optimized asset loading strategy. diff --git a/dist/index.html b/dist/index.html index c8e01200..991153c6 100644 --- a/dist/index.html +++ b/dist/index.html @@ -32,6 +32,7 @@ padding: 1em; box-sizing: border-box; } + body.gameIsStarted { background-color: #000; background-image: none; @@ -199,6 +200,7 @@ .developed-by { margin-bottom: 2em; } + .translated-by { margin-top: .5em; } @@ -236,6 +238,7 @@ .warning { color: #ff4193; } + .wasted-container { position: absolute; top: 0; @@ -259,6 +262,7 @@ opacity: .5; display: none; } + .gameIsStarted[data-is-touch="1"] .touch-controls-wrapper { display: block; } @@ -281,6 +285,7 @@ top: 20vmin; left: 1vmin; } + .touch-control.jump { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M121.787 67.372c-10.171-5.855-16.174-11.504-22.205-20.367-.048-.072-.055-.17-.07-.255-1.01-5.835 3.645-9.23 2.079-13.779-1.554-4.515-4.935-5.477-10.671-4.799-7.724.913-5.02 8.573-4.922 14.326.025 1.505-.323 2.963-.45 3.438a1.3 1.3 0 0 1-.119.296C79.084 57.48 60.617 57.572 56.24 64.396c-2.187 3.463.742 12.197 2.047 14.916.032.066.029.132.054.2 1.92 5.165 8.862 10.678 11.644 13.166.33.295.798.373 1.204.196 2.781-1.215 4.48-5.286 8.165-5.456.276-.013.558.089.761.275 4.97 4.54-4.437 14.808-7.902 19.622-.351.487-1.02.627-1.527.306-8.18-5.175-11.68-9.811-20.629-15.887a1.15 1.15 0 0 0-1.314.014c-6.648 4.845-11.96 11.785-14.164 19.877l.399.634c3.857.447 8.46-2.464 12.36-4.032.31-.125.662-.097.958.061 9.43 5.019 13.74 9.035 20.47 16.301.125.136.287.25.461.312 8.366 3.013 17.284-9.892 21.315-13.667 6.534-8.944 12.715 1.198 12.788 1.345 3.874 7.735-.928 20.056-.108 27.144a1.1 1.1 0 0 0 .72.896c4.581 1.728 18.64 8.512 22.398 5.977.456-.308.604-.904.388-1.41-1.446-3.37-6.348-6.845-9.537-9.005a1.14 1.14 0 0 1-.435-1.287c3.001-9.439 2.904-12.684 4.45-22.979.279-5.517-10.63-15.458-13.864-28.936-1.604-6.68-2.447-5.915-2.329-12.724.013-.77.776-1.314 1.501-1.056 7.693 2.74 20.539 12.082 23.994 6.753 1.901-2.827-1.959-4.487-8.721-8.58M75.734 80.146c-11.991-3.257-9.08-14.113-1.609-15.495 6.445 0 2.467 10.513 1.609 15.495'/%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3C/svg%3E"); width: 20vmin; @@ -288,9 +293,11 @@ bottom: 10vmin; right: 22vmin; } + [data-state-car="1"] .touch-control.jump { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M152.717 80.384c-.96-3.85-4.883-8.09-8.6-8.673-6.997-1.095-16.088-.738-23.234-.72l-39.21.093c-11.376.02-30.61-.79-41.1.153-6.453 1.766-10.547 5.334-10.54 12.63.008 8.717 2.39 17.42 3.354 26.066 1.17 8.424 2.058 17.695 3.87 25.953.48 2.18 2.608 5.848 5.14 6.878 3.03 1.073 5.87 1.092 8.87 1.23 9.558.432 82.67 1.523 86.62-.53 2.49-.978 3.21-1.804 4.313-3.025 2.698-2.924 3.506-6.992 4.563-12.5 2.7-15.208 6.955-42.447 5.954-47.554zM61.08 127.61c0 2.048-1.66 3.708-3.707 3.708h-4.14c-2.047 0-3.707-1.66-3.707-3.708v-4.14c0-2.047 1.66-3.707 3.708-3.707h4.14c2.047 0 3.707 1.66 3.707 3.708v4.14zm0-17.91c0 2.05-1.66 3.71-3.707 3.71h-4.14c-2.047 0-3.707-1.66-3.707-3.71v-4.138c0-2.048 1.66-3.71 3.708-3.71h4.14c2.047 0 3.707 1.662 3.707 3.71v4.14zm0-17.63c0 2.048-1.66 3.708-3.707 3.708h-4.14c-2.047 0-3.707-1.66-3.707-3.71v-4.137c0-2.048 1.66-3.708 3.708-3.708h4.14c2.047 0 3.707 1.66 3.707 3.71v4.137zm17.872 35.54c0 2.048-1.66 3.708-3.71 3.708h-4.137c-2.048 0-3.71-1.66-3.71-3.708v-4.14c0-2.047 1.662-3.707 3.71-3.707h4.138c2.05 0 3.71 1.66 3.71 3.708v4.14zm0-17.91c0 2.05-1.66 3.71-3.71 3.71h-4.137c-2.048 0-3.71-1.66-3.71-3.71v-4.138c0-2.048 1.662-3.71 3.71-3.71h4.138c2.05 0 3.71 1.662 3.71 3.71v4.14zm0-17.63c0 2.048-1.66 3.708-3.71 3.708h-4.137c-2.048 0-3.71-1.66-3.71-3.71v-4.137c0-2.048 1.662-3.708 3.71-3.708h4.138c2.05 0 3.71 1.66 3.71 3.71v4.137zm19.032 35.54c0 2.048-1.66 3.708-3.708 3.708h-4.14c-2.047 0-3.707-1.66-3.707-3.708v-4.14c0-2.047 1.66-3.707 3.707-3.707h4.14c2.047 0 3.707 1.66 3.707 3.708v4.14zm0-17.91c0 2.05-1.66 3.71-3.708 3.71h-4.14c-2.047 0-3.707-1.66-3.707-3.71v-4.138c0-2.048 1.66-3.71 3.707-3.71h4.14c2.047 0 3.707 1.662 3.707 3.71v4.14zm0-17.63c0 2.048-1.66 3.708-3.708 3.708h-4.14c-2.047 0-3.707-1.66-3.707-3.71v-4.137c0-2.048 1.66-3.708 3.707-3.708h4.14c2.047 0 3.707 1.66 3.707 3.71v4.137zM116 127.61c0 2.048-1.66 3.708-3.708 3.708h-4.14c-2.047 0-3.708-1.66-3.708-3.708v-4.14c0-2.047 1.66-3.707 3.71-3.707h4.138c2.048 0 3.708 1.66 3.708 3.708v4.14zm0-17.91c0 2.05-1.66 3.71-3.708 3.71h-4.14c-2.047 0-3.708-1.66-3.708-3.71v-4.138c0-2.048 1.66-3.71 3.71-3.71h4.138c2.048 0 3.708 1.662 3.708 3.71v4.14zm0-17.63c0 2.048-1.66 3.708-3.708 3.708h-4.14c-2.047 0-3.708-1.66-3.708-3.71v-4.137c0-2.048 1.66-3.708 3.71-3.708h4.138c2.048 0 3.708 1.66 3.708 3.71v4.137zm19.032 35.54c0 2.048-1.66 3.708-3.708 3.708h-4.14c-2.047 0-3.707-1.66-3.707-3.708v-4.14c0-2.047 1.66-3.707 3.708-3.707h4.14c2.047 0 3.707 1.66 3.707 3.708v4.14zm0-17.91c0 2.05-1.66 3.71-3.708 3.71h-4.14c-2.047 0-3.707-1.66-3.707-3.71v-4.138c0-2.048 1.66-3.71 3.708-3.71h4.14c2.047 0 3.707 1.662 3.707 3.71v4.14zm0-17.63c0 2.048-1.66 3.708-3.708 3.708h-4.14c-2.047 0-3.707-1.66-3.707-3.71v-4.137c0-2.048 1.66-3.708 3.708-3.708h4.14c2.047 0 3.707 1.66 3.707 3.71v4.137z' fill='%23fff'/%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3C/svg%3E"); } + .touch-control.car.getIn { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M112.826 27.04c3.368 2.647 6.287 4.85 9.743 7.38-5.187 6.85-10.147 13.085-15.288 19.41 2.753 3.265 5.108 5.824 8.002 9.566-5.86-.177-24.25 0-24.25 0-1.24-9.96-2.455-16.65-3.683-26.03 3.44 3.278 6.422 5.84 10.382 8.587 5.913-6.907 9.656-11.38 15.094-18.914z' fill='%23fff'/%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3Cpath d='M145.936 84.723c-.15-.282-.818-.517-1.126-.597-4.66-1.21-8.292 1.697-12.74 3.443-3.497-5.626-6.708-12.57-10.783-16.874-1.344-1.344-2.684-1.356-5.115-1.53-5.38-.387-14.963-.43-24.27-.322-9.308-.107-18.892-.065-24.27.32-2.432.176-3.772.188-5.116 1.532C58.44 75 55.23 81.944 51.733 87.57c-4.448-1.747-8.08-4.654-12.74-3.444-.308.08-.977.315-1.126.597-1.606 3.04 6.236 5.984 8.977 7.433-1.56 3.096-3.625 6.33-5.157 9.312-.42.815-.645 1.705-.645 4.02v19.378c0 3.144 2.59 5.627 5.733 5.584 3.948-.053 8.017.222 11.862.128 1.656-.04 3.193-.826 4.263-2.09 2.698-3.193 4.257-3.63 9.682-2.144 6.722 1.84 12.866 2.642 19.32 2.442 6.453.2 12.597-.6 19.32-2.442 5.424-1.486 6.983-1.05 9.68 2.143 1.07 1.265 2.608 2.05 4.264 2.09 3.845.095 7.914-.18 11.862-.127 3.144.043 5.733-2.44 5.733-5.584v-19.38c0-2.313-.225-3.203-.644-4.018-1.532-2.98-3.597-6.216-5.157-9.312 2.74-1.45 10.582-4.393 8.976-7.433zm-77.32 26.443c-.565.447-1.254.725-1.97.81l-.478.054c-7.588.912-30.085-.418-18.638-13.027 1.39-2.116 32.598 3.08 21.088 12.163zm37.6 13.318c-3.366 0-8.032.032-12.428.057-.034 0-.06-.008-.092-.01-.034.002-.058.01-.093.01h-.237v-.018l-1.465.01-1.463-.01v.018H90.2c-.035 0-.06-.008-.093-.01-.033.002-.058.01-.092.01-4.396-.024-9.062-.056-12.43-.056-2.86 0-4.687-2.144-4.687-3.71 0-2.447 1.598-4.326 4.86-4.288 4.027-.078 8.115-.017 12.198 0 .048 0 .1.03.15.044.052-.014.105-.044.152-.044.548-.002 1.096-.006 1.644-.01.547.004 1.095.008 1.643.01.047 0 .1.03.15.044.05-.014.104-.044.152-.044 4.083-.017 8.17-.078 12.198 0 3.262-.038 4.86 1.84 4.86 4.288 0 1.566-1.828 3.71-4.688 3.71zm-12.85-33.716v-.013l-1.465.007-1.463-.007v.013c-13.286.05-27.046-.036-34.213-.433 3.203-5.86 5.188-10.063 9.016-15.986 6.264 0 15.72.116 25.197.232v.036l1.465-.018 1.464.018v-.036c9.476-.116 18.933-.233 25.197-.233 3.828 5.922 5.813 10.125 9.016 15.985-7.168.397-20.928.483-34.214.433zm24.27 21.262l-.478-.055c-.717-.084-1.406-.362-1.972-.81-11.51-9.083 19.697-14.278 21.087-12.162 11.447 12.61-11.05 13.94-18.638 13.027z' fill='%23fff'/%3E%3C/svg%3E"); width: 13vmin; @@ -298,12 +305,15 @@ bottom: 50vmin; right: 4vmin; } + [data-state-car="1"] .touch-control.car.getIn { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3Cpath d='M145.936 84.723c-.15-.282-.818-.517-1.126-.597-4.66-1.21-8.292 1.697-12.74 3.443-3.497-5.626-6.708-12.57-10.783-16.874-1.344-1.344-2.684-1.356-5.115-1.53-5.38-.387-14.963-.43-24.27-.322-9.308-.107-18.892-.065-24.27.32-2.432.176-3.772.188-5.116 1.532C58.44 75 55.23 81.944 51.733 87.57c-4.448-1.747-8.08-4.654-12.74-3.444-.308.08-.977.315-1.126.597-1.606 3.04 6.236 5.984 8.977 7.433-1.56 3.096-3.625 6.33-5.157 9.312-.42.815-.645 1.705-.645 4.02v19.378c0 3.144 2.59 5.627 5.733 5.584 3.948-.053 8.017.222 11.862.128 1.656-.04 3.193-.826 4.263-2.09 2.698-3.193 4.257-3.63 9.682-2.144 6.722 1.84 12.866 2.642 19.32 2.442 6.453.2 12.597-.6 19.32-2.442 5.424-1.486 6.983-1.05 9.68 2.143 1.07 1.265 2.608 2.05 4.264 2.09 3.845.095 7.914-.18 11.862-.127 3.144.043 5.733-2.44 5.733-5.584v-19.38c0-2.313-.225-3.203-.644-4.018-1.533-2.98-3.597-6.216-5.157-9.312 2.74-1.45 10.582-4.393 8.976-7.433zm-77.32 26.443c-.565.447-1.254.725-1.97.81l-.478.054c-7.588.912-30.085-.418-18.638-13.027 1.39-2.116 32.598 3.08 21.088 12.163zm37.6 13.318c-3.366 0-8.032.032-12.428.057-.034 0-.06-.008-.092-.01-.034.002-.058.01-.093.01h-.237v-.018l-1.465.01-1.463-.01v.018H90.2c-.035 0-.06-.008-.093-.01-.033.002-.058.01-.092.01-4.396-.024-9.062-.056-12.43-.056-2.86 0-4.687-2.144-4.687-3.71 0-2.447 1.598-4.326 4.86-4.288 4.027-.078 8.115-.017 12.198 0 .048 0 .1.03.15.044.052-.014.105-.044.152-.044.548-.002 1.096-.006 1.644-.01.547.004 1.095.008 1.643.01.047 0 .1.03.15.044.05-.014.104-.044.152-.044 4.083-.017 8.17-.078 12.198 0 3.262-.038 4.86 1.84 4.86 4.288 0 1.566-1.828 3.71-4.688 3.71zm-12.85-33.716v-.013l-1.465.007-1.463-.007v.013c-13.286.05-27.046-.036-34.213-.433 3.203-5.86 5.188-10.063 9.016-15.986 6.264 0 15.72.116 25.197.232v.036l1.465-.018 1.464.018v-.036c9.476-.116 18.933-.233 25.197-.233 3.828 5.922 5.813 10.125 9.016 15.985-7.168.397-20.928.483-34.214.433zm24.27 21.262l-.478-.055c-.717-.084-1.406-.362-1.972-.81-11.51-9.083 19.697-14.278 21.087-12.162 11.447 12.61-11.05 13.94-18.638 13.027zM99.743 65.357c-3.368-2.648-6.287-4.85-9.743-7.38 5.186-6.85 10.146-13.086 15.287-19.41-2.753-3.266-5.108-5.825-8.002-9.567 5.86.177 24.25 0 24.25 0 1.24 9.962 2.455 16.65 3.683 26.03-3.44-3.277-6.42-5.84-10.38-8.587-5.914 6.907-9.657 11.38-15.095 18.914z' fill='%23fff'/%3E%3C/svg%3E"); } + [data-state-car="1"][data-state-bike="1"] .touch-control.car.getIn { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M99.743 65.357A339 339 0 0 0 90 57.977c5.186-6.85 10.146-13.086 15.287-19.41-2.753-3.266-5.108-5.825-8.002-9.567 5.859.177 24.25 0 24.25 0 1.241 9.962 2.455 16.65 3.683 26.03-3.439-3.277-6.421-5.84-10.381-8.586-5.913 6.906-9.656 11.38-15.094 18.913'/%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3Cpath fill='%23fff' d='M46.75 91.5c2.566 0 5.334.625 7.682 1.688l2.85-4.252a27.2 27.2 0 0 0-13.657-3.686 3.123 3.123 0 0 1-3.125-3.125v-6.25C40.5 63.815 50.314 54 62.375 54h6.25a3.123 3.123 0 0 1 3.125 3.125 3.123 3.123 0 0 1-3.125 3.125H65.5v6.25h9.375c1.044 0 2.02.522 2.6 1.392l3.24 4.858h39.239l1.568-2.353a8.74 8.74 0 0 1 7.279-3.897h15.741a3.123 3.123 0 0 1 3.125 3.125c0 8.03-6.089 14.664-13.889 15.53l-4.27 6.405c10.06.32 18.159 8.553 18.159 18.69 0 10.339-8.411 18.75-18.75 18.75-7.997 0-15.017-5.135-17.633-12.5H79.486c-4.156 0-7.766-2.96-8.578-7.034l-.443-2.213c-1.15-5.752-4.142-10.725-8.204-14.524l-2.648 3.948c3.792 3.594 5.887 8.563 5.887 13.573 0 10.089-8.482 18.75-18.75 18.75C36.41 129 28 120.589 28 110.25S36.41 91.5 46.75 91.5m82.167 24.979h-10.803c2.191 3.765 6.262 6.271 10.803 6.271 6.894 0 12.5-5.606 12.5-12.5s-5.606-12.5-12.5-12.5c-1.389 0-2.726.275-4.014.715l-1.092 1.638a8.72 8.72 0 0 1-7.278 3.897h12.384a6.25 6.25 0 0 1 6.25 6.25c0 3.452-2.798 6.229-6.25 6.229M46.75 122.75c6.771 0 12.5-5.687 12.5-12.5 0-2.889-1.012-5.77-3.208-8.249l-6.698 9.989a3.12 3.12 0 0 1-4.333.854 3.12 3.12 0 0 1-.855-4.333l6.7-9.991c-1.508-.528-2.515-.77-4.106-.77-6.894 0-12.5 5.606-12.5 12.5s5.606 12.5 12.5 12.5'/%3E%3C/svg%3E"); } + [data-state-cutscene="1"] .touch-control.car.getIn { display: block; background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3Cpath d='M76.024 51.614c7.215 4.49 16.193 20.678 21.925 25.955.753.693 1.82.92 2.793.598 4.258-1.4 14.066-5.376 15.932-2.672 3.824 5.225-9.416 11.092-18.357 13.963-2.217.71-4.65-.083-5.995-1.983-4.255-6.007-6.493-9.236-13.173-1.233-1.04 1.245-1.078 3.058-.07 4.327 2.815 3.543 6.912 7.27 10.335 10.875 1.484 1.562 1.625 3.955.347 5.69-4.775 6.48-14.643 17.82-19.528 22.582-.785.765-1.995.882-2.923.298l-3.353-2.115c-.973-.613-1.303-1.862-.785-2.888 2.79-5.523 8.93-12.284 13.5-17.32 1.277-1.407 1.42-3.497.373-5.082-1.985-3.006-3.752-4.64-6.992-6.593-.932-.56-2.13.09-2.152 1.178-.04 2.046-.125 4.604-.316 5.29-1.868 6.725-15.158 21.157-21.865 26.69-1.05.863-2.572.78-3.535-.177-.83-.827-1.43-1.534-2.252-2.584-.524-.67-.617-1.602-.202-2.345 4.63-8.278 11.298-13.945 16.69-21.242 1.184-1.605 1.62-3.644 1.243-5.603-.452-2.352-.806-4.846-.527-7.065 1.326-10.563 15.577-15.607 19.085-24.758.4-1.04-.364-1.807-.948-2.466-5.822-1.887-14.778 11.32-20.57 13.428-7.605-.868-4.03-6.424-.457-9.81 5.583-5.294 12.335-9.72 18.13-14.666 1.025-.873 2.506-.985 3.65-.274zM127.852 75.447c7.623 4.59 14.306 8.234 22.266 12.245-2.71 5.632-8.265 18.778-10.903 23.54-.713-2.753-1.568-5.598-3.717-10.243-.474-1.026-1.652-1.532-2.72-1.16-5.48 1.905-19.053 7.29-21.364 8.08-1.382-4.795-1.906-7.06-3.23-12.205 4.755-2.007 14.343-5.325 19.907-7.736 1.224-.53 1.907-1.84 1.663-3.15-.655-3.532-1.9-9.373-1.9-9.373z' fill='%23fff'/%3E%3Cpath d='M100.143 66.36c5.355 0 9.696-4.34 9.696-9.695s-4.342-9.697-9.697-9.697c-5.355 0-9.697 4.342-9.697 9.697 0 5.355 4.342 9.696 9.697 9.696z' fill='%23fff'/%3E%3C/svg%3E"); @@ -312,6 +322,7 @@ top: 10vmin; transform: scaleX(-1); } + .touch-control.drift, .touch-control.fist { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M48.575 82.876c12.76 9.6 13.945 21.293 21.284 33.61 7.5-2.263 9.523-4.338 15.8-9.184-.653-1.997-1.183-3.81-2.086-5.714-5.166-11.52-9.89-21.34-14.508-33.174 3.92 1.74 5.757 3.15 9.106 5.71.913.695 4.57-.75 6.567.232-1.565 1.058-6.015 3.003-6.02 4.535-.017 7.38 7.636 22.065 12.178 27.228l17.296-6.716c-2.535-9.46-7.944-28.736-7.385-38.22l.675-.592c5.033 7.74 3.37 24.505 11.9 34.71 8.255-.534 9.578-2.02 16.26-6.922-2.062-11.312-5.643-48.818-19.734-49.496-6.638 1.553-10.306 4.15-16.348 6.606-14.702-4.753-11.985-4.656-22.196 6.81-1.213 1.363-9.993 3.963-10.938 5.006-5.745 6.35-6.773 10.228-15.897 12.727-2.417.664-3.877 7.187-4.615 9.216-1.466 4.484-2.835 9.438-4.214 13.997-.826 2.733-1.336 5.286.36 7.842 2.363 3.836 8.693 2.807 10.81 6.15 2.377 3.75 2.937 13.18 8.175 14.348 3.438.767 7.58-1.766 10.76-3.324-3.724-12.515-11.076-23.942-17.23-35.384z' fill='%23fff'/%3E%3Cpath d='M139.29 76.574c-.832-3.267-4.175-4.872-6.21-7.05-.655-.7-1.874-5.424-2.362-6.688-.64-1.657-.942-2.7-2.707-3.583-1.492 1.99 8.082 27.39 5.548 33.407-4.045 9.602-12.13 6.892-19.555 11.084-2.274 1.532-2.86 2-5.653 1.374l1.175-2.213-.338-.922c-4.032-1.435-11.41 4.362-14.103 7.316.142 1.925.35 6.11.773 7.77 2.39 4.477 4.858 10.595 9.717 12.15 7.577.282 10.958-3.92 16.89-8.488.497-.168 3.113-1.657 3.73-1.998 2.844-1.193 8.088-3.242 10.18-5.41.918-.95 1.047-3.01.814-4.262-.63-3.387 1.204-7.433 2.12-11.082 1.724-6.848 1.73-14.566-.017-21.406zM78.953 121.303c-3.905 1.578-14.127.496-16.074 3.307.417 1.323.318 1.31 1.32 2.24 3.492 2.044 8.896 3.2 12.93 4.768 5.04.56 6.047-1.212 9.282-4.705 2.62-3.386 7.65-8.2 3.624-11.993-2.683.503-7.825 5.067-11.084 6.383z' fill='%23fff'/%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3C/svg%3E"); @@ -320,12 +331,15 @@ bottom: 33vmin; right: 3vmin; } + .touch-control.fist { display: block; } + [data-state-gun="1"] .touch-control.fist { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M113.275 2.388c11.083 2.943 21.506 7.96 30.718 14.787-.832-.136-4.625-.767-5.06-1-4.285-2.27-23.49-11.08-25.658-13.787zM91.542 64.147c.873.824 3.194 5.202 4.528 6.765 5.86 6.868 13.74 14.98 21.192 19.97-9.033 9.032-43.144 42.087-45.875 44.26-7.878-4.435-22.384-19.248-26.423-27.516 6.858-6.858 36.106-34.665 46.578-43.48zM132.3 37.525c2.3-.314 5.298-.835 7.14-.132 4.912 1.63 8.65 7.55 8.1 9.82-3.06 14.093-14.886 29.83-25.856 39.173-1.358-1.31-3.365-2.734-4.907-3.9-9.16-7.094-13.797-13.086-20.37-22.29 7.514-9.293 23.767-20.395 35.892-22.67zM25.277 48.865c1.607 0 3.792 18.945 4.335 21.955 2.132 11.736 4.433 23.442 6.902 35.113 2.78 13.47 7.206 24.874 2.163 38.387-.556 1.49-3.318.808-5.196-2.646-2.13-3.616-3.578-7.593-4.27-11.732-1.248-7.246-1.887-25.434-2.382-33.256-.706-11.162-3.83-46.71-1.55-47.82zM54.492 41.21c.196-.76 1.32-.616 1.307.17-.165 10.494-4.643 38.773-5.653 43.64-.593 2.857-1.946 7.165-4.094 9.234-1.1 1.06-1.28-.113-1.28-.113-1.642-7.586.798-17.82 2.777-26.036l6.942-26.893zM95.508 154.367c-7.81.656-25.895 2.686-33.246 2.512-4.196-.08-8.342-.93-12.23-2.51-3.693-1.35-4.77-3.983-3.38-4.75 12.63-6.968 24.56-4.26 38.29-3.48 11.908.734 23.824 1.296 35.746 1.687 3.058.097 22.118-.514 22.353 1.076-.765 2.417-36.386 4.53-47.532 5.464zM113.266 129.685c-7.84 3.16-17.605 7.072-25.35 6.56 0 0-1.186-.01-.3-1.25 1.733-2.428 5.796-4.398 8.534-5.403 4.67-1.71 31.988-10.28 42.345-11.978.775-.128 1.083.963.358 1.27l-25.587 10.8z' fill='%23fff'/%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3C/svg%3E"); } + [data-state-car="1"][data-state-car-gun="1"][data-state-bike="1"][data-state-menu="0"] .touch-control.fist, [data-state-car="1"][data-state-panzer="1"] .touch-control.fist, [data-state-car="1"][data-state-car-with-weapon="1"] .touch-control.fist, @@ -334,18 +348,22 @@ right: 24vmin; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M113.275 2.388a90.7 90.7 0 0 1 30.718 14.787c-.832-.136-4.625-.767-5.061-.999-4.284-2.272-23.488-11.081-25.657-13.788M91.542 64.147c.873.824 3.194 5.202 4.528 6.765 5.86 6.868 13.741 14.979 21.192 19.969-9.033 9.033-43.143 42.088-45.875 44.262-7.878-4.436-22.384-19.249-26.423-27.517 6.858-6.858 36.106-34.665 46.578-43.48M132.299 37.525c2.302-.314 5.299-.835 7.14-.132 4.913 1.629 8.651 7.55 8.1 9.821-3.06 14.092-14.885 29.829-25.855 39.172-1.358-1.311-3.365-2.734-4.907-3.901-9.159-7.093-13.797-13.085-20.37-22.29 7.514-9.292 23.767-20.394 35.892-22.67M25.277 48.866c1.607 0 3.792 18.944 4.336 21.954a1242 1242 0 0 0 6.901 35.113c2.781 13.47 7.206 24.874 2.163 38.387-.556 1.49-3.318.808-5.196-2.646a34.2 34.2 0 0 1-4.271-11.732c-1.248-7.246-1.887-25.434-2.382-33.256-.706-11.162-3.83-46.71-1.551-47.82M54.492 41.21c.196-.76 1.32-.616 1.307.17-.164 10.494-4.642 38.773-5.652 43.641-.593 2.856-1.946 7.164-4.094 9.233-1.1 1.06-1.281-.113-1.281-.113-1.64-7.587.8-17.82 2.778-26.037zM95.508 154.367c-7.81.656-25.895 2.686-33.246 2.512a34.2 34.2 0 0 1-12.231-2.508c-3.692-1.352-4.77-3.984-3.378-4.752 12.63-6.967 24.558-4.259 38.29-3.48q17.861 1.102 35.745 1.688c3.058.097 22.118-.514 22.353 1.076-.766 2.417-36.387 4.53-47.533 5.464M113.266 129.685c-7.839 3.16-17.605 7.072-25.35 6.56 0 0-1.186-.009-.3-1.251 1.733-2.427 5.796-4.397 8.534-5.402 4.669-1.711 31.988-10.279 42.345-11.978.775-.128 1.083.963.358 1.269z'/%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3C/svg%3E"); } + [data-state-car="1"][data-state-hunter="1"] .touch-control.fist { right: 3vmin; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M66.725 2.388a90.7 90.7 0 0 0-30.718 14.787c.832-.136 4.625-.767 5.061-.999 4.284-2.272 23.488-11.081 25.657-13.788M154.723 48.866c-1.607 0-3.792 18.944-4.335 21.954a1244 1244 0 0 1-6.902 35.113c-2.781 13.47-7.206 24.874-2.163 38.387.557 1.49 3.318.808 5.196-2.646a34.2 34.2 0 0 0 4.271-11.732c1.248-7.246 1.887-25.434 2.382-33.256.706-11.162 3.83-46.71 1.551-47.82M125.508 41.21c-.196-.76-1.32-.616-1.307.17.164 10.494 4.642 38.773 5.652 43.641.593 2.856 1.946 7.164 4.094 9.233 1.099 1.06 1.281-.113 1.281-.113 1.641-7.587-.8-17.82-2.778-26.037zM84.492 154.367c7.81.656 25.895 2.686 33.246 2.512a34.2 34.2 0 0 0 12.231-2.508c3.692-1.352 4.77-3.984 3.378-4.752-12.629-6.967-24.558-4.259-38.29-3.48a1240 1240 0 0 1-35.745 1.688c-3.057.097-22.117-.514-22.353 1.076.766 2.417 36.387 4.53 47.533 5.464M66.734 129.685c7.839 3.16 17.605 7.072 25.35 6.56 0 0 1.186-.009.3-1.251-1.733-2.427-5.796-4.397-8.534-5.402-4.67-1.711-31.988-10.279-42.345-11.978-.776-.128-1.083.963-.358 1.269z'/%3E%3Cpath fill='%23fff' d='M90 5.243C43.265 5.243 5.243 43.265 5.243 90S43.265 174.757 90 174.757 174.757 136.735 174.757 90 136.735 5.243 90 5.243M90 0c49.706 0 90 40.294 90 90s-40.294 90-90 90S0 139.706 0 90 40.294 0 90 0'/%3E%3Cpath fill='%23fff' d='M70.28 62.378C64.595 57.72 46.92 44.362 44.756 46.676 42.594 48.993 56.679 66.19 61.709 71.55l1.906 1.88q.234.23.476.455l9.248 8.643L75.502 95.5l5.521-5.791 10.692 9.995 18.867 31.636c.373.349 1.283-.019 2.033-.822l5.627-6.02-1.121-1.047 2.598-1.033 2.083 1.057c.013-.005 3.15-1.164 4.246-4.543l-1.175-2.027.422-3.218 1.628 1.522 5.626-6.019c.752-.804 1.057-1.736.684-2.085l-32.292-16.18-11.144-10.418L96 74l-14.643-1.38-8.586-8.024q-.24-.225-.485-.445z'/%3E%3C/svg%3E"); } + [data-state-car="1"][data-state-hunter="1"] .touch-control.drift { right: 24vmin; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M113.275 2.388a90.7 90.7 0 0 1 30.718 14.787c-.832-.136-4.625-.767-5.061-.999-4.284-2.272-23.488-11.081-25.657-13.788M91.542 64.147c.873.824 3.194 5.202 4.528 6.765 5.86 6.868 13.741 14.979 21.192 19.969-9.033 9.033-43.143 42.088-45.875 44.262-7.878-4.436-22.384-19.249-26.423-27.517 6.858-6.858 36.106-34.665 46.578-43.48M132.299 37.525c2.302-.314 5.299-.835 7.14-.132 4.913 1.629 8.651 7.55 8.1 9.821-3.06 14.092-14.885 29.829-25.855 39.172-1.358-1.311-3.365-2.734-4.907-3.901-9.159-7.093-13.797-13.085-20.37-22.29 7.514-9.292 23.767-20.394 35.892-22.67M25.277 48.866c1.607 0 3.792 18.944 4.336 21.954a1242 1242 0 0 0 6.901 35.113c2.781 13.47 7.206 24.874 2.163 38.387-.556 1.49-3.318.808-5.196-2.646a34.2 34.2 0 0 1-4.271-11.732c-1.248-7.246-1.887-25.434-2.382-33.256-.706-11.162-3.83-46.71-1.551-47.82M54.492 41.21c.196-.76 1.32-.616 1.307.17-.164 10.494-4.642 38.773-5.652 43.641-.593 2.856-1.946 7.164-4.094 9.233-1.1 1.06-1.281-.113-1.281-.113-1.64-7.587.8-17.82 2.778-26.037zM95.508 154.367c-7.81.656-25.895 2.686-33.246 2.512a34.2 34.2 0 0 1-12.231-2.508c-3.692-1.352-4.77-3.984-3.378-4.752 12.63-6.967 24.558-4.259 38.29-3.48q17.861 1.102 35.745 1.688c3.058.097 22.118-.514 22.353 1.076-.766 2.417-36.387 4.53-47.533 5.464M113.266 129.685c-7.839 3.16-17.605 7.072-25.35 6.56 0 0-1.186-.009-.3-1.251 1.733-2.427 5.796-4.397 8.534-5.402 4.669-1.711 31.988-10.279 42.345-11.978.775-.128 1.083.963.358 1.269z'/%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3C/svg%3E"); } + [data-state-cutscene="0"] .touch-control.drift { display: block; background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M155.682 31.545c-3.006 1.738-16.654 5.808-18.21 4.298 0 0-.33-.214-.356-.378-.03-.185.247-.647.247-.647 2.298-3.75 9.56-8.042 13.94-7.783 1.106.946 3.457 3.465 4.38 4.51zM111.352 116.232c2.337.907 12.1 8.543 14.915 10.923-11.544 12.374-18.29 18.525-31.07 33.505-1.35 1.847-2.6 2.928-4.24 4.493-1.313-4.007-6.348-22.645-5.487-25.52 1.8-6.01 20.32-19.724 25.882-23.4zM112.906 85.722c2.77-1.256 6.726 8.143 8.302 11.01-3.388 2.724-11.924 7.278-16.048 9.972-7.097 5.04-10.673 7.897-17.018 13.703-1.472 1.365-3.248 2.77-4.808 4.066 2.037-13.266 4.066-23.427 6.064-25.13 5.61-4.777 11.77-7.383 23.508-13.62zM124.86 102.533c7.296 3.84 11.244 7.396 17.462 11.245 1.214 2.833 8.74 16.567 8.48 19.61-.708.893-1.806-.068-3.5-.917-8.988-5.306-23.542-15.6-31.213-22.33l8.77-7.607zM27.355 74.804c.092.007.188-.002.278.022 3.06.84 12.335 19.733 14.308 23.555-1.23 4.286-2.436 8.636-4.955 12.362l-.967.07c-4.586-6.8-8.516-14.807-12.522-21.996-4.435-7.96-6.085-11.68 3.86-14.012zM122.503 60.503c1.536-.426 1.588.393 2 .758 1.73 2.046 2.22 4.287 3.075 6.863-5.837 3.864-11.89 6.716-17.838 10.003-6.234 3.446-9.964 6.363-15.44 10.718-1.22.978-2.237 1.895-3.04 1.824 0 0-1.122-.6.143-3.004 5.136-9.837 8.13-15.778 11.467-18.125 4.45-3.133 9.86-5.352 19.633-9.037zM46.743 51.525c.964-.29 1.178.387 1.178.387 1.29 1.49 7.535 16.27 9.424 20.48.137.303.108.654-.076.933-2.05 3.11-4.47 5.94-6.956 8.704-.448.497-1.26.406-1.573-.186-2.7-5.09-7.62-19.058-9.614-25.58-.32-1.045.232-2.154 1.252-2.546 2.152-.83 3.854-1.442 6.365-2.193zM127.027 76.845c2.76 1.33 9.805 7.913 12.803 11.666.145 3.92 1.036 11.92 1.2 14.315 0 0-.105.47-.808.218-.687-.246-2.332-1.755-2.332-1.755-3.652-3.105-15.423-15.795-18.016-19.81 1.842-1.422 5.098-3.414 7.153-4.633zM69.644 38.085c2.6 3.174 5.75 9.65 7.46 13.187.288.596.233 1.31-.16 1.842-2.188 2.962-4.556 5.154-7.232 7.803l-.64.537c-.983.82-2.484.387-2.87-.834-1.352-4.285-3.102-13.898-3.242-16.473-.156-2.856 1.723-4.053 4.07-6.24.757-.708 1.957-.624 2.614.178zM135.857 40.008c1.003 2.922 1.507 5.006 2.072 7.003-3.807 3.227-22.617 11.036-27.787 12.938 0 0-.293.147-.507-.11-.095-.113.142-.576.142-.576 3.136-7.787 17.896-17.02 26.08-19.254zM83.334 25.325c-3.313 1.153-13.9 4.647-16.127 5.903 0 0-.67.453.224.51 4.504.28 9.56-.074 14.934-.395.06 1.694 1.705 13.03 2.015 16.282 3.686-2.24 6.86-4.025 9.928-6.238-2.245-6.242-9.78-15.065-10.974-16.062zM56.74 34.11c2.31-.46 4.235 3.45 3.205 4.743-4.004 3.876-17.752 7.938-25.022 10.24-1.56.533-3.257 1.266-3.624.704-.21-.32.057-.7.057-.7 5.355-6.725 17.36-11.246 25.383-14.986zM138.843 54.34c1.357 1.826 6.14 9.517 5.946 10.476-.89 4.402-1.605 7.906-3.48 10.15 0 0-6.736-8.783-10.172-16.53 2.456-1.77 7.705-4.096 7.705-4.096zM94.488 20.795c.124-.764.86-1.274 1.616-1.104 5.85 1.32 14.246 5.35 14.494 12.28.048 1.358-1.085 1.986-2.067 2.683-.08.058-.164.107-.257.146-1.45.612-3.26 1.207-4.89 1.748-.667.222-1.38-.116-1.654-.764-2.076-4.923-4.55-9.4-7.1-14.108-.142-.26-.194-.564-.146-.858l.004-.023zM119.17 16.514c5.266.707 12.525 3.07 8.695 8.922-.128.195-.303.36-.506.476-1.196.687-2.63 1.386-3.96 2.035-.716.35-1.58.065-1.96-.636-1.825-3.38-2.657-5.633-3.852-8.795-.397-1.05.468-2.15 1.582-2zM143.037 41.264c2.343-.878 3.948-1.256 6.465-1.728 1.837 3.403 3.175 4.957 4.26 8.807-1.53 1.6-3.066 3.735-5.395 5.158-1.413-3.51-4.078-8.518-5.33-12.236z' fill='%23fff'/%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3Cpath d='M9.233 114.513c2.605-1.168 8.91-4.608 11.482-5.737 1.302-.572 2.815.016 3.398 1.314l9.596 21.337c.12.267.193.552.217.843l1.207 14.184c.195 2.293-6.607 9.28-6.443 3.844l-8.125-6.772c-.283-.236-.513-.53-.673-.862l-11.93-24.657c-.638-1.316-.062-2.896 1.27-3.494zM5.986 87.782c2.903 2.448 10.414 6.7 12.89 8.772 1.162.97 1.106 2.768-.11 3.668l-12.46 5.714c-1.194.884-2.898.444-3.516-.906l-1.05-2.3c-.157-.342-.228-.715-.21-1.09l.62-12.188c.097-1.93 2.358-2.915 3.836-1.67zM6.42 61.874l23.986-5.218c.865-.158 1.71.365 1.953 1.21l2.333 8.086c.253.876-.225 1.797-1.087 2.095l-27.67 9.636c-1.187.41-2.397-.554-2.262-1.803l1.35-12.504c.082-.755.65-1.366 1.397-1.502zM154.29 144.275l-18.395-7.887c-.752-.323-1.627-.11-2.148.522l-4.868 5.913c-.724.878-.486 2.196.5 2.765l16.71 9.665c.77.445 1.747.277 2.324-.4l6.553-7.69c.805-.944.465-2.4-.676-2.888zM102.498 172.835c4.77-6.2 14.573-19.017 17.55-22.908.492-.646 1.4-.798 2.08-.35L138.1 160.12c.94.623.927 2.008-.03 2.604-3.956 2.46-12.31 7.632-12.76 7.707-.438.074-14.122 3.216-21.245 4.853-1.414.326-2.453-1.298-1.57-2.448z' fill='%23fff'/%3E%3C/svg%3E"); } + [data-state-gun="1"][data-state-cutscene="1"] .touch-control.drift, [data-state-gun="1"][data-state-menu="1"] .touch-control.drift, [data-state-car="0"] .touch-control.drift, @@ -353,6 +371,7 @@ [data-state-car="1"] .touch-control.fist { display: none; } + [data-state-gun="1"][data-state-car="0"] .drift, [data-state-scope-mode="1"] .touch-control.drift, [data-state-scope-gun="1"] .touch-control.drift { @@ -360,6 +379,7 @@ right: 24vmin; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M157.486 83.922c-4.036.033-12.021-.362-16.227-.848-.999-3.95-2.811-9.163-4.866-13.435-7.087-14.734-18.804-24.032-34.294-28.463q-2.236-.63-4.499-1.166c.685-5.796.983-10.637 1.819-16.052-5.452-.022-10.707.115-16.346 0 1.166 5.86 1.395 9.12 2.14 14.95-26.574 7.485-36.978 16.742-45.46 42.94-7.602-.443-9.812-.706-16.932-1.446.037 4.748.19 9.58.001 14.284 5.89-.767 10.425-.813 16.596-1.278 3.269 15.554 10.295 27.641 23.73 36.47 7.41 4.868 11.781 5.302 19.844 7.767-.624 6.071-1.247 11.319-2.077 16.85 5.389.044 9.039.386 14.42 0 .117-1.701-.354-12.296-.163-16.043 24.729-6.101 38.088-17.492 45.575-42.44q8.36.945 16.739 1.706c0-5.35.265-8.555 0-13.797m-23.783 13.194c-3.816 18.191-19.862 34.33-38.772 33.624a1.045 1.045 0 0 1-.993-.999 938 938 0 0 1-.669-19.655c-.087-3.82-.04-9.966-.587-13.764a1 1 0 0 0-.659-.81c-1.287-.444-3.383-.465-4.647-.013-.34.122-.579.42-.643.776-1.58 8.697-2.178 24.285-3.616 34.404l-.398.406c-6.008.124-16.262-5.4-20.869-9.213-8.833-7.315-13.464-16.093-14.927-27.149-.08-.599.38-1.143.983-1.17 6.57-.303 30.78-.61 34.904-1.703.333-.088.586-.348.699-.674.478-1.383.376-1.815.173-3.576-.13-.527-.435-.768-1.008-1.13-9.693-2.195-25.306-1.94-34.996-3.383a1.034 1.034 0 0 1-.855-1.24c3.68-16.786 17.564-34.374 37.265-35.144.592-.023 1.334.168 1.334.168.665 6.312.607 30.03 2.203 35.762.112.403.471.687.887.733 1.387.154 1.977.229 3.302-.068.378-.085.683-.365.785-.74 1.57-5.78 3.27-27.393 4.078-35.124a1.04 1.04 0 0 1 1.22-.912c11.148 2.031 19.236 6.252 26.807 15.434 3.31 4.016 8.921 14.758 8.888 20.462a1.03 1.03 0 0 1-.867 1.016c-10.455 1.676-30.495 1.607-36.282 3.388a.94.94 0 0 0-.582.485c-.49 1.042-.395 2.322-.344 3.568.015.362.21.7.532.865 5.259 2.69 31.19 2.207 37.311 4.104.602.139.423.888.343 1.272'/%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3C/svg%3E"); } + .touch-control.run { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3Cpath d='M76.024 51.614c7.215 4.49 16.193 20.678 21.925 25.955.753.693 1.82.92 2.793.598 4.258-1.4 14.066-5.376 15.932-2.672 3.824 5.225-9.416 11.092-18.357 13.963-2.217.71-4.65-.083-5.995-1.983-4.255-6.007-6.493-9.236-13.173-1.233-1.04 1.245-1.078 3.058-.07 4.327 2.815 3.543 6.912 7.27 10.335 10.875 1.484 1.562 1.625 3.955.347 5.69-4.775 6.48-14.643 17.82-19.528 22.582-.785.765-1.995.882-2.923.298l-3.353-2.115c-.973-.613-1.303-1.862-.785-2.888 2.79-5.523 8.93-12.284 13.5-17.32 1.277-1.407 1.42-3.497.373-5.082-1.985-3.006-3.752-4.64-6.992-6.593-.932-.56-2.13.09-2.152 1.178-.04 2.046-.125 4.604-.316 5.29-1.868 6.725-15.158 21.157-21.865 26.69-1.05.863-2.572.78-3.535-.177-.83-.827-1.43-1.534-2.252-2.584-.524-.67-.617-1.602-.202-2.345 4.63-8.278 11.298-13.945 16.69-21.242 1.184-1.605 1.62-3.644 1.243-5.603-.452-2.352-.806-4.846-.527-7.065 1.326-10.563 15.577-15.607 19.085-24.758.4-1.04-.364-1.807-.948-2.466-5.822-1.887-14.778 11.32-20.57 13.428-7.605-.868-4.03-6.424-.457-9.81 5.583-5.294 12.335-9.72 18.13-14.666 1.025-.873 2.506-.985 3.65-.274zM127.852 75.447c7.623 4.59 14.306 8.234 22.266 12.245-2.71 5.632-8.265 18.778-10.903 23.54-.713-2.753-1.568-5.598-3.717-10.243-.474-1.026-1.652-1.532-2.72-1.16-5.48 1.905-19.053 7.29-21.364 8.08-1.382-4.795-1.906-7.06-3.23-12.205 4.755-2.007 14.343-5.325 19.907-7.736 1.224-.53 1.907-1.84 1.663-3.15-.655-3.532-1.9-9.373-1.9-9.373z' fill='%23fff'/%3E%3Cpath d='M100.143 66.36c5.355 0 9.696-4.34 9.696-9.695s-4.342-9.697-9.697-9.697c-5.355 0-9.697 4.342-9.697 9.697 0 5.355 4.342 9.696 9.697 9.696z' fill='%23fff'/%3E%3C/svg%3E"); width: 20vmin; @@ -367,28 +387,35 @@ bottom: 10vmin; right: 1vmin; } + [data-state-cutscene="1"] .touch-control { display: none; } + [data-state-car="1"] .touch-control.run { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M130.653 110.546c-.205-22.184.412-44.5-.35-66.68-.11-3.174-2.71-5.692-5.885-5.737-15.644-.22-53.415-.515-53.53-.504-.915.086-3.722 0-4.734 2.458-2.344 6.024 1.557 18.59-.31 25.085-1.58 5.497-6.287 10.43-9.786 14.817-3.567 4.474-7.68 9.03-9.642 14.49-1.616 4.5-1.678 21.996-1.31 35.028.216 7.58 6.324 13.672 13.903 13.868 14.88.387 31.588-.018 45.203.086 4.19-.017 8.38-.15 12.568-.3 7.59-.273 13.65-6.402 13.82-13.993.14-6.174.11-12.334.053-18.62zm-60.76 17.423c0 2.21-1.793 4.002-4.004 4.002H61.42c-2.21 0-4.003-1.793-4.003-4.003v-4.47c0-2.21 1.79-4.002 4.002-4.002h4.467c2.21 0 4.002 1.792 4.002 4.003v4.47zm0-24.503c0 2.21-1.793 4.003-4.004 4.003H61.42c-2.21 0-4.003-1.792-4.003-4.003V99c0-2.21 1.79-4.003 4.002-4.003h4.467c2.21 0 4.002 1.792 4.002 4.003v4.467zm23.48 24.502c0 2.21-1.79 4.002-4.002 4.002h-4.466c-2.21 0-4.003-1.793-4.003-4.003v-4.47c0-2.21 1.793-4.002 4.004-4.002h4.467c2.212 0 4.004 1.792 4.004 4.003v4.47zm0-24.503c0 2.21-1.79 4.003-4.002 4.003h-4.466c-2.21 0-4.003-1.792-4.003-4.003V99c0-2.21 1.793-4.003 4.004-4.003h4.467c2.212 0 4.004 1.792 4.004 4.003v4.467zm0-23.713c0 2.21-1.79 4.003-4.002 4.003h-4.466c-2.21 0-4.003-1.793-4.003-4.003v-4.468c0-2.21 1.793-4.003 4.004-4.003h4.467c2.212 0 4.004 1.793 4.004 4.003v4.468zm0-24.33c0 2.212-1.79 4.004-4.002 4.004h-4.466c-2.21 0-4.003-1.792-4.003-4.003v-4.467c0-2.21 1.793-4.003 4.004-4.003h4.467c2.212 0 4.004 1.792 4.004 4.003v4.467zm24.48 72.545c0 2.21-1.792 4.002-4.003 4.002h-4.467c-2.21 0-4.003-1.793-4.003-4.003v-4.47c0-2.21 1.792-4.002 4.003-4.002h4.467c2.21 0 4.003 1.792 4.003 4.003v4.47zm0-24.503c0 2.21-1.792 4.003-4.003 4.003h-4.467c-2.21 0-4.003-1.792-4.003-4.003V99c0-2.21 1.792-4.003 4.003-4.003h4.467c2.21 0 4.003 1.792 4.003 4.003v4.467zm0-23.713c0 2.21-1.792 4.003-4.003 4.003h-4.467c-2.21 0-4.003-1.793-4.003-4.003v-4.468c0-2.21 1.792-4.003 4.003-4.003h4.467c2.21 0 4.003 1.793 4.003 4.003v4.468zm0-24.33c0 2.212-1.792 4.004-4.003 4.004h-4.467c-2.21 0-4.003-1.792-4.003-4.003v-4.467c0-2.21 1.792-4.003 4.003-4.003h4.467c2.21 0 4.003 1.792 4.003 4.003v4.467z' fill='%23fff'/%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3C/svg%3E"); } + [data-state-menu="1"] .touch-control.run, [data-state-cutscene="1"] .touch-control.run { display: block; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M54.539 45.298h46.587c3.841 3.84 36.78 36.383 46.447 45.99-14.743 15.222-31.813 32.923-46.41 47.52-2.083.402-10.467.059-12.972.036-11.478-.104-22.322.207-33.781.207 2.04-2.375 4.171-4.654 6.326-6.925 13.37-13.37 25.613-27.106 39.014-40.506-10.682-11.63-22.021-22.89-32.823-34.228-3.948-4.143-7.811-7.558-12.388-12.094'/%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3C/svg%3E"); } + [data-state-cutscene="1"] .touch-control.run { bottom: auto; top: 12vmin; } + [data-state-menu="1"] .touch-control { display: none; } + [data-is-touch="1"][data-state-menu="1"] .touch-control.car.getIn { display: block; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M113.073 57.392C102.271 68.73 90.932 79.99 80.25 91.62c13.4 13.4 25.643 27.136 39.014 40.506 2.155 2.271 4.285 4.55 6.326 6.925-11.459 0-22.303-.311-33.78-.207-2.506.023-10.89.366-12.973-.036-14.597-14.597-31.667-32.298-46.41-47.52 9.667-9.607 42.606-42.15 46.447-45.99h46.587c-4.577 4.536-8.44 7.951-12.388 12.094'/%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3C/svg%3E"); } + [data-state-car="1"] .touch-control.camera { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M30.244 52.207c6.014 2.763 26.82 11.125 32.152 12.538V51.102c26.8 0 56.51.576 83.09.576-.075 19.047-.28 36.933-.28 57.066-7.028-.01-23.055 0-29.213 0-2.09 2.16-2.28 2.35-4.094 4.794 4.44 6.457 10.777 12.274 15.418 18.58 1.225 2.162 2.345 3.47.184 4.87-6.622 1.956-16.95-11.9-23.395-17.2-6.045 5.008-11.17 11.167-17.56 15.644-2.445 1.715-4.743 2.255-6.222-.86.503-6.18 12.373-15.065 16.717-21.105l-2.993-4.723c-11.76-.215-20.865.347-31.654 0V97.068c-4.142.907-9.568 3.54-13.618 5.218-6.152 2.565-12.37 4.974-18.643 7.225.02-12.474.11-50.317.11-57.303z' fill='%23fff'/%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3C/svg%3E"); width: 10vmin; @@ -396,6 +423,7 @@ bottom: 65vmin; right: 17vmin; } + [data-state-job="1"] .touch-control.job { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3Cpath fill='%23fff' d='M132.677 92.029v-8.324q2.775 0 4.336-1.907 1.647-1.908 1.647-5.376h14.393q0 4.422-2.688 7.977-2.687 3.555-7.283 5.636-4.596 1.995-10.405 1.994m-4.248 28.699v-10.925h5.202v10.925zm0-26.965V83.705h5.116v10.058zm11.098 8.844v-1.648h14.393v1.648zm0-1.561q0-3.468-1.647-5.376-1.561-1.907-4.335-1.907v-8.324q5.809 0 10.404 2.081 4.595 1.995 7.283 5.55 2.688 3.467 2.688 7.976zm-11.098-31.82V58.3h4.335v10.924zm24.624 7.283H138.66q0-3.468-1.647-5.376-1.561-1.908-4.336-1.908V58.301q5.81 0 10.405 2.427 4.595 2.342 7.283 6.503 2.688 4.076 2.688 9.278m.867 26.011q0 5.203-2.688 9.364-2.688 4.076-7.283 6.503-4.595 2.341-10.404 2.341v-10.925q2.773 0 4.335-1.907 1.647-1.908 1.647-5.376zm-39.017 18.208V58.301h14.393v62.427zM90.528 103.907V75.035h14.393v28.872zm-26.185 0V75.035h14.393v28.872zm40.578-28.699H90.528q0-3.468-1.647-5.376-1.56-1.907-4.336-1.907V57q5.81 0 10.405 2.428 4.596 2.34 7.283 6.503 2.688 4.075 2.688 9.277m-40.578 0q0-5.202 2.688-9.277 2.688-4.163 7.283-6.503Q78.91 57 84.72 57v10.925q-2.775 0-4.422 1.907-1.56 1.908-1.56 5.376zm40.578 28.613q0 5.202-2.688 9.364-2.688 4.075-7.283 6.503-4.596 2.34-10.405 2.341v-10.925q2.775 0 4.336-1.908 1.647-1.907 1.647-5.375zm-40.578 0h14.393q0 3.468 1.56 5.375 1.649 1.908 4.423 1.908v10.925q-5.81 0-10.405-2.341-4.595-2.428-7.283-6.503-2.688-4.162-2.688-9.364M39.96 103.994V58.301h14.392v45.693zm14.392-.087q0 8.585-4.682 13.44-4.68 4.768-13.092 4.768l-1.734-10.924q1.647 0 2.774-.867 1.128-.868 1.734-2.515.608-1.647.608-3.902zm-23.323 5.81a6.9 6.9 0 0 0 1.734 1.127q.955.347 2.08.347l1.735 10.924q-2.775 0-5.376-.78-2.514-.78-5.202-2.688z'/%3E%3C/svg%3E"); width: 10vmin; @@ -403,6 +431,7 @@ bottom: 65vmin; right: 29vmin; } + [data-state-car="1"] .touch-control.horn { background-image: url("data:image/svg+xml,%3Csvg width='180' height='180' viewBox='0 0 180 180' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757 0 46.735-38.022 84.757-84.757 84.757-46.735 0-84.757-38.022-84.757-84.757C5.243 43.265 43.265 5.243 90 5.243zM90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0z' fill='%23fff'/%3E%3Cpath d='M150.61 55.75c-3.294.562-10.056 14.44-18.115 18.47-14.23 7.11-33.75 4.134-49.36 4.746-11.985.468-24.48.077-36.367-.507-4.03-.2-12.315-3.44-16.54-4.665 0 5.98-.29 15.515-.29 21.525 6.753-1.41 10.284-4.51 19.012-5.71-2.487 13.625-6.334 24.215 9.3 32.604 1.48.804 5.694 1.71 7.578 1.91 15.87 1.692 35.367 2.61 48.896-1.455 12.814-3.85 14.838-21.134 10.252-31.645 0 0 .62-.235.84-.235 10.992 0 17.692 19.47 23.22 20.232 7.792-.898 6.015-57.966 1.574-55.27zM116.143 102.1c0 7.063-5.726 12.79-12.79 12.79H69.25c-7.064 0-12.79-5.727-12.79-12.79 0-7.063 5.726-12.788 12.79-12.788h34.104c7.063 0 12.79 5.725 12.79 12.788z' fill='%23fff'/%3E%3C/svg%3E"); width: 10vmin; @@ -410,6 +439,7 @@ bottom: 65vmin; right: 5vmin; } + [data-state-mobring="1"] .touch-control.mobile { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3Cpath fill='%23fff' d='m54.761 74.215 45.326 77.923a1 1 0 0 0 1.365.362l32.843-19.002c.41-.237.591-.729.375-1.151-.523-1.023-1.641-2.823-3.641-5.229-2.647-3.186-7.72-5.752-9.926-6.637l-20.515-34.51 3.046-9.774a2 2 0 0 0-.184-1.607L91.163 53.643a1 1 0 0 0-1.36-.361L73.447 62.67a3 3 0 0 1-2.916.04l-2.978-1.605a9 9 0 0 1-3.31-3.068L49.61 35.206c-.411-.642-1.051-1.126-1.812-1.188-.524-.042-1.107-.023-1.475.188-.27.154-.507.427-.703.722-.496.747-.346 1.71.128 2.471l14.433 23.159a9 9 0 0 1 1.362 4.76V67.3a3 3 0 0 1-1.458 2.574l-4.974 2.98a1 1 0 0 0-.351 1.36'/%3E%3C/svg%3E"); width: 10vmin; @@ -417,6 +447,7 @@ bottom: 65vmin; right: 5vmin; } + .touch-control.fireRight, .touch-control.fireLeft { width: 10vmin; @@ -425,29 +456,35 @@ left: 31vmin; display: none; } + [data-state-car="1"][data-state-car-gun="1"] .touch-control.fireRight, [data-state-car="1"][data-state-car-gun="1"] .touch-control.fireLeft { display: block; } + [data-state-menu="1"][data-state-car="1"][data-state-car-gun="1"] .touch-control.fireRight, [data-state-menu="1"][data-state-car="1"][data-state-car-gun="1"] .touch-control.fireLeft, [data-state-cutscene="1"][data-state-car="1"][data-state-car-gun="1"] .touch-control.fireRight, [data-state-cutscene="1"][data-state-car="1"][data-state-car-gun="1"] .touch-control.fireLeft { display: none; } + .touch-control.fireRight { left: 43vmin; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M113.275 2.388a90.7 90.7 0 0 1 30.718 14.787c-.832-.136-4.625-.767-5.061-.999-4.284-2.272-23.488-11.081-25.657-13.788M91.542 64.147c.873.824 3.194 5.202 4.528 6.765 5.86 6.868 13.741 14.979 21.192 19.969-9.033 9.033-43.143 42.088-45.875 44.262-7.878-4.436-22.384-19.249-26.423-27.517 6.858-6.858 36.106-34.665 46.578-43.48M132.299 37.525c2.302-.314 5.299-.835 7.14-.132 4.913 1.629 8.651 7.55 8.1 9.821-3.06 14.092-14.885 29.829-25.855 39.172-1.358-1.311-3.365-2.734-4.907-3.901-9.159-7.093-13.797-13.085-20.37-22.29 7.514-9.292 23.767-20.394 35.892-22.67M25.277 48.866c1.607 0 3.792 18.944 4.336 21.954a1242 1242 0 0 0 6.901 35.113c2.781 13.47 7.206 24.874 2.163 38.387-.556 1.49-3.318.808-5.196-2.646a34.2 34.2 0 0 1-4.271-11.732c-1.248-7.246-1.887-25.434-2.382-33.256-.706-11.162-3.83-46.71-1.551-47.82M54.492 41.21c.196-.76 1.32-.616 1.307.17-.164 10.494-4.642 38.773-5.652 43.641-.593 2.856-1.946 7.164-4.094 9.233-1.1 1.06-1.281-.113-1.281-.113-1.64-7.587.8-17.82 2.778-26.037zM95.508 154.367c-7.81.656-25.895 2.686-33.246 2.512a34.2 34.2 0 0 1-12.231-2.508c-3.692-1.352-4.77-3.984-3.378-4.752 12.63-6.967 24.558-4.259 38.29-3.48q17.861 1.102 35.745 1.688c3.058.097 22.118-.514 22.353 1.076-.766 2.417-36.387 4.53-47.533 5.464M113.266 129.685c-7.839 3.16-17.605 7.072-25.35 6.56 0 0-1.186-.009-.3-1.251 1.733-2.427 5.796-4.397 8.534-5.402 4.669-1.711 31.988-10.279 42.345-11.978.775-.128 1.083.963.358 1.269z'/%3E%3Cpath fill='%23fff' d='M90 5.243c46.735 0 84.757 38.022 84.757 84.757S136.735 174.757 90 174.757 5.243 136.735 5.243 90 43.265 5.243 90 5.243M90 0C40.294 0 0 40.294 0 90s40.294 90 90 90 90-40.294 90-90S139.706 0 90 0'/%3E%3C/svg%3E"); } + .touch-control.fireLeft { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' fill='none' viewBox='0 0 180 180'%3E%3Cpath fill='%23fff' d='M66.725 2.388a90.7 90.7 0 0 0-30.718 14.787c.832-.136 4.625-.767 5.061-.999 4.284-2.272 23.488-11.081 25.657-13.788M88.458 64.147c-.873.824-3.194 5.202-4.528 6.765-5.86 6.868-13.74 14.979-21.192 19.969 9.033 9.033 43.144 42.088 45.875 44.262 7.878-4.436 22.384-19.249 26.423-27.517-6.858-6.858-36.106-34.665-46.578-43.48M47.7 37.525c-2.3-.314-5.298-.835-7.14-.132-4.912 1.629-8.65 7.55-8.099 9.821 3.06 14.092 14.885 29.829 25.855 39.172 1.358-1.311 3.365-2.734 4.907-3.901 9.159-7.093 13.797-13.085 20.37-22.29-7.514-9.292-23.767-20.394-35.892-22.67M154.723 48.866c-1.607 0-3.792 18.944-4.335 21.954a1244 1244 0 0 1-6.902 35.113c-2.781 13.47-7.206 24.874-2.163 38.387.557 1.49 3.318.808 5.196-2.646a34.2 34.2 0 0 0 4.271-11.732c1.248-7.246 1.887-25.434 2.382-33.256.706-11.162 3.83-46.71 1.551-47.82M125.508 41.21c-.196-.76-1.32-.616-1.307.17.164 10.494 4.642 38.773 5.652 43.641.593 2.856 1.946 7.164 4.094 9.233 1.099 1.06 1.281-.113 1.281-.113 1.641-7.587-.8-17.82-2.778-26.037zM84.492 154.367c7.81.656 25.895 2.686 33.246 2.512a34.2 34.2 0 0 0 12.231-2.508c3.692-1.352 4.77-3.984 3.378-4.752-12.629-6.967-24.558-4.259-38.29-3.48a1240 1240 0 0 1-35.745 1.688c-3.057.097-22.117-.514-22.353 1.076.766 2.417 36.387 4.53 47.533 5.464M66.734 129.685c7.839 3.16 17.605 7.072 25.35 6.56 0 0 1.186-.009.3-1.251-1.733-2.427-5.796-4.397-8.534-5.402-4.669-1.711-31.988-10.279-42.345-11.978-.775-.128-1.083.963-.358 1.269z'/%3E%3Cpath fill='%23fff' d='M90 5.243C43.265 5.243 5.243 43.265 5.243 90S43.265 174.757 90 174.757 174.757 136.735 174.757 90 136.735 5.243 90 5.243M90 0c49.706 0 90 40.294 90 90s-40.294 90-90 90S0 139.706 0 90 40.294 0 90 0'/%3E%3C/svg%3E"); } + .touch-control.radio { left: 50%; transform: translateX(-50%); width: 40vmin; height: 10vmin; } + .touch-control.weapon { right: 6vmin; top: 5vmin; @@ -463,6 +500,7 @@ height: 100%; z-index: 90; } + #look { position: fixed; top: 0; @@ -485,10 +523,12 @@ display: flex; flex-direction: column-reverse; } + .cover { margin-left: auto; margin-right: auto; } + .click-to-play { margin-top: 2em; margin-bottom: 0; @@ -499,14 +539,17 @@ color: white; font-weight: bold; } + .follow-container { margin-top: -30px; margin-bottom: 20px; } + .disabled { opacity: 0.4; filter: grayscale(100%); } + #demo-off-disclaimer { font-size: 0.7em; margin-top: 2px; @@ -514,23 +557,10 @@ - + @@ -552,14 +582,15 @@
- +
- +
Cloud saves: @@ -588,10 +619,21 @@
- HTML5 port by: - @specialist003, - @caiiiycuk, - @SerGen +
+ HTML5 port by: + @specialist003, + @caiiiycuk, + @SerGen +
+
+ Deobfuscated by: @Lolendor +
+
+ Russian translation: GamesVoice +
+
+ Offline Support: Th3w33knd +
@@ -634,17 +676,17 @@ - + \ No newline at end of file diff --git a/server.py b/server.py index d5f82e88..5eccad2d 100644 --- a/server.py +++ b/server.py @@ -108,4 +108,4 @@ def open_browser(): webbrowser.open(url) threading.Timer(1.5, open_browser).start() - uvicorn.run(app, host="localhost", port=args.port) + uvicorn.run(app, host="0.0.0.0", port=args.port) From 53ea15b025aeaca2e576972a33a1b5140b4f83b4 Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 08:50:09 +0530 Subject: [PATCH 12/29] docs: remove redundant updates section from README --- README.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/README.md b/README.md index 2ab738d4..b3eb9032 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,7 @@ Web-based port of GTA: Vice City running in browser via WebAssembly. -## Recent Updates (Dec 2025) -* **New Cheat Command**: Added `pixi run cheat` to automatically launch the game with cheats enabled and open the browser. -* **Asset Management**: Updated server logic to correctly handle `vcsky/fetched` assets without duplication. -* **Documentation**: Added comprehensive "Quick Start", "Download Assets" (with links), and "Project Structure" sections. -* **Fixes**: Solved directory nesting issues and added `vcbr` to `.gitignore` to prevent large file commit errors. -* **Upstream Sync**: Merged latest changes from `Lolendor/reVCDOS` while preserving custom features (cheats, offline mode). -* **Smart Caching**: Restored "Smart Cache" behavior (check local, then download) as the default. You can check which files I made changes in commit section. @@ -354,4 +347,4 @@ If you find this project useful: ### v1.0.0 - Initial Enhancements * **Offline Mode**: Game assets are cached locally for offline play. * **Cheats**: Added cheat menu (F3) and support for classic GTA VC cheats. -* **Smart Caching**: Optimized asset loading strategy. +* **Smart Caching**: Optimized asset loading strategy. From 63d6a2d5e92c209eb6a757a5c88681105550c3e7 Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 08:51:38 +0530 Subject: [PATCH 13/29] readme got destroyed --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3eb9032..6e378da1 100644 --- a/README.md +++ b/README.md @@ -339,7 +339,7 @@ If you find this project useful: ## Changelog ### v1.1.0 - Tailscale & Localization Update -* **Remote Access**: Server now binds to .0.0.0 to allow connections from LAN and Tailscale. +* **Remote Access**: Server now binds to 0.0.0.0 to allow connections from LAN and Tailscale. * **Documentation**: Added guide for connecting via Tailscale/LAN and handling Secure Contexts. * **Localization**: Fixed Russian text in the main menu (Subscribe for news and releases). * **Credits**: Added comprehensive credits for Deobfuscation, Translation, and Offline Support. From 4ba3536b02a38e037a5e739d7631d112a1da041e Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Thu, 25 Dec 2025 08:52:22 +0530 Subject: [PATCH 14/29] readme got fixed --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6e378da1..aa87ac23 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GTA Vice City — HTML5 Port (DOS Zone) +# GTA Vice City - HTML5 Port (DOS Zone) Web-based port of GTA: Vice City running in browser via WebAssembly. @@ -225,8 +225,8 @@ python server.py --vcsky_cache --vcbr_cache | `cheats` | `1` | Enable cheat menu (F3) | **Examples:** -- `http://localhost:8000/?lang=ru` — Russian version -- `http://localhost:8000/?lang=en&cheats=1` — English + cheats +- `http://localhost:8000/?lang=ru` - Russian version +- `http://localhost:8000/?lang=en&cheats=1` - English + cheats ## Project Structure @@ -275,7 +275,7 @@ python server.py --vcsky_cache --vcbr_cache When local saves are enabled (`--custom_saves` flag), enter any 5-character identifier in the "js-dos key" input field on the start page. This identifier will be used to store your saves in the `saves/` directory on the server. -Example: Enter `mykey` or `12345` — saves will be stored as `mykey_vcsky.saves` or `12345_vcsky.saves`. +Example: Enter `mykey` or `12345` - saves will be stored as `mykey_vcsky.saves` or `12345_vcsky.saves`. ## Controls (Touch) From 025b7eb8ea70f07826d9bce6f1c87d9b290fbf7d Mon Sep 17 00:00:00 2001 From: Alexey Olendor <137941487+Lolendor@users.noreply.github.com> Date: Thu, 25 Dec 2025 06:37:01 +0300 Subject: [PATCH 15/29] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3dbd14ee..2291431e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,4 @@ __pycache__/ # Game Assets vcbr/ -vcsky/fetched +vcsky/ From bafbc4a01b7c935c22a48b8a70b7b6157282f229 Mon Sep 17 00:00:00 2001 From: Alexey Olendor <137941487+Lolendor@users.noreply.github.com> Date: Thu, 25 Dec 2025 06:39:05 +0300 Subject: [PATCH 16/29] Back game.js --- dist/game.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/dist/game.js b/dist/game.js index f925c01e..7fab3b7f 100644 --- a/dist/game.js +++ b/dist/game.js @@ -48,7 +48,7 @@ let haveOriginalGame = false; downloading: "Downloading", enterKey: "enter your key", clickToContinue: "Click to continue...", - enterJsDosKey: "Enter js-dos key", + enterJsDosKey: "Enter js-dos key (5 len)", portBy: "HTML5 port by:", ruTranslate: "", demoOffDisclaimer: "Due to the unexpectedly high popularity of the project, resulting in significant traffic costs, and in order to avoid any risk of the project being shut down due to rights holder claims, we have disabled the demo version. You can still run the full version by providing the original game resources.", @@ -71,7 +71,7 @@ let haveOriginalGame = false; downloading: "Загрузка", enterKey: "введите ваш ключ", clickToContinue: "Нажмите для продолжения...", - enterJsDosKey: "Введите ключ js-dos", + enterJsDosKey: "Введите ключ js-dos (5 букв)", portBy: "Авторы HTML5 порта:", ruTranslate: `
@@ -86,9 +86,9 @@ let haveOriginalGame = false; let currentLanguage = navigator.language.split("-")[0] === "ru" ? "ru" : "en"; if (params.get("lang") === "ru") { currentLanguage = "ru"; - // } - // if (params.get("lang") === "en") { - } else { + // } + // if (params.get("lang") === "en") { + }else{ currentLanguage = "en"; } @@ -98,15 +98,15 @@ let haveOriginalGame = false; })(); if (params.get('lang') === 'ru') { - data_content = `${replaceBR}vc-sky-ru-v6.data`; - wasm_content = `${replaceBR}vc-sky-ru-v6.wasm`; - // } else if (params.get('lang') === 'ru') { - // data_content = `${replaceBR}vc-sky-ru-v6.data.br`; - // wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; + data_content = `${replaceBR}vc-sky-ru-v6.data.br`; + wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; +// } else if (params.get('lang') === 'ru') { +// data_content = `${replaceBR}vc-sky-ru-v6.data.br`; +// wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; } else { - data_content = `${replaceBR}vc-sky-en-v6.data`; - wasm_content = `${replaceBR}vc-sky-en-v6.wasm`; - // data_content = "index.old.data"; + data_content = `${replaceBR}vc-sky-en-v6.data.br`; + wasm_content = `${replaceBR}vc-sky-en-v6.wasm.br`; +// data_content = "index.old.data"; } // data_content = `${replaceBR}vc-sky-ru-v6.data.br`; // wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; @@ -213,7 +213,6 @@ function setStatus(text) { async function loadGame(data) { var Module = { - ALLOW_MEMORY_GROWTH: 1, mainCalled: () => { try { Module.FS.unlink("/vc-assets/local/revc.ini"); @@ -522,7 +521,7 @@ const clickToPlayButton = document.getElementById('click-to-play-button'); clickToPlayButton.textContent = t('clickToPlayDemo'); clickToPlayButton.classList.add('disabled'); const demoOffDisclaimer = document.getElementById('demo-off-disclaimer'); -demoOffDisclaimer.textContent = "* " + t('demoOffDisclaimer'); +demoOffDisclaimer.textContent = "* " +t('demoOffDisclaimer'); const cloudSavesLink = document.getElementById('cloud-saves-link'); cloudSavesLink.textContent = t('cloudSaves'); cloudSavesStatus.textContent = t('enterKey'); @@ -555,7 +554,7 @@ function ownerShipNotConfirmed() { localStorage.removeItem('vcsky.haveOriginalGame'); disclaimerCheckbox.checked = false; clickToPlayButton.textContent = t('clickToPlayDemo'); - demoOffDisclaimer.textContent = "* " + t('demoOffDisclaimer'); + demoOffDisclaimer.textContent = "* " +t('demoOffDisclaimer'); haveOriginalGame = false; clickToPlayButton.classList.add('disabled'); }; @@ -728,4 +727,4 @@ const revc_ini = (() => { return cached; } return revc_iniDefault; -})(); \ No newline at end of file +})(); From b7c092482e08ebca4314deef947bb17b61c30302 Mon Sep 17 00:00:00 2001 From: Th3w33knd <136694430+Th3w33knd@users.noreply.github.com> Date: Thu, 25 Dec 2025 18:31:49 +0530 Subject: [PATCH 17/29] Added Installation Tutorial Video in README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa87ac23..13d005b3 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,12 @@ This project is a community effort to preserve the incredible HTML5 port of GTA: > [!NOTE] > We still do not have the source code for the files in `vcbr` (the compiled WebAssembly modules), which contain the core logic of the game. This project wraps and serves those existing binaries. -**Stability Test**: Watch the server in action here: [GTA VC Server Stability Test](https://www.youtube.com/watch?v=C8nK81N4iBs) +**Stability Test**: Watch the server in action here: [GTA reVCDOS Server Stability Test](https://www.youtube.com/watch?v=C8nK81N4iBs) ## Quick Start +**Installation Tutorial**: Video only covers installation in Windows 11 + Python : [Install reVCDOS GTA:Vice City](https://youtu.be/AEvh2ok-nvs?si=ibyGHrfqiBgYT39c) + 1. **Clone the repository**: ```bash git clone --depth 1 https://github.com/Th3w33knd/reVCDOS From 6f71578df7831581ed494b25438b9a9629cf2b41 Mon Sep 17 00:00:00 2001 From: Th3w33knd <136694430+Th3w33knd@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:05:35 +0530 Subject: [PATCH 18/29] Update README.md --- README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 13d005b3..aa8a4200 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,16 @@ -# GTA Vice City - HTML5 Port (DOS Zone) - -Web-based port of GTA: Vice City running in browser via WebAssembly. - +reVCDOS - HTML5 Port +Running in browser via WebAssembly. You can check which files I made changes in commit section. ## History & Restoration -This project is a community effort to preserve the incredible HTML5 port of GTA: Vice City. +This project is a community effort to preserve the incredible HTML5 port. -* **The Ban**: Rockstar Games issued a takedown for the original web port, shutting down the official servers and CDNs (`cdn.dos.zone`). +* **The Ban**: Takedown for the original web port, shutting down the official servers and CDNs. * **Deobfuscation**: The source code was deobfuscated to allow for self-hosting and study. -* **WebAssembly Magic**: Unlike standard PC mods (e.g., *Vice City: Reviced*), this port runs entirely in the browser using WebAssembly. It streams assets on-the-fly, making it playable on almost any device with a browser and keyboard, without installation. +* **WebAssembly Magic**: Unlike standard PC mods, this port runs entirely in the browser using WebAssembly. It streams assets on-the-fly, making it playable on almost any device with a browser and keyboard, without installation. * **Restoring Functionality**: After the shutdown, the game would hang on a black screen because it couldn't fetch core data files (`.wasm`, `.data`) or assets from the dead CDNs. This repository solves that by: 1. Providing a server that serves these critical files locally. 2. Implementing a caching system to download and save surviving assets. @@ -21,11 +19,11 @@ This project is a community effort to preserve the incredible HTML5 port of GTA: > [!NOTE] > We still do not have the source code for the files in `vcbr` (the compiled WebAssembly modules), which contain the core logic of the game. This project wraps and serves those existing binaries. -**Stability Test**: Watch the server in action here: [GTA reVCDOS Server Stability Test](https://www.youtube.com/watch?v=C8nK81N4iBs) +**Stability Test**: Watch the server in action here: [reVCDOS Server Stability Test](https://www.youtube.com/watch?v=C8nK81N4iBs) ## Quick Start -**Installation Tutorial**: Video only covers installation in Windows 11 + Python : [Install reVCDOS GTA:Vice City](https://youtu.be/AEvh2ok-nvs?si=ibyGHrfqiBgYT39c) +**Installation Tutorial**: Video only covers installation in Windows 11 + Python : [Install reVCDOS](https://youtu.be/AEvh2ok-nvs?si=ibyGHrfqiBgYT39c) 1. **Clone the repository**: ```bash @@ -287,7 +285,7 @@ Touch controls appear automatically on mobile devices. Virtual joysticks for mov Enable with `?cheats=1`, press **F3** to open menu: - Memory scanner (find/edit values) -- All classic GTA VC cheats +- All classic cheats - AirBreak (noclip mode) ## Remote Access (Tailscale / LAN) @@ -348,5 +346,5 @@ If you find this project useful: ### v1.0.0 - Initial Enhancements * **Offline Mode**: Game assets are cached locally for offline play. -* **Cheats**: Added cheat menu (F3) and support for classic GTA VC cheats. +* **Cheats**: Added cheat menu (F3) and support for classic cheats. * **Smart Caching**: Optimized asset loading strategy. From df8f1444cbfcd0da659fb33dc65788087069de6c Mon Sep 17 00:00:00 2001 From: Th3w33knd <136694430+Th3w33knd@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:06:33 +0530 Subject: [PATCH 19/29] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa8a4200..654a8b26 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ reVCDOS - HTML5 Port Running in browser via WebAssembly. -You can check which files I made changes in commit section. +You can check which files I made changes in commit section. Only for *educational* purpose only ## History & Restoration From f7994fffec8f0c08b1a98270e323041de8bd7e92 Mon Sep 17 00:00:00 2001 From: Th3w33knd <136694430+Th3w33knd@users.noreply.github.com> Date: Thu, 25 Dec 2025 19:08:37 +0530 Subject: [PATCH 20/29] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 654a8b26..40571bf5 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ This project is a community effort to preserve the incredible HTML5 port. 2. **Download Assets**: > [!WARNING] - > These files contain copyrighted materials from Rockstar Games. You must own the original game to use them legally. + > These files contain copyrighted materials. You must own the original game to use them legally. * **vcbr** (Core Data): [Download](https://gofile.io/d/ceuXTa) (or [Older Version](https://gofile.io/d/U63PZO)) * **vcsky** (Assets): [Download](https://gofile.io/d/9QsvMn) From ba61eaff1d065909e3ab23e4067f2e78717adc75 Mon Sep 17 00:00:00 2001 From: Avitesh Murmu Date: Sat, 27 Dec 2025 10:45:44 +0530 Subject: [PATCH 21/29] Save local changes before merge --- .htaccess | 77 ++++++++++++++++++++++++++----------------------- dist/game.js | 20 ++++++------- dist/index.html | 4 +-- index.php | 2 +- 4 files changed, 54 insertions(+), 49 deletions(-) diff --git a/.htaccess b/.htaccess index 4f93bf3b..5c2e5918 100644 --- a/.htaccess +++ b/.htaccess @@ -1,47 +1,52 @@ -# JS files -AddType application/javascript .js - -# WebAssembly files -AddType application/wasm .wasm - -# CSS files -AddType text/css .css - -# Brotli-compressed files -AddEncoding br .br - - - -# Enable rewrite engine RewriteEngine On +RewriteBase /reVCDOS/ + +# 1. Security Headers (Required) + + Header set Cross-Origin-Opener-Policy "same-origin" + Header set Cross-Origin-Embedder-Policy "require-corp" + + +# 2. DISABLE Apache Compression for .br files (The Fix) + + # Tell Apache NOT to gzip these files + SetEnvIfNoCase Request_URI "\.br$" no-gzip dont-vary + + +# 3. Explicitly Handle .br Files + + # Disable any default filters that might mess with the binary stream + RemoveOutputFilter .br + RemoveType .br + + # Tell the browser: "This file is encoded with Brotli" + + Header set Content-Encoding "br" + # Prevent caching issues during testing + Header set Cache-Control "no-cache, no-store, must-revalidate" + + -# Set base path if PHP file is not in root -RewriteBase / - -# If the request is for an existing file or directory, serve it directly -RewriteCond %{REQUEST_FILENAME} -f [OR] -RewriteCond %{REQUEST_FILENAME} -d -RewriteRule ^ - [L] - -# Otherwise, route everything to index.php -RewriteRule ^.*$ index.php [L,QSA] - - -# Correct MIME types +# 4. Correct MIME Types AddType application/javascript .js AddType application/wasm .wasm AddType text/css .css -# Brotli compressed files -AddEncoding br .br +# Force MIME types for the specific compressed files + + ForceType application/octet-stream + + + + ForceType application/wasm + -# Optional: force correct MIME for .js.br or .wasm.br ForceType application/javascript - AddEncoding br - - ForceType application/wasm - AddEncoding br - \ No newline at end of file +# 5. Routing +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [L] +RewriteRule ^.*$ index.php [L,QSA] \ No newline at end of file diff --git a/dist/game.js b/dist/game.js index 7fab3b7f..6eba1cfc 100644 --- a/dist/game.js +++ b/dist/game.js @@ -8,8 +8,8 @@ var wasm_content; const params = new URLSearchParams(window.location.search); // Base URLs -const replaceFetch = (str) => str.replace("https://cdn.dos.zone/vcsky/", "/vcsky/") -const replaceBR = "/vcbr/" +const replaceFetch = (str) => str.replace("https://cdn.dos.zone/vcsky/", "vcsky/") +const replaceBR = "vcbr/" const autoFullScreen = true; @@ -98,18 +98,18 @@ let haveOriginalGame = false; })(); if (params.get('lang') === 'ru') { - data_content = `${replaceBR}vc-sky-ru-v6.data.br`; - wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; + data_content = `${replaceBR}vc-sky-ru-v6.data`; + wasm_content = `${replaceBR}vc-sky-ru-v6.wasm`; // } else if (params.get('lang') === 'ru') { -// data_content = `${replaceBR}vc-sky-ru-v6.data.br`; -// wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; +// data_content = `${replaceBR}vc-sky-ru-v6.data`; +// wasm_content = `${replaceBR}vc-sky-ru-v6.wasm`; } else { - data_content = `${replaceBR}vc-sky-en-v6.data.br`; - wasm_content = `${replaceBR}vc-sky-en-v6.wasm.br`; + data_content = `${replaceBR}vc-sky-en-v6.data`; + wasm_content = `${replaceBR}vc-sky-en-v6.wasm`; // data_content = "index.old.data"; } -// data_content = `${replaceBR}vc-sky-ru-v6.data.br`; -// wasm_content = `${replaceBR}vc-sky-ru-v6.wasm.br`; +// data_content = `${replaceBR}vc-sky-ru-v6.data`; +// wasm_content = `${replaceBR}vc-sky-ru-v6.wasm`; async function loadData() { let cache; diff --git a/dist/index.html b/dist/index.html index 991153c6..96a0672d 100644 --- a/dist/index.html +++ b/dist/index.html @@ -189,7 +189,7 @@ height: 325px; max-height: 50vh; display: inline-block; - background-image: url(/cover.jpg); + background-image: url(cover.jpg); background-repeat: no-repeat; background-position: 50% 50%; box-shadow: inset 0 0 .5em #249ef5aa, 0 0 .75em #249ef5aa; @@ -565,7 +565,7 @@