Custom jobs
Create, track, and delete trucking jobs at runtime from other resources.
Use this when another resource should add a job card without you editing Config.Loads (a business script posting a one-off haul, a fuel import, an event). Permanent jobs belong in Config.Loads. The table shape is the same as Jobs & loads.
Jobs are inserted into gs_trucking_custom_jobs and loaded again on resource start. Without MySQL they exist in memory only and vanish on restart.
Create a job
Server only. Returns id, err. On failure id is nil and err is a string (name is required, load.model is required, …).
local id, err = exports['gs-trucking']:CreateJob({
name = 'Port Run',
description = 'Haul a flatbed from the docks.',
icon = 'fas fa-box',
requiredReputation = 0,
cooldown = 10,
illegal = false,
highlight = true,
load = {
model = { `prop_boxpile_08a` },
coords = {
vector4(1223.21, -3125.05, 4.99, 89.16),
},
modelDist = 2.5,
},
trailer = {
model = `trflat`,
exactPos = true,
loadCoords = vector4(1245.32, -3135.93, 5.98, 270.0),
coords = {
vector4(965.55, -3210.14, 5.9, 176.36),
},
dummy = { x = 0.0, y = -7.0, z = -1.2, rx = 0.0, ry = 0.0, rz = 90.0 },
attachpoints = {
{ x = 0.0, y = 4.0, z = 0.39, rx = 0.0, ry = 0.0, rz = 0.0 },
},
},
dropoff = {
{ coords = vector4(1121.46, -1461.07, 34.69, 268.21), payout = 2500 },
},
}, {
event = 'myresource:truckingUpdate', -- optional. stored and fired on every phase
})
if not id then
print('CreateJob failed', err)
endRequired: name, load.model, load.coords, trailer.model, trailer.coords, and dropoff as a list of { coords, payout }. Hose, loader, illegal, and the rest of Config.Loads are optional. Missing icon becomes fas fa-truck.
opts.event (alias opts.callbackEvent) is a server event name in your resource. It is saved in the database with the job so it still fires after a restart.
IDs start at 100000 so they never collide with bundled Config.Loads indexes.
Players who pass HasPerm() get a phone / notify (custom_job_available) when the job is created. Anyone with the trucking app open gets the new card on the next refresh.
Listen for progress
Every custom job also fires the global server event gs-trucking:customJob with the same arguments, so you can listen without passing opts.event.
AddEventHandler('myresource:truckingUpdate', function(jobId, phase, data)
-- data.id, data.phase, data.name
-- data.source, data.identifier when a player is involved
-- data.dropoff on enroute / finished (index into the job dropoff list)
-- data.payout on finished
-- phase:
-- started player accepted
-- trailer_ready trailer spawned
-- trailer_attached hitched
-- at_load trailer on the bay
-- loading_done cargo complete, waiting on dropoff
-- enroute dropoff assigned (illegal dispatch already fired)
-- finished delivered and paid
-- cancelled player cancel, timeout, destroyed trailer, or DeleteJob while active
if phase == 'finished' then
print(data.source, 'delivered', data.name, data.payout)
elseif phase == 'cancelled' then
print(data.source, 'abandoned', jobId)
end
end)These phases only fire for custom jobs (CreateJob), not for rows you typed into Config.Loads.
Other exports
local job = exports['gs-trucking']:GetJob(id)
-- nil if this id is not a custom job
-- job.definition = the Config.Loads-shaped table
-- job.creator = resource that called CreateJob
-- job.callbackEvent = opts.event or nil
-- job.busy, job.player, job.cooldown, job.started = live slot
local list = exports['gs-trucking']:GetCustomJobs() -- array of the same snapshots
-- Cancels anyone currently on it (UnFinished reputation), removes the job card, deletes the DB row
local ok, err = exports['gs-trucking']:DeleteJob(id)Client checks
These cover any job, bundled or custom:
if exports['gs-trucking']:IsJobActive() then
-- returns the job id (number). falsy when idle
end
local info = exports['gs-trucking']:JobInfo()
-- nil when idle
-- info.name, info.description
-- info.departure = load bay / cargo spawn (vector3)
-- info.destination = nil until dropoff is assigned, then that vector3