Custom · origen_graffiti
The custom/ folder lets you extend origen_graffiti without touching the core. All files here are escrow_ignore — they survive updates.
custom/
├── client/
│ ├── hooks.lua — client-side event hooks
│ └── permissions.lua — client-side permission checks (UX feedback)
└── server/
├── hooks.lua — server-side event hooks
└── permissions.lua — server-side permission checks (authoritative)Client Hooks — custom/client/hooks.lua
Fired by the core on the client side. Add your logic inside each handler.
graffiti:hook:onTagAdded
Fired when the client receives a new tag to render — on save or zone sync.
AddEventHandler('graffiti:hook:onTagAdded', function(row)
-- row.id — DB tag ID
-- row.coords — vector3 center
-- row.created_by — license identifier
-- row.image_url — public image URL
-- row.created_at — UTC timestamp string
end)graffiti:hook:onTagRemoved
Fired when the client removes a tag from the local world.
AddEventHandler('graffiti:hook:onTagRemoved', function(id, coords)
-- id — tag DB ID
-- coords — vector3 where the tag was
end)graffiti:hook:onCanvasModeChanged
Fired when the local player enters or exits canvas mode (active painting session). Useful to hide/show HUD, radial menus, etc.
AddEventHandler('graffiti:hook:onCanvasModeChanged', function(active, context)
-- active — boolean (true = enter canvas, false = exit canvas)
-- context.state — current state string
-- context.sessionId
-- context.isOwner — boolean
end)Server Hooks — custom/server/hooks.lua
Fired by the core on the server side. Add your logic inside each handler.
graffiti:hook:onTagCreated
Fired when a player successfully saves a graffiti.
AddEventHandler('graffiti:hook:onTagCreated', function(src, data)
-- src — server ID of the player
-- data.id — DB tag ID
-- data.coords — vector3 center
-- data.created_by — license identifier
-- data.image_url — public image URL (Fivemanage)
-- data.created_at — UTC timestamp string
end)Example — log graffiti creation:
AddEventHandler('graffiti:hook:onTagCreated', function(src, data)
print(('[graffiti] Player %s painted tag #%d at %s'):format(
data.created_by, data.id, tostring(data.coords)
))
end)graffiti:hook:onTagDeleted
Fired when a tag is deleted — by an admin or by a player using an eraser.
AddEventHandler('graffiti:hook:onTagDeleted', function(src, id, coords)
-- src — server ID of the player
-- id — deleted tag DB ID
-- coords — vector3 where the tag was
end)Client Permissions — custom/client/permissions.lua
Called by the core before each action for immediate UX feedback. Returning false cancels the action client-side.
Client checks are for UX only — they can be bypassed. The server permissions are always the authoritative gate.
GraffitiPerms.canPaint(coords) → boolean
Called at the start of activateGraffiti(), before any async operation.
function GraffitiPerms.canPaint(coords)
-- coords — vector3 player position when activating the spray
return true
endGraffitiPerms.canErase(coords) → boolean
Called just before entering erase mode, after the target tag is identified.
function GraffitiPerms.canErase(coords)
-- coords — vector3 center of the tag to erase
return true
endGraffitiPerms.canShop(coords) → boolean
Called when the player presses E near a shop point, before opening the NUI.
function GraffitiPerms.canShop(coords)
-- coords — vector3 shop location coordinates
return true
endServer Permissions — custom/server/permissions.lua
Authoritative checks called server-side before each action. These actually block the operation.
GraffitiPerms.isAdmin(src) → boolean
Called on all admin callbacks and commands. Validates against Config.AdminGroups using three fallback layers:
- FiveM Ace Permissions —
IsPlayerAceAllowed(QBCore, QBX, any ace-based framework) - ESX group —
xPlayer.getGroup() - Bridge fallback —
Framework.GetIsFrameworkAdmin
function GraffitiPerms.isAdmin(src)
-- src — server ID
return true -- or your custom check
endGraffitiPerms.canPaint(src, coords) → boolean
Called in graffiti:server:startSession after distance validation.
function GraffitiPerms.canPaint(src, coords)
-- src — server ID
-- coords — vector3 canvas center
return true
endGraffitiPerms.canErase(src, coords) → boolean
Called in graffiti:server:deleteWorldTag after reading coords from DB.
function GraffitiPerms.canErase(src, coords)
-- src — server ID
-- coords — vector3 tag coordinates
return true
endGraffitiPerms.canShop(src, coords) → boolean
Called in graffiti:server:shopBuy before checking player funds.
function GraffitiPerms.canShop(src, coords)
-- src — server ID
-- coords — vector3 player position at purchase time (may be nil)
return true
end