Map Markers v1
Status: contract locked for implementation.
This document defines the v1 marker contract that implementation should follow. It is not yet a shipped stable Lua runtime API, but the contract below should now be treated as locked unless implementation reveals a concrete issue.
Current implementation note:
- authoritative server-side marker enter/exit/hit tracking has now started against this contract
- this still does not mean the full public scripting API is declared stable yet
Scope​
Markers are authored map elements with controlled visual and trigger data.
Markers are not:
- arbitrary Godot node graphs
- arbitrary mesh/material/shader property bags
- a replacement for checkpoints or race pickups
Markers should remain a distinct family because they are lighter-weight than checkpoints and pickups and can exist without gameplay progression/effect semantics.
Marker Types​
cylinderarrowcoronaring
Explicitly not marker types:
checkpointrace_pickup
Current intended client visual mapping:
cylinder->MeshInstance3DusingCylinderMesharrow->MeshInstance3Dusing cone-shapedCylinderMeshcorona->Sprite3Dor billboarded quadring->MeshInstance3DusingTorusMesh
These are presentation details. The authored and baked APIs stay marker-domain specific.
Authority Split​
Server-authoritative:
- marker existence
- marker transform and size
- marker inclusion in authoritative baked trigger/query data
- future marker hit/effect semantics when gameplay uses them
Client-only:
- mesh/sprite choice
- shader/material setup
- other purely visual effects
Authored API​
Marker v1 exposes one distinct authored family:
kind = "marker"markerType = "cylinder" | "arrow" | "corona" | "ring"
Marker v1 does not overload markerType with checkpoint or pickup semantics.
Authored source should expose a marker record under the map source package.
Current working editor source still uses the flat primitives[] shape. The v1 target authored shape is:
{
"id": "marker_01",
"kind": "marker",
"markerType": "cylinder",
"position": { "x": 0.0, "y": 2.0, "z": 0.0 },
"scale": { "x": 4.0, "y": 4.0, "z": 4.0 },
"color": { "r": 0.98, "g": 0.72, "b": 0.18, "a": 0.88 },
"visible": true,
"collision": true,
"render": true,
"style": {
"opacity": 1.0,
"emission": 1.0,
"billboard": false
}
}
Authored field meanings:
id: stable marker id inside the map packagekind: alwaysmarkermarkerType: one of the supported marker visual familiesposition: authored local positionrotation: authored local visual rotation in degreesscale: authored visual/trigger scalecolor: authored display tint in0.0..1.0visible: editor/runtime visibility intentcollision: whether the marker participates in authoritative baked query datarender: whether the marker has a baked client render descriptorstyle: optional visual-only presentation parameters
Required fields:
idkindmarkerTypepositionscalerotationvisiblecollisionrender
Optional fields:
colorstyle
Validation rules:
kindmust bemarkermarkerTypemust be one ofcylinder,arrow,corona,ringscale.x,scale.y, andscale.zmust be positive numbersrotation.x,rotation.y, androtation.z, when present, must be numbers in degreescolorchannels, when present, must be in0.0..1.0style.opacity, when present, must be>= 0.0style.emission, when present, must be>= 0.0style.billboard, when present, must be booleanstyleremains presentation-only and must not affect authoritative gameplay behavior
Defaults when optional fields are omitted:
color = { r = 0.98, g = 0.72, b = 0.18, a = 0.88 }style.opacity = color.astyle.emission = 1.0style.billboard = (markerType == "corona")
Baked Authoritative API​
The authoritative baked marker descriptor belongs in baked runtime query/collision payloads. This descriptor is authoritative and server-owned.
V1 descriptor:
{
"markerId": "marker_01",
"markerType": "cylinder",
"position": { "x": 0.0, "y": 2.0, "z": 0.0 },
"size": { "x": 4.0, "y": 4.0, "z": 4.0 },
"enabled": true
}
V1 rules:
- only markers with
collision = trueare included in the authoritative baked marker table - authoritative baked data must stay independent from Godot-specific mesh/material choices
markerTypeis carried for family/type discrimination only- checkpoint progression and pickup effects must not be encoded as generic marker runtime data
- v1 authoritative marker data does not include effect metadata
Authoritative runtime meaning:
positionandsizedefine the marker query volume- server authority decides whether a player/vehicle is inside the marker
- clients must not authoritatively decide marker overlap
Runtime Marker Semantics​
V1 marker semantics are deliberately narrow:
- markers are authoritative query/trigger volumes
- markers do not carry built-in gameplay effects in v1
- marker events are observation events, not effect-definition objects
- if game logic wants teleport/speed/etc., that will be layered later on top of markers or separate gameplay families
V1 overlap rules:
- the server evaluates marker overlap against the controlled player/vehicle state
- a marker becomes
enteredwhen an entity was outside on the previous authoritative update and inside on the current authoritative update - a marker becomes
exitedwhen an entity was inside on the previous authoritative update and outside on the current authoritative update hitin v1 is defined as the same moment asentered- no repeated
hitspam while remaining inside the same marker
V1 scope limits:
- no built-in cooldown field
- no built-in one-shot consumption field
- no marker-owned effect fields
- no checkpoint progression semantics
- no pickup semantics
Baked Render API​
The render descriptor belongs in map/map_render.json.
V1 descriptor:
{
"id": "marker_01",
"markerType": "cylinder",
"position": { "x": 0.0, "y": 2.0, "z": 0.0 },
"rotation": { "x": 0.0, "y": 0.0, "z": 0.0 },
"scale": { "x": 4.0, "y": 4.0, "z": 4.0 },
"visible": true,
"effectiveVisible": true,
"color": { "r": 0.98, "g": 0.72, "b": 0.18, "a": 0.88 },
"opacity": 1.0,
"emission": 1.0,
"billboard": false
}
V1 rules:
- only markers with
render = trueshould emit a render descriptor markerTypeis the only marker-family discriminator in v1 render data- render descriptors may grow presentation-only fields without changing authoritative gameplay meaning
coronaremains a sprite/billboard presentation concern in render data, not an authored engine-component contractbillboardis allowed as a render hint only
Required baked render fields:
idmarkerTypepositionscalevisibleeffectiveVisiblecoloropacityemissionbillboard
Current internal implementation note:
- internal bake currently emits marker presentation data into a dedicated
markersarray inmap/map_render.json - that internal shape is now largely aligned with this v1 contract
- this page still describes the intended public contract, not an exposed stable runtime API
Runtime Events​
V1 external runtime event direction:
map:marker_hitmap:marker_entermap:marker_exit
These events should come from authoritative runtime logic, not from client-local mesh overlap tests.
V1 event payload contract:
{
"mapId": "editor_smoke_01",
"markerId": "marker_01",
"markerType": "cylinder",
"playerId": 1,
"vehicleEntityId": 42,
"position": { "x": 0.0, "y": 2.0, "z": 0.0 },
"size": { "x": 4.0, "y": 4.0, "z": 4.0 }
}
Payload rules:
mapIdis the authoritative loaded map idmarkerIdis the authored marker idmarkerTypeis the baked authoritative marker typeplayerIdis the authoritative player id if knownvehicleEntityIdis the authoritative controlled vehicle/entity id if knownpositionandsizeare the authoritative baked descriptor values
Delivery rules:
map:marker_hitandmap:marker_enterare emitted on the same authoritative transition into the markermap:marker_exitis emitted only when leaving a previously entered marker- event ordering is authoritative per entity/marker pair
- event delivery should happen on the server-side runtime API first
- client-facing presentation reactions should be driven from authoritative state, not local-only overlap guesses
Not Shipped Yet​
Not implemented as stable public API yet:
- stable marker render instancing contract
- stable marker authoring schema migration from current flat source records to the planned nested shape
- final public Lua event binding surface
- final query/helper methods for script-side marker inspection
Current Internal Status​
Current internal editor/runtime status:
- internal marker authoring currently supports:
cylinderarrowcoronaring
- internal save/load and bake for those marker subtypes exist
- internal baked client presentation now also exists for those marker subtypes
- internal authored marker style fields now exist:
opacityemission
- this is still internal editor/runtime behavior, not a stable external public contract yet
Current internal implementation note:
- current editor source still uses flat records in
primitives[] - current public plan uses the cleaner nested shape shown above
- migration from the current internal source format to the planned public-facing shape is still pending
Related Documents​
map_gameplay_plan.mdx