A practical guide to how to add a mining job to your qbcore fivem server, with implementation decisions, validation steps, and security considerations for a production-minded project.
Overview
This guide shows how to add a robust, secure mining job to a QBCore FiveM server. It focuses on architecture, server-side validation, implementation choices (qb-target / ox_target / ox_lib), database modeling, testing, deployment, and ongoing maintenance. Follow the patterns here to avoid trusting any client input, to keep server authority intact, and to make the job maintainable and scalable.
Architecture and Data Model
Design your mining job with clear server authority and minimal client responsibility. Typical components:
- Client: UI, targeting interactions, animation triggers, local visual effects only.
- Server: authoritative logic for harvest, inventory, rewards, cooldowns, anti-cheat, and persistence.
- Database: persistent player progress, optionally rock respawn schedules, and audit logs.
- Third-party libs: qb-target or ox_target for interactions; ox_lib for menu builders if needed; ghmattimysql or mysql-async for persistence.
Minimal DB schema example:
CREATE TABLE IF NOT EXISTS mining_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
steam HEX(16),
player_id INT,
action VARCHAR(64),
item VARCHAR(64),
amount INT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Implementation Choices and fxmanifest
Decide whether to implement as a standalone resource or integrate into an existing job resource. Example fxmanifest essentials:
fx_version 'cerulean'
game 'gta5'
author 'Your Name'
version '1.0.0'
shared_script 'shared.lua'
server_script 'server.lua'
client_script 'client.lua'
dependencies {
'qb-core'
}
If using qb-target or ox_target, declare them as dependencies or conditionally detect them in runtime to register interaction zones appropriately.
Server-Side Job Flow (Secure)
The server must validate every client request. Do not assume client-sent IDs, coordinates, or durations are genuine. The canonical flow:
- Player triggers the target interaction client-side (no sensitive state changed).
- Client fires a server event like
qb-mining:server:StartMinewith a rock ID. - Server verifies the rock ID is allowed, checks cooldowns/locks, checks inventory capacity, and then sets a server-side lock for that rock or player.
- Server returns an optimistic confirmation to client (e.g., start animation) but does not grant items yet.
- After the expected mining duration, client notifies server with a request to complete (or server times out and resolves itself via server tick).
- Server re-validates and grants items or denies and logs suspicious activity.
Example server-side skeleton:
-- server.lua
local QBCore = exports['qb-core']:GetCoreObject()
local rockLocks = {} -- [rockId] = source or true
RegisterNetEvent('qb-mining:server:StartMine', function(rockId)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
-- Validate rock and availability on server list
if not ValidRock(rockId) or rockLocks[rockId] then
TriggerClientEvent('qb-mining:client:MiningDenied', src, 'Rock unavailable.')
return
end
-- Inventory capacity check
if not Player.Functions.CanCarryItem('stone', 1) then
TriggerClientEvent('qb-mining:client:MiningDenied', src, 'No inventory space.')
return
end
-- Lock and start a server-side timer
rockLocks[rockId] = src
SetTimeout(8000, function()
-- Re-validate and reward
local p = QBCore.Functions.GetPlayer(src)
if p and rockLocks[rockId] == src then
p.Functions.AddItem('stone', 1) -- authoritative
ExportLogMining(p, 'stone', 1)
end
rockLocks[rockId] = nil
end)
end)
Client Integration: Targets and Animations
Client code only triggers UI and local effects. Use qb-target or ox_target to present interaction points. Never let client code directly add items.
-- client.lua (qb-target)
local target = exports['qb-target']
target:AddBoxZone("mining_rock_1", vector3(2856.0, 2790.0, 40.0), 1.0, 1.0, {
name="mining_rock_1",
heading=0,
debugPoly=false,
}, {
options = {
{
type = "client",
event = "qb-mining:client:TryMine",
icon = "fas fa-hammer",
label = "Mine Rock",
rockId = 1
}
},
distance = 2.5
})
RegisterNetEvent('qb-mining:client:TryMine', function(data)
-- Play animation locally
-- request server to start mining
TriggerServerEvent('qb-mining:server:StartMine', data.rockId)
end)
If you detect multiple target systems, prefer a conditional loader and make the resource compatible with both. Do not send coordinates or other sensitive state that the server must trust.
Inventory Items, Shops, and Processing
Create item definitions and processing recipes in your shared or database data. Example items:
- stone (raw)
- ore (raw ore for smelting)
- gem (rare drop)
Processing should be server-side too: when a player uses a furnace, server checks for required inputs, removes them server-side, and adds outputs. If you want a cooldown or labor animation, use server-side timers as shown previously.
Security and Server Validation
Security is paramount. Do not trust any client payload. Key patterns:
- Validate rock IDs against a server-side table. Do not accept arbitrary IDs.
- Check player inventory and weight capacity server-side before granting items.
- Add per-player and per-rock cooldowns and rate limits to prevent farming via rapid events.
- Log suspicious behavior: repeated failed attempts, mismatched client/server timers, or malformed payloads.
- Use QBCore.Functions.GetPlayer(source) and server callbacks for authoritative state.
Example validation checklist
- Is rockId in allowed set?
- Is rock locked by another player?
- Does player have inventory space?
- Has player been rate-limited?
- Has the operation been logged for auditing?
Testing and Debugging
Test thoroughly in a staging server. Steps:
- Enable verbose server logging for the module and run automated simulated actions.
- Test edge cases: full inventory, multiple clients attempting the same rock, network latency simulation.
- Force failure paths: make DB unavailable to confirm graceful degradation and retries.
- Confirm logs capture: player id, steam hex, action, result.
Useful debug snippet for server logging:
local function ExportLogMining(player, item, amount)
print(("MINING LOG - %s (%s): %s x%d"):format(player.PlayerData.citizenid, player.PlayerData.steam, item, amount))
-- optionally persist to DB here
end
Deployment and Maintenance
Deployment checklist and maintenance considerations are about safe rollouts and migrations. Use your resource manifest versioning, back up player inventories and DB tables, and validate migration scripts before applying on production. If you need orchestration or documentation for staff, integrate the resource with your admin tools.
For quick ops and team access tools, consider pairing that documentation with team platforms such as the Stellar AI project manager: https://trystellarai.com/app. When coordinating deploy windows and changelogs, keep a copy of DB migration scripts checked into version control and maintain a rollback plan. For planning features and tracking issues, you can centralize task boards using: https://trystellarai.com/app.
Performance and Scaling
Mines are event-driven but can create spikes if many players mine simultaneously. Mitigation:
- Batch DB writes for logs using a worker or buffer with a reasonable flush interval.
- Keep rock state in memory and persist only essential changes or periodic snapshots.
- Rate limit per-player mining attempts and global concurrent mines.
Roblox / Luau Considerations (Brief)
If you are implementing a similar mining job in Roblox, server authority patterns are similar: RemoteEvents are used for client->server requests, but the server must validate everything. On the Roblox server:
- Validate player permissions, item capacity, and object availability server-side.
- Use DataStore with careful failure handling: implement exponential backoff, limit retries, and consider a local cache to avoid repeated calls on short failures.
- Handle DataStore rate limits by batching transforms and persisting only changes or snapshots at safe intervals.
Always treat RemoteEvents as untrusted input and check all fields before applying changes to a DataStore or updating a player's inventory.
Practical Deployment Checklist
| Step | Action | Completed |
|---|---|---|
| 1 | Create resource skeleton and fxmanifest | ☐ |
| 2 | Implement server validation and item grants | ☐ |
| 3 | Register items in shared item list | ☐ |
| 4 | Set up target interactions (qb-target / ox_target) | ☐ |
| 5 | Write DB migration / logs table | ☐ |
| 6 | Staging tests: concurrency, inventory edge cases | ☐ |
| 7 | Production rollout with monitoring and rollback plan | ☐ |
Long-Term Maintenance
Maintain changelogs, add automated alerting for suspicious activity, and rotate audit logs. Periodically review spawn rates and economy impact of the mining job. If you add new items or processing, add DB migrations and test them on staging first.
Further Reading and Blog
For deeper operational or design perspectives, see the developer blog post collection: https://trystellarai.com/blog.