Lenga's current export flow is straightforward:
- it stages a current-platform player build
- on macOS, it creates a one-click
.appbundle - it runs that packaged player in player mode
Use this workflow when you want a packaged player build for the same platform you are developing on.
Before You Export
Make sure your project has:
- a valid
ProjectSettings/lenga.json - a startup scene configured
- a
bootstrap.php - a
composer.json - a populated
vendor/folder
Lenga exports the standard project layout, so if those pieces are missing the export will fail.
Step 1: Open Build Settings
In the editor, open:
Project -> Build Settings...
You can configure:
Product NameVersionIdentifierBuild ProfileLaunch ModeWindow WidthWindow HeightTarget FPSFrame Rate CapIcon PathDefault OutputMinimum macOSStartup SceneRestricted PHP APIs
Product Name
This is the shipping-facing name Lenga uses for the exported build.
It is saved to the root name field in ProjectSettings/lenga.json, and it is also used for things like the exported executable name and export folder naming.
Version
This is the shipping version string stored in ProjectSettings/lenga.json.
Lenga also carries it into the exported build manifest so publish tooling and release checks can inspect it without guessing.
Identifier
This is the application identifier or bundle-style id for the project, such as:
com.lenga.rollerworld
It is saved to the root id field in ProjectSettings/lenga.json and carried into the export manifest.
Build Profile
Lenga supports two export profiles:
DevelopmentRelease
Development is the default. It keeps runtime debug-mode support enabled in the packaged project, which is useful when you want to hand a build to a teammate or test on another machine without losing debug visibility.
Release is opt-in. It disables runtime debugMode in the packaged ProjectSettings/lenga.json and turns off the runtime diagnostics overlay for the exported build.
Launch Mode
This controls how the game window opens when the exported build starts.
Lenga supports:
WindowedBordered FullscreenBorderless Fullscreen
Use Windowed when you want a normal game window.
Use Bordered Fullscreen when you want the game to open as a maximized window with normal window chrome.
Use Borderless Fullscreen when you want the game to cover the monitor without normal window borders.
Target FPS
This controls the default frame-rate target saved to ProjectSettings/lenga.json.
The default is 60.
Raise it when your game is designed for a higher refresh target. Lower it when you want a more conservative default for menus, slower-paced games, or lower-power hardware.
Frame Rate Cap
This controls whether the packaged game uses the configured Target FPS value or leaves the frame rate uncapped.
When Leave uncapped is enabled, Lenga does not apply the project frame-rate cap at startup. The Target FPS value is still kept in project settings so you can turn the cap back on later without re-entering it.
Window Width and Window Height
These control the default window size saved to ProjectSettings/lenga.json.
Lenga uses these values for normal windowed launches, and it also uses them as the starting size before fullscreen-style launch modes adjust the window.
Icon Path
This is the image the exported build should use when supported by the current platform and build layout.
On macOS, app bundles use .icns icons. If you configure a different image format, the build can still export, but the app bundle icon will not use that image.
Default Output
This is the default folder Lenga opens to when you start an export.
If you leave it at the default value, Lenga exports into:
build/dist
Relative paths are resolved from the project root.
When you actually export, Lenga opens a folder picker so you can choose a different destination for that export without rewriting the saved default first.
Minimum macOS
On macOS, this controls the minimum system version written into the exported app bundle.
This setting does not magically make the current engine build compatible with older macOS releases. Export validation still checks the runtime executable and bundled native dylibs, and export fails if any bundled Mach-O artifact requires a newer macOS version than the one you configured.
Startup Scene
This is the scene the packaged player will boot into by default.
If you leave it on First Enabled Scene, Lenga will use the first enabled scene from your build scene list.
Restricted PHP APIs
This build setting controls what happens when Lenga finds project PHP code that references APIs or wrappers player mode blocks.
Default behavior:
- export stops
- the Console lists the first reported violations
Override behavior:
- turn on
Allow packaging anyway - export continues, but the build is knowingly non-compliant with the default player-mode sandbox
Step 2: Export the Build
You can export in either of two ways:
Project -> Export Current Platform...
or from the Build Settings modal:
Export Current Platform
Before export, Lenga will save the active scene if it is dirty.
Lenga then opens a destination picker so you can choose where the export should be staged for that run.
If the chosen export name already exists, Lenga creates the next numbered sibling instead of trying to replace the older export in place.
Lenga scans your project PHP files for common APIs and wrappers that player
mode blocks, such as shell execution, process spawning, FFI, direct socket
or cURL calls, and remote wrapper usage like http:// or phar://.
By default, export stops when those references are found. That keeps the shipping path aligned with the actual player-mode sandbox instead of letting a build stage successfully and fail later at runtime.
Those export messages now call out the kind of restriction involved, such as:
- process execution or unsafe host access
- outbound networking
- native code loading
- remote or special stream wrappers
Step 3: Understand the Output Layout
On macOS, the exported folder looks like this:
build/dist/my-game-macos-dev/
MyGame.app/
Contents/
Frameworks/
libraylib.550.dylib
libbox2d.3.dylib
libssl.3.dylib
libcrypto.3.dylib
libiconv.2.dylib
libxml2.16.dylib
Info.plist
MacOS/
MyGame
Resources/
Game/
Assets/
ProjectSettings/
bootstrap.php
composer.json
vendor/
Saved/
build-manifest.json
Notes:
- the
.appbundle is the launch artifact you double-click - the native runtime executable is renamed to match your project and placed inside
Contents/MacOS/ - non-system native dependencies are bundled into
Contents/Frameworks/ - export stages into a clean temporary folder first, then replaces the chosen destination only after validation succeeds
- the packaged project lives inside
Contents/Resources/Game/ - Lenga writes a
build-manifest.jsonfile with metadata like name, version, identifier, profile, platform, launch artifact, project root, executable name, startup scene, sandbox profile, writable root, and the explicit shippedpermissionscontract - the export folder name includes the selected build profile, such as
-releaseor-dev
The packaged runtime can detect the bundled Game/ folder automatically, so it does not need --project passed manually in normal exported use.
Step 4: Know What Player Mode Does
Exported builds automatically run in player mode.
That means the packaged runtime uses a stricter PHP sandbox than your normal editor/development workflow.
Player-mode protections include:
- disabled process and shell execution
- disabled dynamic extension loading
- disabled
FFI - disabled URL wrappers
- tighter filesystem confinement
- an engine-written packaged player marker so exported builds opt into the stricter packaged policy intentionally
- a packaged
include_pathnarrowed to the exported game root and exportedvendor/ - PHP temp files and PHP error logs redirected into the packaged
Game/Saved/ - bundled default gamepad mappings when an engine or project mapping database is available
The permissions contract recorded in build-manifest.json is:
- packaged game content is read-only
- packaged game reads are allowed from the exported
Game/root - writes are limited to
Game/Saved/ Preferenceswrites stay engine-managed and do not widen general PHP filesystem access- outbound networking is denied
- process and shell execution are denied
- dynamic extension loading is denied
- remote and special stream wrappers are denied
- stream-wrapper registration is denied
If you want the full security model explained in one place, read:
The export workflow blocks by default when it sees common blocked APIs or
wrappers in your project code, so you get earlier feedback than a late runtime
failure. If you intentionally need to stage a non-compliant build, Build
Settings exposes an explicit Allow packaging anyway override.
If a packaged build still reaches a blocked path at runtime, the failure surfaces through normal PHP/runtime error output and the packaged PHP error log under:
MyGame.app/Contents/Resources/Game/Saved/Logs/php-runtime.log
Before export completes, Lenga also validates the staged macOS app bundle for missing packaged files, packaged engine-asset resolution, remaining symlinks, the written sandbox manifest contract, and unresolved external native dylib paths.
In the exported layout, Lenga also marks packaged game content read-only and leaves only:
MyGame.app/Contents/Resources/Game/Saved/
writable for save data, logs, and player-mode temporary files.
Small local settings that use Preferences are written through Lenga's
engine-managed per-user preferences location instead of writing directly beside
the exported app.
Step 5: Test the Exported Build
After export, run the packaged build directly from the exported folder.
On macOS, double-click:
MyGame.app
Then check:
- the game boots into the expected startup scene
- save files and logs are written under
Game/Saved/ Preferencessettings are written through the engine-managed per-user preferences location- your project does not rely on blocked PHP features like shell execution
- gamepads still behave correctly on the packaged build, especially on macOS if you use a controller database file
Limitations
This workflow does not cover:
- cross-platform export from one machine
- signing and notarization workflows
- installers or archive generation
- a richer permissions manifest
- DMG generation
This export workflow gives you:
- a real packaged player build
- for the current development platform
- with a practical default-deny PHP sandbox
- with a one-click macOS
.applaunch artifact
Use this workflow to validate packaged builds during development and internal testing.
Optional: Ship a Gamepad Mapping Database
Lenga already ships with a bundled SDL controller mapping database and exported builds carry that default forward automatically.
If your project needs to override that default, Lenga can also load a project-specific database:
- a project-specific path in
ProjectSettings/lenga.json - the engine-default bundled path at
res/input/gamecontrollerdb.txt
Example project setting:
{
"input": {
"gamepadMappingsPath": "ProjectSettings/gamecontrollerdb.txt"
}
}
This is especially useful on macOS, where controller mappings can vary more from one device to another. In most cases, though, you should be able to rely on the engine-shipped default database without any extra setup.