Quick navigation

Go to

Map, markers & zones

3D world markers

Eight native mesh shapes, RGBA colors, dimensions and rotation. Lua creation, updates, loading diagnostics, resource ownership and cleanup.

6 min read1,250 wordsMarkdown
On this page7 sections

Use Open77.markers in a client resource to place checkpoints, rental locations, objectives and other indicators in the world. Markers are native REDengine meshes with depth testing and a translucent gradient, not WebUI overlays or map pins.

The eight-shape catalogue, RGBA colors, transforms and loading diagnostics require client 2.31.13+op77.83 or newer. The meshes ship with the client; server developers do not need Blender, WolvenKit or a separate asset pack.

Create a marker

Request the permission in your resource manifest:

permissions { "world.markers" }

Run this in your client script once the world and local character are ready. Use open77:worldReady for a new session and check Open77.character.state().attached when starting a resource in an already-loaded world; see resource lifecycle.

local rentalMarker, reason = Open77.markers.create({
    position = { x = 381, y = -2396, z = 182.06 }, -- replace with your location
    shape = "cylinder",
    radius = 1.5,
    height = 1.2,
    color = { r = 0, g = 220, b = 255, a = 180 },
    maxDistance = 80,
})
assert(rentalMarker, reason)

Keep the returned decimal-string handle unchanged; do not convert it with tonumber. Creation is asynchronous: receiving a handle does not mean the mesh has finished loading.

A marker only draws a location. It does not create an interaction prompt, collision, map pin or rental action. Pair it with interactions, PolyZone or proximity zones, and validate purchases and permissions on the server. Use blips for the map and minimap.

Shapes

Open77.markers.shapes() returns the following stable string identifiers. It needs no permission because it only reads the static catalogue. Numeric marker types from other games are not supported.

Shape Geometry
ring Thin horizontal ring with a hollow centre
cylinder Open cylindrical wall with an upward fade
checkpoint Vertical circular checkpoint frame
arrow Extruded arrow pointing down; rotate it to point elsewhere
chevron Extruded directional chevron
cone Downward-pointing cone
diamond Low-poly diamond
sphere Rounded sphere
for _, shape in ipairs(Open77.markers.shapes()) do
    print(shape)
end

Position is the mesh's bottom origin, not its centre. There is no automatic terrain snap: place ground markers slightly above the floor to avoid z-fighting. Most meshes have a unit width and height before scaling; the ring is only 0.035 units thick.

Options and dimensions

create(options) and update(id, patch) use the same fields. Only position is required on creation; updates leave omitted fields unchanged.

Option Default Meaning
position Required on create World position {x, y, z}, in metres
shape "ring" One of the eight shape names
style "interaction" Palette: cyan interaction, gold objective, green spawn, red danger
radius 1.5 Horizontal half-width, 0.1..50 metres
height 1.5 Vertical multiplier, 0.01..100
scale {x=1, y=1, z=1} Additional XYZ multipliers, 0.01..100 on each axis
rotation {x=0, y=0, z=0} Local XYZ Euler angles in degrees; Z is yaw
color Style palette {r, g, b, a} integer channels 0..255; omitted alpha defaults to 180
visible true Explicit visibility
minDistance 0 Hide below this distance from the current camera, in metres; must be non-negative
maxDistance 100 Hide beyond this camera distance, 1..500 metres

Horizontal dimensions are 2 * radius * scale.x and 2 * radius * scale.y. Vertical size is height * scale.z times the mesh's authored height. Effective dimensions are capped at 200 metres, and minDistance must be less than maxDistance.

An explicit RGBA color overrides the style palette. Alpha 0 is invisible; 255 is the maximum opacity before the mesh gradient. Colors are independent per marker. Hex strings are not accepted by this native API.

-- Change only this marker's color and dimensions.
assert(Open77.markers.update(rentalMarker, {
    height = 2.0,
    scale = { x = 1.2, y = 1.0, z = 1.0 },
    rotation = { x = 0, y = 0, z = 45 },
    color = { r = 255, g = 60, b = 100, a = 220 },
}))

-- Restore a style palette. Omitting color would keep the custom color.
assert(Open77.markers.update(rentalMarker, { style = "objective", color = false }))
assert(Open77.markers.update(rentalMarker, { visible = false }))
assert(Open77.markers.update(rentalMarker, { visible = true }))

