Map Package Format
Map Package Format
This is the canonical v1 contract for MTADM map packages. The MTADM map editor, backend, game server, and game client should treat this page as the source of truth for .mtamap and .mtabin.
MTADM is a deterministic racing game. The game server, game client, website backend, and editor must agree on the exact map runtime data before a race starts. A leaderboard time or replay is valid only for the exact immutable map release it was driven on.
Core Model
MTADM uses two package formats.
.mtamap = editable source package
.mtabin = generated runtime packageThe editor exports .mtamap. The website/backend validates .mtamap and generates .mtabin. Public servers and clients use .mtabin.
For v1, both formats can be zip/container-based. The .mtabin extension means "runtime package", not necessarily a fully binary file yet.
Reusable asset packages are defined separately as .mtaasset. See Assets.
Package Flow
MTADM Map Editor
-> exports done.mtamap
-> backend validates source package
-> backend resolves, registers, and deduplicates asset versions
-> backend generates done-v3.0.1.mtabin
-> server/client use .mtabin for playOffline/local play should use the same mental model.
Local .mtamap
-> local validation/compile
-> local .mtabin
-> local server starts from .mtabin
-> client connects to local serverPublic Runtime And Editable Source
Every playable public map version needs a runtime package.
The editable source package is optional for public sharing.
- Public runtime:
.mtabinis downloadable for play. - Public editable source:
.mtamapis also downloadable for remixing/editing. - Private editable source:
.mtamapis kept private, but the map can still be playable through.mtabin.
This means a map can be "binary-only" from the player perspective: players can download/play the generated .mtabin, but cannot download the editable .mtamap.
Stable Identity
Do not use slugs, names, or filenames as system identity.
Use separate fields:
| Field | Purpose |
|---|---|
mapId |
Stable internal ID for the logical map. |
mapReleaseId |
Stable internal ID for one immutable playable version. |
slug |
Human-readable URL identifier. Can change. |
title |
Human-readable display title. Can change. |
version |
Human-readable release label, for example v3.0.1. |
sourcePackageHash |
Hash of uploaded .mtamap. |
runtimePackageHash |
Hash of generated .mtabin. |
deterministicHash |
Hash over gameplay-affecting runtime data. |
Example:
{
"mapId": "map_01JABC",
"mapReleaseId": "mapver_01JXYZ",
"slug": "done",
"title": "Done",
"version": "v3.0.1",
"deterministicHash": "sha256:..."
}Release Rule
Published map releases are immutable.
If gameplay, collision, checkpoints, server-side scripting, or race rules change, create a new map release. Never mutate an already published runtime package.
Example:
Done v3.0.0 = original release
Done v3.0.1 = shortcut fix
Done v3.1.0 = larger update
Done v4.0.0 = major reworkEach release has its own leaderboard scope.
.mtamap
.mtamap is the editable source package exported by the MTADM map editor.
It is used for:
- editing
- website upload
- source sharing when enabled
- future remixing
- backend validation
- asset extraction
The backend must treat .mtamap as untrusted user input.
Minimal .mtamap
The smallest valid source package contains:
done.mtamap
manifest.json
source/
map.jsonRecommended .mtamap
A complete editor export should look like this:
done.mtamap
manifest.json
source/
map.json
visuals.json
assets.json
scripts/
server/
main.lua
client/
visual.lua
assets/
objects/
road_10m/
object.json
collision.glb
mesh.glb
material.json
textures/
asphalt_diffuse.webp
materials/
asphalt.material.json
sounds/
checkpoint.ogg
editor/
editor_state.json
thumbnail.webpArchive Rules
The archive must be safe to unpack.
Rules:
- Paths must use
/. - Absolute paths are invalid.
../path traversal is invalid.- NUL bytes in paths are invalid.
- Empty paths are invalid.
- Directory entries are allowed but ignored.
- Duplicate normalized paths are invalid.
- File count must stay below the backend limit.
- Each file must stay below the backend per-file limit.
- Total package size must stay below the upload limit.
Current upload limit:
100 MiB per .mtamapAccepted File Areas
Top-level files and directories:
| Path | Required | Purpose |
|---|---|---|
manifest.json |
yes | Package metadata. |
source/map.json |
yes | Gameplay-affecting map source. |
source/visuals.json |
no | Visual-only presentation source. |
source/assets.json |
no, recommended | Stable asset manifest. |
scripts/server/*.lua |
no | Future server-side deterministic scripts. |
scripts/client/*.lua |
no | Future client-side visual scripts. |
assets/** |
no | Reusable map assets. |
editor/** |
no | Editor-only state and thumbnails. |
Unsupported top-level paths should be rejected.
Accepted Extensions
Current accepted asset/editor extensions:
.json
.png
.webp
.jpg
.jpeg
.ogg
.wav
.glb
.gltf
.obj
.mtl
.material
.mesh
.luaThis list should stay restrictive. Add new formats deliberately when the game/editor supports validation for them.
manifest.json
manifest.json describes the source package.
{
"format": "mtadm.map.source",
"formatVersion": 1,
"title": "Done",
"slug": "done",
"description": "A deterministic racing map.",
"createdWithGameVersion": "0.3.0",
"minGameVersion": "0.3.0",
"physicsVersion": "phys_1",
"vehicleVersion": "vehicle_phys_1",
"scriptApiVersion": "lua_map_api_1",
"determinismVersion": "det_1"
}Field rules:
| Field | Required | Rule |
|---|---|---|
format |
yes | Must be mtadm.map.source. |
formatVersion |
yes | Positive integer. Starts at 1. |
title |
yes | Display name. Not a stable ID. |
slug |
yes | URL-friendly suggestion. Not a stable ID. |
description |
no | User-facing map description. |
createdWithGameVersion |
recommended | Editor/game version that exported the package. |
minGameVersion |
recommended | Minimum game version that can load the runtime package. |
physicsVersion |
recommended | Physics rules version used for deterministic validation. |
vehicleVersion |
recommended | Vehicle physics version. |
scriptApiVersion |
future | Lua scripting API version. |
determinismVersion |
recommended | Determinism format/rules version. |
The backend may generate internal IDs. The editor must not assume slug is permanent.
Coordinates And Units
Coordinate arrays use three numbers.
{
"position": [
0,
2,
80
],
"rotation": [
0,
90,
0
],
"scale": [
1,
1,
1
]
}Rules:
- Positions are MTADM world coordinates.
- Rotations are Euler degrees unless the editor/game contract changes.
- Scale defaults to
[1, 1, 1]. - IDs should be stable within one source package.
- IDs must be unique inside their own collection.
source/map.json
source/map.json contains gameplay-affecting source data.
This file is deterministic input.
{
"spawns": [
{
"id": "spawn_start_1",
"position": [
0,
2,
0
],
"rotation": [
0,
0,
0
]
}
],
"checkpoints": [
{
"id": "checkpoint_1",
"position": [
0,
2,
80
],
"size": [
12,
6,
3
],
"order": 1
}
],
"finishZones": [
{
"id": "finish_1",
"position": [
0,
2,
500
],
"size": [
14,
6,
3
]
}
],
"objects": [
{
"id": "road_a",
"asset": "road_10m",
"position": [
0,
0,
20
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
],
"collision": true
}
],
"triggers": [],
"zones": [],
"raceSettings": {
"laps": 1,
"startMode": "standing"
}
}Top-level fields:
| Field | Required | Purpose |
|---|---|---|
spawns |
yes | Player start positions. |
checkpoints |
yes | Ordered checkpoint requirements. |
finishZones |
yes | Finish trigger volumes. |
objects |
recommended | Placed objects/assets. |
triggers |
optional | Gameplay trigger volumes. |
zones |
optional | Boost, kill, reset, or special zones. |
raceSettings |
recommended | Race rules. |
Changing this file creates a new deterministic map release after publication.
Spawns
Each spawn defines a valid start position.
{
"id": "spawn_start_1",
"position": [
0,
2,
0
],
"rotation": [
0,
0,
0
]
}Rules:
- At least one spawn is required for playable race maps.
- Spawn IDs must be unique.
- Rotation controls the initial vehicle orientation.
Checkpoints
Checkpoints define required route progress.
{
"id": "checkpoint_1",
"position": [
0,
2,
80
],
"size": [
12,
6,
3
],
"rotation": [
0,
0,
0
],
"order": 1
}Rules:
ordermust be stable and deterministic.- Checkpoint volumes must be large enough for reliable detection.
- Shortcut fixes require a new release.
Finish Zones
Finish zones complete the race.
{
"id": "finish_1",
"position": [
0,
2,
500
],
"size": [
14,
6,
3
],
"rotation": [
0,
0,
0
]
}Rules:
- A race map needs at least one finish zone.
- Finish detection is deterministic gameplay data.
Placed Objects
Placed objects reference assets from source/assets.json.
{
"id": "road_a",
"asset": "road_10m",
"position": [
0,
0,
20
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
],
"collision": true
}Rules:
assetreferences an assetid.collision: truemeans the object affects gameplay.- Moving, scaling, rotating, adding, or removing collision objects changes deterministic data.
Zones And Triggers
Zones and triggers are gameplay volumes.
{
"id": "boost_1",
"type": "boost",
"position": [
0,
1,
120
],
"size": [
8,
3,
12
],
"direction": [
0,
0,
1
],
"strength": 1.5
}Recommended v1 zone types:
boostkillresetcheckpointfinishcustom
Any zone that can affect vehicle state, race progress, or validation is deterministic.
Race Settings
raceSettings defines race rules.
{
"laps": 1,
"startMode": "standing",
"respawnMode": "last_checkpoint",
"allowRespawn": true
}Changing race settings creates a new deterministic release.
source/visuals.json
source/visuals.json contains client presentation data.
{
"environment": {
"sky": "clear",
"timeOfDay": "day"
},
"lights": [
{
"id": "sun_key",
"type": "directional",
"rotation": [
-45,
35,
0
],
"intensity": 1
}
],
"decorations": [
{
"id": "banner_1",
"asset": "finish_banner",
"position": [
0,
5,
498
],
"rotation": [
0,
0,
0
]
}
],
"audio": []
}Visual-only data should not affect deterministic validation.
For v1, every published upload still creates a new map release. Do not implement visual-only shared leaderboard scopes yet.
source/assets.json
source/assets.json is the stable manifest for assets referenced by the map. It lets the backend resolve existing asset versions or register missing bundled assets during .mtamap upload.
{
"assets": [
{
"id": "road_10m",
"type": "object",
"name": "Road 10m",
"root": "assets/objects/road_10m",
"entrypoint": "object.json",
"assetVersionId": null,
"usage": [
"collision",
"visual"
],
"visibility": "public"
},
{
"id": "asphalt_diffuse",
"type": "texture",
"name": "Asphalt Diffuse",
"root": null,
"entrypoint": null,
"assetVersionId": "33333333-3333-3333-3333-333333333333",
"usage": [
"visual"
],
"visibility": "public"
},
{
"id": "checkpoint_sound",
"type": "sound",
"name": "Checkpoint Sound",
"root": "assets/sounds/checkpoint_sound",
"entrypoint": "checkpoint.ogg",
"assetVersionId": null,
"usage": [
"sound"
],
"visibility": "private"
}
]
}Asset fields:
| Field | Required | Rule |
|---|---|---|
id |
yes | Stable local asset ID within the package. |
type |
yes | Asset type. |
name |
yes | Display name. |
root |
required for bundled assets | Folder inside .mtamap that contains this asset package. |
entrypoint |
type-dependent | Main file relative to root. |
assetVersionId |
no | Existing backend asset version to reuse. |
usage |
yes | One or more usage categories. |
visibility |
yes | public or private. |
Resolution rules:
- If
assetVersionIdis set, the backend should resolve that exact existing asset version. - If
assetVersionIdis empty, the backend should register the bundled asset content fromroot. - If
rootis set,entrypointmust stay insideroot. - If the bundled content hash already exists, the backend should reuse the existing asset version.
- If the bundled content hash does not exist, the backend should create a new asset and asset version.
- Published map releases must link to exact asset versions, never "latest asset".
- The runtime compiler must resolve every placed object asset reference to an exact
assetVersionId. - Do not treat every file below
assets/**as an independent asset by default. - A bundled compound object folder is one asset version unless it explicitly references external
assetVersionIddependencies.
Supported v1 asset types:
objectmeshtexturematerialshadersoundscriptthumbnailother
Supported v1 usage values:
collisionvisualtexturematerialshadersoundscriptthumbnailother
Asset Visibility
Each asset can be public or private.
Public assets:
- Can have public asset pages.
- Can be reused by other maps when reuse workflows exist.
- Can show which public maps use them.
Private assets:
- Stay visible to the owner, map co-authors, and admins.
- Can still be bundled into a map runtime package if the map needs them.
- Should not expose standalone downloads to unrelated users.
- Are still normal assets with
assetIdand immutableassetVersionIdrecords. - Are not reusable by unrelated users.
Default asset visibility is public unless the editor or upload flow marks an asset private.
Private single-map assets should not become a second special storage model. They are normal assets with restricted visibility.
Asset Deduplication
The backend deduplicates asset content by hash, not by filename.
same content hash = same asset version content
different content hash = different asset version contentRules:
- Filenames are not identity.
- Local asset IDs are editor/package identity only.
- Backend asset versions are immutable content records.
- Map releases link to exact asset versions.
- Deterministic assets must be included in the map release deterministic hash when they affect gameplay.
Standalone Asset Packages
Reusable assets can also be uploaded outside a map package.
Recommended extension:
.mtaassetExample standalone object upload:
road_barrier.mtaasset
manifest.json
object.json
collision.glb
mesh.glb
material.json
textures/
barrier_diffuse.webp
shaders/
barrier.shader
preview/
thumbnail.webpmanifest.json:
{
"format": "mtadm.asset.source",
"formatVersion": 1,
"type": "object",
"name": "Road Barrier",
"slug": "road-barrier",
"visibility": "public",
"usage": [
"collision",
"visual"
],
"createdWithGameVersion": "0.3.0"
}Standalone asset upload and bundled map upload should create the same backend asset/version records. The source is different, but maps still consume exact assetVersionId values. The full asset package contract is documented in Assets.
The same root + entrypoint model is used when a .mtamap embeds asset content. A bundled object folder is equivalent to the contents of a standalone .mtaasset package, minus the outer file extension.
Object Assets
Object assets are reusable map building blocks.
Example assets/objects/road_10m/object.json:
{
"format": "mtadm.asset.object",
"formatVersion": 1,
"id": "road_10m",
"name": "Road 10m",
"mesh": "mesh.glb",
"collision": "collision.glb",
"materials": [
"material.json"
],
"bounds": {
"size": [
10,
1,
12
]
}
}Rules:
- Object metadata must reference files relative to its own folder.
- Collision files affect deterministic gameplay when used by a collision-enabled placed object.
- Mesh/material-only changes are visual unless they change collision or physics material data.
Materials And Textures
Material metadata should reference texture assets by relative path or asset ID.
{
"format": "mtadm.asset.material",
"formatVersion": 1,
"name": "Asphalt",
"textures": {
"albedo": "../textures/asphalt_diffuse.webp"
}
}Texture validation should eventually check dimensions, format, and size limits.
Scripts
Lua scripting is not documented as a public API yet.
Package paths are reserved:
scripts/server/*.lua
scripts/client/*.luaRules:
- Uploaded scripts are untrusted.
- Server-side scripts are deterministic data.
- Client-side scripts must not affect authoritative gameplay.
- Public scripting support requires a finalized sandbox policy.
Editor Data
editor/ is for editor-only data.
editor/
editor_state.json
thumbnail.webpRules:
- Editor data is never required by game servers.
- Editor state must not include local machine paths or secrets.
- Thumbnails can be used for website preview.
.mtabin
.mtabin is the generated runtime package.
It is used by:
- public servers
- local servers
- clients joining servers
- future replay validation
- future leaderboard validation
The backend generates .mtabin; users should not upload it as the source-of-truth package.
Generated .mtabin Structure
Generated v1 runtime package:
done-v3.0.1.mtabin
runtime_manifest.json
deterministic/
gameplay.json
collision.json
physics_materials.json
scripts/
server.lua
client/
visuals.json
assets/
33333333-3333-3333-3333-333333333333/
object.json
mesh.glb
material.json
textures/
barrier_diffuse.webp
preview/
thumbnail.webpThe game must rely on runtime_manifest.json entrypoints and asset metadata instead of assuming every optional file exists.
runtime_manifest.json
The runtime manifest is generated by the backend.
{
"format": "mtadm.map.runtime",
"formatVersion": 1,
"mapId": "map_01JABC",
"mapReleaseId": "mapver_01JXYZ",
"slug": "done",
"title": "Done",
"version": {
"major": 3,
"minor": 0,
"patch": 1,
"display": "v3.0.1"
},
"minGameVersion": "0.3.0",
"physicsVersion": "phys_1",
"vehicleVersion": "vehicle_phys_1",
"scriptApiVersion": "lua_map_api_1",
"determinismVersion": "det_1",
"hashes": {
"sourcePackageHash": "sha256:...",
"runtimePackageHash": "sha256:...",
"deterministicHash": "sha256:...",
"gameplayHash": "sha256:...",
"collisionHash": "sha256:...",
"serverScriptHash": "sha256:...",
"visualHash": "sha256:..."
},
"entrypoints": {
"gameplay": "deterministic/gameplay.json",
"collision": "deterministic/collision.json",
"physicsMaterials": "deterministic/physics_materials.json",
"serverScript": "deterministic/scripts/server.lua",
"visuals": "client/visuals.json"
},
"assets": [
{
"assetId": "asset_01JABC",
"assetVersionId": "33333333-3333-3333-3333-333333333333",
"localAssetId": "road_barrier",
"type": "object",
"usage": [
"collision",
"visual"
],
"deterministic": true,
"contentHash": "sha256:...",
"deterministicHash": "sha256:...",
"entrypoint": "client/assets/33333333-3333-3333-3333-333333333333/object.json",
"dependencies": []
}
]
}The game/server must verify:
formatismtadm.map.runtime.formatVersionis supported.mapReleaseIdexists.deterministicHashexists.runtimePackageHashmatches the downloaded package.- Physics, vehicle, script API, and determinism versions are supported.
- Required entrypoints exist.
- Every asset entry references an exact
assetVersionId. - Deterministic asset hashes match the deterministic data the server loads.
Deterministic Hash
deterministicHash covers gameplay-affecting data.
It must include:
- gameplay source/runtime data
- collision data
- deterministic asset versions
- checkpoints
- spawns
- finish zones
- trigger zones
- boost zones
- kill/reset zones
- physics materials
- server-side scripts
- race rules
- deterministic moving objects
- physics version
- vehicle version
- determinism version
It should not include visual-only data by default:
- textures
- shaders
- sounds
- music
- lights
- particles
- thumbnails
- decorative non-collision objects
For v1, do not share leaderboards between different map releases even if only visuals changed.
Backend Validation
The backend must validate .mtamap before generating .mtabin.
Validation responsibilities:
- Verify archive safety.
- Verify required files exist.
- Parse
manifest.json. - Parse
source/map.json. - Parse optional
source/visuals.json. - Parse optional
source/assets.json. - Resolve existing
assetVersionIdreferences. - Register missing bundled assets.
- Validate referenced asset paths.
- Validate file extensions and sizes.
- Reject unsafe or unsupported files.
- Calculate all hashes itself.
- Deduplicate assets by content hash.
- Include deterministic asset versions in the map release deterministic hash.
- Generate runtime package.
- Store immutable map release metadata.
Never trust user-provided checksums.
Map Release Status
Recommended release statuses:
draftprocessingvalidation_failedpublishedrecommendeddeprecatedhiddenblocked
Blocked runtime downloads must be denied by the backend/media layer, not only hidden in the frontend.
Leaderboards
Leaderboards are scoped to exact map releases.
Required future leaderboard identity:
mapId
mapReleaseId
deterministicHash
physicsVersion
vehicleVersionDefault map pages should show the recommended release leaderboard. Version pages should show that exact version's leaderboard.
Replays
Replay validation requires exact runtime identity.
Future replay metadata must include:
mapId
mapReleaseId
slug
version
deterministicHash
runtimePackageHash
gameVersion
physicsVersion
vehicleVersion
scriptApiVersion
determinismVersion
tickRate
tickCount
inputHash
resultTimeMsA replay is valid only when simulated against the exact matching runtime package and deterministic versions.
Server Loading Flow
When a server starts a public map:
- Select the recommended map release.
- Download
.mtabin. - Verify
runtimePackageHash. - Read
runtime_manifest.json. - Verify supported versions.
- Load deterministic entrypoints.
- Announce runtime identity to clients.
The server does not need to load visual-only textures, sounds, shaders, or thumbnails.
Client Join Flow
When a client joins:
- Server sends
mapId,mapReleaseId,deterministicHash,runtimePackageHash, and version metadata. - Client checks local cache.
- Client downloads
.mtabinif missing. - Client verifies package hash.
- Client verifies runtime manifest matches server identity.
- Client loads deterministic data and client visuals.
If any identity or hash check fails, the client must not join the race.
Offline Flow
Offline mode should still start a local server from a runtime package.
Local .mtamap
-> local validation
-> local .mtabin
-> local server loads .mtabin
-> client connects locallyLocal packages can use local IDs:
{
"mapId": "local:done",
"mapReleaseId": "local:done:sha256:...",
"deterministicHash": "sha256:..."
}Version Compatibility
The game should reject or warn on unsupported:
formatVersionminGameVersionphysicsVersionvehicleVersionscriptApiVersiondeterminismVersion
Do not silently load unknown deterministic formats for public play.
Current Public API Shape
Map versions should expose enough data for game/server tooling.
{
"id": "mapReleaseId",
"map_id": "mapId",
"version_label": "v3.0.1",
"runtime_download_url": "/media/...",
"source_download_url": "/media/...",
"status": "published",
"moderation_visibility": "visible",
"runtime_package_hash": "sha256:...",
"deterministic_hash": "sha256:...",
"gameplay_hash": "sha256:...",
"collision_hash": "sha256:...",
"server_script_hash": "sha256:...",
"visual_hash": "sha256:...",
"min_game_version": "0.3.0",
"physics_version": "phys_1",
"vehicle_version": "vehicle_phys_1",
"script_api_version": "lua_map_api_1",
"determinism_version": "det_1",
"assets": []
}source_download_url must be empty or unavailable when editable source sharing is private.
Security Expectations
Editor:
- Export normalized relative paths.
- Do not include absolute paths.
- Do not include local machine paths.
- Do not include secrets.
- Keep IDs stable between exports when possible.
Backend:
- Treat
.mtamapas untrusted. - Reject unsafe archives.
- Validate JSON before using it.
- Calculate hashes server-side.
- Keep published runtime releases immutable.
Game/client:
- Verify hashes before loading runtime packages.
- Do not trust display names, filenames, or slugs for identity.
- Do not execute scripts outside the approved sandbox.
Server:
- Use
.mtabinfor public play. - Announce exact map runtime identity to clients.
- Reject mismatched clients.
Public Launch Checklist
Before public ranked play, the platform needs:
source/assets.jsonparsing instead of filename guessing.- Collision and physics material runtime files.
- Texture size validation.
- Mesh/collision complexity validation.
- Lua sandbox rules before public scripting.
- Backend-denied blocked runtime downloads.
- Dedicated game/server map lookup endpoints.
- Leaderboards scoped to
mapReleaseId. - Replay validation against exact
.mtabin.