Exports
Four exports, all of them server-side: call them from a server script with the player's source. There is no client export, so a client that wants the editor triggers an event of its own and the server calls from there.
The editor is a UI another resource can borrow: it places props, hands the list back, and stores nothing of its own unless you asked it to.
Opening the Editor
exports.rm_mapeditor:openEditor(source --[[number]], opts --[[table]], cb --[[function]])The callback is required, and it fires exactly once: with the rows when the player is done, or with nil when they cancelled, dropped, or the session could not open.
exports.rm_mapeditor:openEditor(src, {
origin = shellOrigin, -- given: results come back as offsets from it
existing = savedFurniture, -- the current layout, edited in place
hidden = savedHides, -- and the pieces of map it took out of view
filter = { tags = { 'furniture' } },
limit = 50,
}, function(result, hidden)
-- result: rows, or nil when cancelled
-- hidden: pieces of the map the session took out of view
end)What Comes Back
A prop row:
solid is false for a prop whose collision was switched off, tint is a palette row and absent at 0, group is a name and absent for a prop in none. Rotations are degrees, in order 2 (ZXY), the order SetEntityRotation wants.
A light row says so:
And the second argument, the pieces of map the session hid:
Store both halves. A layout is not reproducible from the props alone: some of them stand where the map's own geometry used to, so replaying the props without the hides stands a copy of a wall inside the wall it replaced. Both go back in through existing and hidden.
Options
origin
a vector3. Rows come back as offsets from it rather than world coordinates
existing
rows to open with, edited in place
hidden
the hides that went with them
limit
how many objects this session may place
area
a zone the session is held to: { x, y, z, radius }, or a polygon { points = { vector3, ... }, minZ, maxZ }
shell
build inside an interior, see below
filter
what the picker offers: categories, excludeCategories, tiers, tags, maxSize
allow
what the session may make, see below
look
{ hour, weather } held for everybody in the session
mode
'inspect' opens the viewer instead. openInspector is the readable way to say it
model
opens the viewer straight onto one prop
Building Inside a Shell
The boundary becomes the building's own box and everything comes back in the building's frame, so the same layout stands up in every copy of that shell, at whatever heading each one was spawned at.
A model and a place, never a handle or a network id: a shell is scenery, spawned on each client that needs to see it, so there is usually no network id naming it and the handle that exists belongs to a client. One already standing there is worked inside and left alone; otherwise one goes up for the session and comes down with it.
margin widens the box by that many metres at every face (0.5 by default), because a box measured to the millimetre refuses the chair somebody meant to push against the wall.
Standing the result back up is the mirror of it:
What a Session May Make
Four things this editor does are not a prop standing in a map: a light, a hidden piece of map, a prop's collision switch, and the colour of the light inside a drawable. Each is a call somebody has to keep making, so a caller that stores rows and spawns props itself gets the props and whatever of these it makes the calls for.
So a session says what it offers:
runtime is the switch over all four and each flag is under it. Off by default for a shell session, because that caller is exactly the one who will stand the layout up somewhere else. The standalone editor gets everything.
Opening the Viewer
The same session with nothing to place: the catalogue as a reference, and a click on the world to ask what something is. The callback is optional and is handed nothing, since a session that places nothing has no result, and it fires on close, so you can put your own UI back.
model
opens straight onto one prop, by name or hash
tiers
what the picker browses. The viewer opens on all three, because a room's own surfaces are often the reason for opening it
filter
the editor's filter, the same fields
Asking About a Prop
Both are synchronous, read the server's own files, and need no player and no session. Both answer nil for a model this catalogue does not carry, which is also how you ask whether it carries one.
getPropDetails answers for every row the pipeline measured, including the ones the picker does not offer:
measured off the asset
dims, groundOffset, bbmin, footprint, triangles, shaders, hasCollision, collisionType, lights, lightTypes, emissive, alpha, decal, cloth, memoryBytes, lodDist, ytyp, dlc, path
decided about it
category, label, tier, minBuild, offered
where the shipped map uses it
usedWorld, usedInterior, usedAt, usedIn
offered is not the same question as tier: a row can be tiered prop and still not be offered, because a measurement said it cannot be placed.
getPropName takes a hash in either spelling and also takes a name, which is the other use for it: somewhere to send a name of unknown case before comparing it to one of yours.
Replacing the Commands
Any command can be false in cfg.lua, and these exports are how a menu, an item or a job script opens the same thing:
No permission is asked for on the way through. The command checks an ace because a player typed it; an export was called by a server script that had already decided, and this one opens the editor for the player it names.