Setup, permissions and configuration for dd-plugsystem, written alongside the code and shipped with every download.
A plug drop management system for FiveM. Staff build plug packs, loot tables
of items and cash, assign them to specific players and set an interval. The server
then delivers that pack to that player automatically, on that schedule, forever.
Everything runs from a React panel gated behind FiveM ACE permissions.
Requirements
Requirement
Notes
FiveM server (artifact 6683+)
Lua 5.4 enabled
oxmysql
Required. The only hard dependency
ESX, QBCore or Qbox
Auto-detected
ox_inventory or qb-inventory
Auto-detected
ox_lib (optional)
Notifications, the text prompt and the pickup progress bar
ox_target or qb-target (optional)
Third-eye interaction on the package. Auto-detected
Installation
Place the folder in your resources directory:
resources/[custom]/dd-plugsystem
Keep the folder name dd-plugsystem. If you rename it, update the
ensure line to match; nothing else needs changing.
The database is optional. The resource creates its own tables on first start.
To run it yourself instead:
mysql -u root -p your_database < sql/digitalden.plugsystem.sql
Four tables are created: dd_plug_packs, dd_plug_assignments,
dd_plug_drops, dd_plug_logs.
Add it to server.cfg, after your framework, your inventory and oxmysql:
ensure oxmysql
ensure es_extended # or qb-core / qbx_core
ensure ox_inventory # or qb-inventory
ensure dd-plugsystem
Grant yourself access with the ACE lines in the next section, then restart.
This resource uses FiveM ACE permissions only. It does not read your framework's
admin groups, so it behaves identically on ESX, QBCore and Qbox.
The four nodes
Node
Grants
digitalden.plugsystem.view
Open the panel; read packs, assignments, drops and logs.
digitalden.plugsystem.assign
Everything in view, plus create, edit, pause and delete assignments.
digitalden.plugsystem.manage
Everything in assign, plus create, edit and delete packs, force a drop, reload the item list.
digitalden.plugsystem.admin
Everything in manage, plus prune logs and use /plugreload.
The tiers are inclusive. Granting admin alone gives full access, so
there is no need to grant the lower nodes as well.
Grant the nodes to your groups
# Full access
add_ace group.admin digitalden.plugsystem.admin allow
# Can build packs and force drops, cannot prune logs
add_ace group.moderator digitalden.plugsystem.manage allow
# Can only assign packs to players
add_ace group.helper digitalden.plugsystem.assign allow
# Read-only access to the panel
add_ace group.trialstaff digitalden.plugsystem.view allow
Put your staff into those groups
Any identifier FiveM understands works: license:, steam:,
discord:, fivem:, xbl:, live:.
The license identifier is the most reliable. Find a player's license by running
status in the server console while they are connected.
If you are denied in game you get a notification, and the server console prints the exact
node that was required. That line tells you precisely what to add.
Framework and inventory
Everything lives in config/config.lua, which is escrow-exempt and fully editable after purchase.
Auto-detection checks for qbx_core, es_extended then
qb-core, in that order. Set it explicitly if you run more than one side by
side during a migration.
Resource names are configurable too, for a renamed core:
Config.Delivery.mode = 'dropoff' -- 'dropoff' | 'direct'
Config.Delivery.pickupTimeout = 0 -- minutes; 0 = until the next restart
dropoff spawns a package at a drop point, gives the player a blip and GPS route, and they press E to collect it.
direct puts the items straight into their inventory with a notification. Anything they cannot carry is reported back and recorded in the log.
pickupTimeout = 0, the default, means a package stays exactly where it
landed for the whole session. Because a restart despawns every prop, anything still
uncollected then is written off on the next boot and logged as expired. Set a number of
minutes instead if you want packages to time out during the session.
Drop points
Where a package lands is decided in three levels, most specific first:
The assignment's pin, set in the assignment editor. Applies to that one player.
The pack's pin, set in the pack editor. Applies to every drop of that pack.
Config.DropLocations, the shared pool, picked at random and scattered within Config.DropRadius.
Both pins are captured with a Pin here button that copies the coordinates you are
standing on, so you never have to read them off a map. Clear a pin with the bin icon to
fall back to the next level.
Keep the radius small.Config.DropRadius defaults to 12.0,
and a large radius is the usual reason packages end up inside buildings, on roofs or in
walls. A 45 m scatter will leave open ground almost anywhere in the city; 10 to 15 m is
the sweet spot.
To add your own points, stand where you want the drop and run /plugdrophere.
The exact line to paste into Config.DropLocations is printed to the server
console, which is far more reliable than reading coordinates off a map. The client also
probes for real ground at spawn time, so a slightly wrong Z still settles the package on
the surface rather than under it.
One banner per whole minute remaining, starting minutesBefore out and
counting down to the last. Warnings only fire for players who are online, and the banner
hides itself while the panel is open so the two never overlap.
Config.Scheduler = {
tickSeconds = 15, -- how often due assignments are checked
deliverOnLoginDelay = 15, -- seconds after login before a missed order is handed over
catchUpMissed = true, -- skip forward over intervals missed while the server was down
catchUpMaxCycles = 3
}
Set enabled = false to fall back to text placeholders. ox_inventory items
that declare their own client.image are honoured, as are qb-inventory items
with a custom image field.
auto prefers ox_lib, then mythic_notify, then your framework's own. For a
custom system set provider = 'custom' and point
Config.Notify.resource at your event name; it fires as
TriggerEvent(resource, message, kind, title).
textui, the shipped default, draws an on-screen prompt and listens for Config.Interaction.key.
auto uses a third-eye if ox_target or qb-target is running, and falls back to a text prompt.
target forces third-eye. With no target resource running it warns once and falls back, rather than leaving the package uncollectable.
keypress is the bare native prompt with no dependency at all.
The prompt style is set by textui: nui is a built-in prompt in
the panel's style with no dependencies, ox_lib uses ox_lib's own text UI,
native uses the game's help box. Move the built-in one with
promptPosition: top-left, top-center,
left-center or bottom-center.
Nothing is attached to the player; the only prop is the package on the ground. With
ox_lib present the animation and progress bar are driven by its progressBar,
so an interrupted pickup does not consume the drop.
Using the panel
Open with /plugpanel, or bind a key by setting Config.Keybind.enabled = true.
Plug packs
Name, colour and description, an optional cash range, then the loot table. Items come
from a searchable grid of everything on your server shown with its real inventory icon,
so there are no spawn codes to type. Each item gets a minimum, a maximum and a chance.
Every item is rolled independently, and if every roll fails one random entry is
granted at its minimum, so a drop is never empty. Disabling a pack stops deliveries
without deleting its assignments.
Assignments
Pick a pack, a player and an interval from 1 minute to 14 days. Online players are listed
first; typing in the search also queries your framework's player table, so offline
characters can be assigned too.
Max deliveries deactivates the assignment after that many drops. 0 is unlimited.
Pickup location pins this player's drops to where you are standing, overriding the pack's own location.
Deliver first drop now fires once on save, then follows the interval.
How the countdown behaves
next_run is an absolute timestamp, so a countdown survives a restart exactly
where it left off and runs on real time whether or not the player is online. Pausing
banks the time left and resuming hands it straight back. Editing a name, pack or player
never disturbs a running countdown; only changing the interval, or ticking deliver first
drop now, reschedules.
The player has to be there
When a drop fires and the player is on the server, it lands. If not, it is recorded as a
missed order: nothing spawns, and the order stays claimable until their next
scheduled drop. Join inside that window and it is handed over shortly after loading in.
Let the window pass and it is gone.
Nothing ever stacks up. At most one claimable order per assignment exists at a
time, and forcing a drop to an offline player is refused rather than queued.
/mydrops tells the player plainly where they stand.
Forcing a drop restarts that assignment's cycle. The countdown resets to the full
interval from that moment and the delivery counts toward any cap, so a player never gets
a forced drop and a scheduled one back to back.
The scheduler sleeps until whichever assignment is due first rather than polling on a
fixed beat, so a drop lands on its countdown even at 1-minute intervals.
Config.Scheduler.tickSeconds is only the longest it will ever sleep.
Activity and System
Activity is a filterable audit trail of every action and delivery, with actor,
target and relative timestamps. Admins can prune entries older than 30 days.
System is a diagnostics readout: detected framework and inventory, delivery mode,
scheduler tick, pickup window, drop location count, resource version, and the four ACE
nodes with the ones you hold marked.
Commands
Command
Permission
Description
/plugpanel
view
Open the management panel.
/plugcollect
none
Collect the package you are standing on, if the E prompt is not usable.
/mydrops
none
Player tracker: every pack assigned to them, a live countdown, and whether a package is waiting.
/plugdrophere
manage
Prints your position to the console as a ready-to-paste Config.DropLocations line.
/plugdebug
admin
Dumps what the scheduler sees: tick health, every countdown, and why any of them is not firing. Start here if a drop has not landed.
/plugreload
admin
Reload packs, assignments and the item list without restarting.
Command names are configurable via Config.Command.
Exports and logging
Server exports
-- Returns every configured pack
local packs = exports['dd-plugsystem']:GetPacks()
-- Returns every assignment
local assignments = exports['dd-plugsystem']:GetAssignments()
-- Deliver a pack to a player right now, bypassing the schedule
local ok, mode = exports['dd-plugsystem']:GiveDrop('char1:1100001abcdef', 2)
-- Check a player's access level ('view' | 'assign' | 'manage' | 'admin')
local allowed = exports['dd-plugsystem']:HasPlugAccess(source, 'manage')
GiveDrop returns true plus the delivery mode used
(dropoff, direct or queued), or false
plus a reason.
Discord logging
Config.Logs = {
enabled = true,
webhook = 'https://discord.com/api/webhooks/...',
botName = 'DigitalDEN',
avatar = '',
color = 3066993,
events = {
packCreated = true,
dropCollected = true,
permissionDenied = true,
-- set any event to false to stop sending it
}
}
Embeds are queued and flushed on a timer so a burst of activity cannot rate-limit your
webhook. Database logging keeps working with the webhook left empty.
Database
Table
Purpose
dd_plug_packs
Pack definitions. The loot table is stored as JSON in items.
dd_plug_assignments
Who receives which pack, the interval, next run timestamp, delivery counters and expiry. Deleting a pack cascades to its assignments.
dd_plug_drops
Every drop with its rolled contents, state (pending, ready, collected, expired) and timestamps.
dd_plug_logs
The audit trail behind the Activity tab.
All timestamps are Unix epoch seconds.
Troubleshooting
"No supported framework detected"
Your core started after this resource, or it is named something unexpected. Move
ensure dd-plugsystem below your core in server.cfg, or set
Config.Framework and Config.FrameworkResources explicitly.
"No supported inventory detected"
Same cause. Set Config.Inventory to 'ox' or 'qb'
and confirm the resource name in Config.InventoryResources.
The item selector is empty
The item list is cached for five minutes. Press the refresh icon inside the pack editor,
or run /plugreload. On qb-inventory the list comes from
QBCore.Shared.Items, so a broken items file shows up here first.
Item icons show two-letter placeholders
That item has no matching image file in your inventory resource. ox_inventory reads
ox_inventory/web/images/<item>.png, qb-inventory reads
qb-inventory/html/images/<image>. If your images live elsewhere, point
Config.ItemImages at them. The placeholder is cosmetic; the item still works.
/plugpanel says I have no permission
The ACE node is missing. The server console denial line names the exact node required.
ACE changes need a server restart, not a resource restart.
A player never receives their drop
Check the Activity tab. Drop delivered means the server sent it; Order
missed means the player was not on the server when it fired. A missed order is handed
over Config.Scheduler.deliverOnLoginDelay seconds after they next load in,
as long as their next scheduled drop has not already come round.
Items are missing after collecting a package
Their inventory was full. The system reports this to the player and records the failed
items in the log entry, but it does not drop them on the floor.
Still stuck?
Run /plugdebug first, then open a ticket with its output and your console lines from resource start. Support is 11:00 to 23:00 UK, every day.