Developer

4 min readUpdated 2 weeks ago

Overview

Props Carry provides client exports for reading and managing carried props, a server export for position data, and handler hooks in the open/ folder for your own rules. Props sync to nearby players through state bags. Admins manage the live item catalog in game with /carryadmin, and the changes persist to oxmysql and reach every player without a restart.

Server Exports

lua
1local positions = exports.ml_propscarry:GetPlayerPositions(source)
  • GetPlayerPositions(source): all custom positions of a player, as { [itemName] = { x, y, z, rx, ry, rz, bone? } }. bone is set when the player moved the item to another bone

Client Exports

Prop State

lua
1local props = exports.ml_propscarry:GetCarriedProps()
2local positions = exports.ml_propscarry:GetCustomPositions()
3local otherProps = exports.ml_propscarry:GetOtherPlayersProps()
  • GetCarriedProps(): Returns { itemName = propEntity, ... } for the local player
  • GetCustomPositions(): the local player's custom positions, in the same shape
  • GetOtherPlayersProps(): Returns { serverId = { itemName = propEntity, ... }, ... } for nearby players

Prop Management

lua
1exports.ml_propscarry:AddCarriedProp(itemName, propEntity)
2exports.ml_propscarry:RemoveCarriedProp(itemName)
3exports.ml_propscarry:RefreshProps()
4exports.ml_propscarry:ForceBroadcast()
  • AddCarriedProp(itemName, prop): Registers a prop entity as carried
  • RemoveCarriedProp(itemName): stops tracking the prop without deleting it, and returns the entity so you can handle it
  • RefreshProps(): Deletes all props and recreates them (full reset)
  • ForceBroadcast(): Forces a StateBag update to sync with other players

Spawning and Releasing

lua
1local entity = exports.ml_propscarry:SpawnProp(itemConfig)
2exports.ml_propscarry:ReleaseCarriedProp(itemName)
  • SpawnProp(itemConfig): creates and attaches the prop of a Config.Items entry on the local player. Returns the entity or nil
  • ReleaseCarriedProp(itemName): deletes the carried prop of that item, matched without case

Local Positions

lua
1exports.ml_propscarry:SetCustomPosition(itemName, pos)
2exports.ml_propscarry:ClearCustomPosition(itemName)
3exports.ml_propscarry:SetPendingPosition(itemName, pos)
4local pending = exports.ml_propscarry:GetPendingPosition(itemName)
5exports.ml_propscarry:ClearPendingPosition(itemName)
  • SetCustomPosition, ClearCustomPosition: change the position the local client uses for an item. They do not save to the server
  • SetPendingPosition, GetPendingPosition, ClearPendingPosition: the position waiting to be saved by the editor

pos is { x, y, z, rx, ry, rz, bone? } with named fields.

Gizmo

lua
1exports.ml_propscarry:UseGizmo(itemData, propEntity, positionData, onComplete)
2exports.ml_propscarry:StopGizmo()
3local active = exports.ml_propscarry:IsGizmoActive()
  • UseGizmo(itemData, propEntity, positionData, onComplete): opens the 3D gizmo on an entity, starting from positionData. onComplete runs when the player confirms or cancels
  • StopGizmo(): closes the gizmo
  • IsGizmoActive(): true while the gizmo is open

Weapon Entity

lua
1local entity, hadComponents = exports.ml_propscarry:CreateWeaponEntity(itemConfig)
  • CreateWeaponEntity(itemConfig): Creates a weapon object for a Config.Items entry, with components and tint resolved from inventory metadata. Returns the entity and whether any components/tint were applied

Editor

lua
1exports.ml_propscarry:StartEditing(itemName)
2exports.ml_propscarry:StopEditing()
3local busy = exports.ml_propscarry:IsEditing()
4exports.ml_propscarry:MLAdminOpen()
  • StartEditing(itemName): Opens the position editor on one carried item, the same one /carryedit opens
  • StopEditing(): Closes the editor without saving
  • IsEditing(): true while the player editor or the admin editor is open. Check it before starting an animation or a menu that would fight with it
  • MLAdminOpen(): opens the admin catalog manager, permission checked. Use it to add a button to your own staff menu
  • MLAdminPanel(): label and icon of the catalog manager, for a staff menu that lists admin tools

Client Handlers

Located in open/client.lua.

open/client.lua
1function Handlers.CanShowProp(itemConfig)
2    return true -- return false to prevent spawning
3end
4
5function Handlers.OnPropSpawned(itemName, entity)
6end
7
8function Handlers.OnPropRemoved(itemName)
9end
10
11function Handlers.CanUseEditor(itemName)
12    return true -- return false to block editor
13end
  • CanShowProp: return false to hide the prop, for example while cuffed or on duty
  • OnPropSpawned: Fires after a prop is created and attached to the player.
  • OnPropRemoved: Fires after a prop is deleted.
  • CanUseEditor: return false to block the editor for that item

Server Handlers

Located in open/server.lua.

open/server.lua
1function Handlers.CanSavePosition(src, itemName, position)
2    return true -- return false to block save
3end
4
5function Handlers.OnPositionSaved(src, itemName, position)
6end
7
8function Handlers.IsAdmin(src)
9    return nil -- return true/false to override, nil for default
10end
  • CanSavePosition: Validate position before saving to KVP. Return false to reject.
  • OnPositionSaved: Fires after a position is saved.
  • IsAdmin: Override the admin check used by /carryadmin and the live catalog callbacks. Return nil to keep the default, the admin permissions of your framework read through ml_bridge. On standalone, return true here for your staff, for example with IsPlayerAceAllowed(src, 'ml_propscarry.admin').

Examples

open/client.lua
1function Handlers.CanShowProp(itemConfig)
2    if LocalPlayer.state.isCuffed then
3        return false
4    end
5    return true
6end
your_script/client.lua
1local carried = exports.ml_propscarry:GetCarriedProps()
2for itemName, entity in pairs(carried) do
3    print(itemName .. ' is visible on player')
4end