Home › Documentation › 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)custom/client/hooks.luaFired by the core on the client side. Add your logic inside each handler.
graffiti:hook:onTagAddedFired 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:onTagRemovedFired 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:onCanvasModeChangedFired 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)custom/server/hooks.luaFired by the core on the server side. Add your logic inside each handler.
graffiti:hook:onTagCreatedFired 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:onTagDeletedFired 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)custom/client/permissions.luaCalled 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) → booleanCalled 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) → booleanCalled 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) → booleanCalled when the player presses E near a shop point, before opening the NUI.
function GraffitiPerms.canShop(coords)
-- coords — vector3 shop location coordinates
return true
endcustom/server/permissions.luaAuthoritative checks called server-side before each action. These actually block the operation.
GraffitiPerms.isAdmin(src) → booleanCalled on all admin callbacks and commands. Validates against Config.AdminGroups using three fallback layers:
IsPlayerAceAllowed (QBCore, QBX, any ace-based framework)xPlayer.getGroup()Framework.GetIsFrameworkAdminfunction GraffitiPerms.isAdmin(src)
-- src — server ID
return true -- or your custom check
endGraffitiPerms.canPaint(src, coords) → booleanCalled 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) → booleanCalled 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) → booleanCalled 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