World objects

From Wiki G1R-MP G1 Remake Multiplayer
Revision as of 18:19, 20 September 2026 by QCherry (talk | contribs) (Document Map Editor, saved maps, static scene deployment and Lua APIs since 0.1.4)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

World objects

Client-side; since 0.1.4 (in development, not public 0.1.3).

WorldObjects.request submits asynchronous commands for resource-owned static meshes. It is generic gameplay-resource API, not restricted to the map editor. There is no engine global named CreateModel or MapEditor.CreateObject; MapEditor helpers are Lua functions inside the editor resource only.

Function and events

requestIdOrFalse = WorldObjects.request(command)
-- Events on the calling resource:
-- onClientWorldObjectResult(requestId, ok, reason, json)
-- onClientWorldObjectsReset()

Supply a table. A numeric request ID means queued, not spawned/applied. false means immediate validation/readiness/capacity/IPC rejection. Match the ID in onClientWorldObjectResult before committing Lua state. The result's JSON argument is a string; decode it with fromJSON. Never expose native pointers to Lua.

Commands

op Required fields and effect
create id, mesh, x,y,z, pitch,yaw,roll, sx,sy,sz, hidden,collision,selected. Creates a new instance; an existing ID fails with id-already-exists.
update id, all nine transform numbers and all three booleans. Updates an existing instance. Full state, not a partial patch. Does not replace the mesh: delete/create to change the asset.
delete id. Removes the caller's instance; an absent ID also succeeds.
clear Only op. Removes all instances owned by the calling resource, never original world actors or another resource's objects.
pick op. Traces from the native player-controller view through the OS cursor, up to 1000 m. Requires usable game focus/viewport.
raycast x,y,z,tx,ty,tz, start and end in metres, length at most 1000 m.
lease Only op. Registers/renews ownership without creating a mesh; the host also renews ownership automatically.

Object IDs: 1-64 ASCII letters, digits, underscores or hyphens, scoped to the calling resource. Mesh must be a valid installed static-mesh object path like /Game/Directory/Asset.Asset, at most 512 characters, not an OBJ filename or filesystem path. The editor additionally restricts choices to its shipped catalog. A syntactically valid path can still fail to load.

Position: metres, each coordinate +/-100000. Rotation: pitch/yaw/roll in degrees, each +/-36000. Scale: sx/sy/sz in [0.01,100]. All numbers must be finite. The three booleans are required even when false. selected requests custom-depth selection presentation; it does not guarantee an outline without matching post-processing. Names and editor locks belong to the Lua document, not the native command schema.

Result contract

Successful mutations return JSON containing their id; clear/lease return an empty JSON object. Successful traces return {"hit":false} or a hit containing hit,x,y,z,nx,ny,nz,id. The coordinates are metres, normals unitless. A hit ID is populated only for a recognized object owned by the caller; it is empty for ordinary world surfaces or unrecognized/foreign instances. Do not access hit coordinates when hit=false. Traces depend on collision; use the editor hierarchy to select non-colliding objects.

Typical failure reasons include world-not-ready, native-dependencies-pending, mesh-load-or-spawn-failed, id-already-exists, object-limit, owner-limit, object-invalid-or-transform-failed and raycast-unavailable. An unanswered host request reports native-timeout after 10 seconds. A timeout does not prove a mutation was never applied: do not blindly retry it. Reconcile/reload the owned scene.

Example: complete create command

function requestMesh(meshPath, x, y, z)
    return WorldObjects.request({
        op="create", id="example_rock", mesh=meshPath,
        x=x, y=y, z=z, pitch=0, yaw=0, roll=0,
        sx=1, sy=1, sz=1,
        hidden=false, collision=true, selected=false
    })
end
-- Supply an actual installed mesh path and your desired coordinates.
-- Call after gameplay readiness; wait for onClientWorldObjectResult.
-- Reusing example_rock while it exists fails: assign unique IDs per instance.

Example: asynchronous deletion

local pending = {}
addEventHandler("onClientWorldObjectResult", resourceRoot,
    function(requestId, ok, reason, json)
        local id = pending[requestId]
        if not id then return end
        pending[requestId] = nil
        if ok then
            outputDebugString("Deleted owned object: " .. id)
        else
            outputDebugString("Delete not confirmed: " .. tostring(reason), 1)
        end
    end)

function deleteOwnedObject(id)
    local requestId = WorldObjects.request({op="delete", id=id})
    if not requestId then return false end
    pending[requestId] = id
    return true -- accepted only; completion is handled above
end

For full scene creation, use the supplied map_scene/client.lua loader and Map scene deployment. It includes startup readiness retries and reconstruction, which a one-shot on-start create call would miss.

Lifetime, limits and multiplayer

There are at most 512 native objects across all owners on a client and 16 owning resources. Commands encode to at most 4096 bytes; the host allows 32 pending object requests and 80 submissions per one-second rate window across resources. Submit large scenes incrementally. The native ownership lease is 15 seconds and the host sends renewals every second. Stop/disconnect/world changes clear owned instances. Handle onClientWorldObjectsReset to discard stale IDs/pending state and reconstruct deliberately; the event has no arguments.

This API creates client-local static actors, not synchronized interactive doors, inventory items, server collision geometry or networked physics. Sending the same trusted static map resource to every player, as map_scene does, reconstructs the same layout for late joiners. Dynamic multiplayer edits require their own server-authorized state distribution.