# Shipping a Game A SimVX game reaches players by three routes. None of them is a fallback for the others: pick the one that matches your audience. | Route | What the player gets | Install burden | |---|---|---| | **Source and wheel** | `pip install your-game`, then `simvx run game.py` | Needs Python | | **Standalone executable** | A folder (or one file) they double-click | None | | **Web export** | A URL | None; also the mobile answer | Web export is covered in its own page, [Web Export](web/export.md). This page covers the other two. ## Source and wheel The natural path for a Python engine. Build a wheel for your game, publish it, and a player with Python installs it like any other package: ```bash uv build --wheel pip install dist/your_game-1.0.0-py3-none-any.whl your-game ``` This is the path with the least that can go wrong, because nothing repackages your dependencies: numpy, the Vulkan loader and the engine's native extensions are installed by pip exactly as their authors built them. ## Standalone executable For players who will not install Python. The editor's **Export** dialog offers a *Standalone Executable* target, and `simvx` uses [PyInstaller](https://pyinstaller.org/) underneath. The engine has four native pieces that a bundler has to carry: numpy, the Vulkan loader, the CFFI audio extension, and (if you use it) the Jolt physics `.so`. It also loads shaders, fonts and default assets through `importlib.resources`, which a bundler cannot see by reading `import` statements. That second point is the one that bites: a bundler invoked with no options produces a binary that dies on its first frame with `ModuleNotFoundError: No module named 'simvx.graphics.shaders'`. So the collection flags are not optional: ```bash pyinstaller --collect-all simvx --collect-all vulkan --collect-all glfw --collect-all freetype \ --hidden-import miniaudio --hidden-import _cffi_backend \ game.py ``` Expect roughly **155 MB** for a one-directory build of a small game, most of it numpy, Pillow and the engine's own assets. It starts in about the same time as running the game from a virtualenv: the engine's Vulkan setup dominates, and the bundling costs nothing measurable. `--onefile` compresses those 155 MB to about **62 MB**, at the cost of unpacking into a temporary directory on every launch: measured on Linux, that adds roughly 0.6 s to each start. Ship one-directory unless the single file genuinely matters to you. One-directory writes a *folder* named after your game, with the executable inside it next to an `_internal/` directory. `dist/mygame/mygame` is the thing you run; `dist/mygame/` is the thing you ship, and it must be shipped whole. A bundle is host-only. PyInstaller does not cross-compile, so a Windows `.exe` is built on Windows and a macOS app on macOS. ### A bundle does not keep your code secret Godot ships a `.pck` with optional encryption and Unity ships compiled assemblies; SimVX does neither, and will not. Two different things happen to the two halves of a bundle, and it is worth being precise about both: - **The engine ships as plain readable source.** A one-directory bundle contains around 440 `.py` files under `_internal/simvx/`, plus shaders and default assets as ordinary files. Anyone can open them in an editor. That is the collection step doing its job. - **Your own modules are byte-compiled into the executable**, in one-file and one-directory alike. You will not find `game.py` in the bundle, and searching it for a string from your source finds nothing. The second point is **not** protection, and treating it as protection would be a mistake. The bundled code objects come back out with a few lines of standard library: ```python from PyInstaller.archive.readers import CArchiveReader import marshal, dis code = marshal.loads(CArchiveReader("dist/mygame/mygame").extract("game")) # "game" = your entry module dis.dis(code) # docstrings, every string constant, the whole disassembly ``` That recovers your strings, your API keys if you put any there, and the full logic in disassembled form. A decompiler is only needed to turn it back into readable Python. Byte-compiling raises the effort of reading your code from "open the file" to "spend an afternoon"; it does not raise it to "cannot". That is a position, not an omission. An engine that promotes open source and treats source distribution as a first-class shipping path is not in the business of hiding the game's source from the person running it. Choose knowingly: - If your game's value is in its content, its feel, or its multiplayer service, this costs you nothing. - If your business model depends on the client being opaque, use a different engine. Do not ship a SimVX bundle and assume the code inside it is private. Packing assets into a single archive may still arrive, for load time and tidiness. Obfuscation is not a goal and is not on the roadmap. ## Mobile Play the [web export](web/export.md) in a mobile browser. That is the supported mobile path today, and it works: touch input is captured and the WebGPU renderer runs on current mobile browsers. A native Android target is best-effort future work, not a scheduled feature. If you need a store listing rather than a URL, that gap is real and current.