Invalid patches fail atomically, without partially changing the marker. Color, shape and visibility changes can rebuild the native entity asynchronously while retaining the Lua handle. Do not animate colors every frame. Small moves, dimensions and rotation can update an attached mesh in place; moves more than 25 metres from its native anchor recreate it to keep streaming bounds local.

Functions and snapshots

All functions below are in Open77.markers. Except for shapes(), they require world.markers and operate on the calling resource's markers.

Function Result
create(options) Decimal-string handle, or nil, reason
update(id, patch) true, or false, reason
get(id) One owned snapshot, or nil, reason
list() Array of owned snapshots, or nil, reason
remove(id) true, or false, reason
clear() Removes this resource's markers; returns true, or false, reason
shapes() Array of supported shape names

Snapshots contain id, position, shape, style, radius, height, scale, rotation, color, customColor, minDistance, maxDistance, visible, rendered, failed, and an optional error string. color is the effective RGBA color; customColor indicates an explicit override.

rendered means the native mesh is attached and enabled. It does not guarantee that the marker is on screen or unoccluded. A marker may be outside the view, behind a wall, hidden by its distance range or still loading. Use failed and error to distinguish a loading failure from normal visibility changes.

-- Read again after creation to inspect asynchronous loading.
local state, reason = Open77.markers.get(rentalMarker)
if not state then
    print("Marker unavailable: " .. tostring(reason))
elseif state.failed then
    print("Marker failed: " .. tostring(state.error))
end

-- Explicit cleanup when this location is no longer needed.
assert(Open77.markers.remove(rentalMarker))
rentalMarker = nil
-- Open77.markers.clear() removes all markers owned by this resource.

See the Lua API reference for individual function signatures.

Errors and troubleshooting

Immediate failures return a reason as the second return value. Native loading failures appear later in the snapshot's error when failed is true.

Reason Meaning / action
permission_denied:world.markers Add the manifest permission to the calling resource.
world_unavailable Wait for the world to be ready before creating the marker.
markers_backend_unavailable No client marker backend is available in this host.
position_required, invalid_position, invalid_scale, invalid_rotation, invalid_color, invalid_argument Check field types and ranges; colors must be integer RGBA bytes.
unsupported_shape, unknown_marker_style Use a supported shape or palette name.
invalid_marker_id The handle is malformed.
not_found The marker is gone or, for get, is not owned by this resource.
owned_by_another_resource An update or removal targeted another resource's marker.
quota_exceeded Reduce active markers or allow pending native cleanup to finish.
marker_assets_missing, marker_appearance_missing The client could not load the required mesh/material or appearance. Update or repair the client installation.
marker_streaming_timeout, entity_spawn_failed Native loading did not finish or entity creation failed; inspect client diagnostics.
unsupported_material_runtime The runtime does not match the supported material implementation. Use a compatible client/game build.

If failed is false but nothing is visible, check the position and ground offset, alpha, visible, camera distance and world occlusion before recreating the marker.

Ownership, limits and replication

The limit is 64 logical markers per resource and 256 native material slots across the client. Retiring entities keep their slots until render jobs and cleanup finish. A replacement can use a spare slot while the old entity retires; when the pool is full, it waits for safe cleanup. Repeatedly creating, recoloring and removing markers can therefore exhaust the pool temporarily.

Resource stop/reload and disconnect clean up native markers, including pending spawns. Recreate session markers after world readiness; also handle a resource started while the world is already loaded. Do not reuse handles from a previous resource lifetime.

Markers are local and are not automatically replicated. A server resource can send validated definitions to relevant clients, but visual markers are never proof of a player's position, routing bucket, permission or completed interaction.

The optional open77_markers package exports the same seven functions. Its markers belong to that facade package, not automatically to each resource calling its exports. Prefer the native API directly when you need your own resource's quota and automatic cleanup. The separate open77_worldui POI facade still exposes only ring/cylinder presets and prompt colors; use this native API for the full shape and RGBA options.

See also

// For agents & LLMsthis page as Markdownllms.txtllms-full.txt

// Guides Β· Lua API

Search the docs

1,905 guides and functions indexed

Try