API Reference
Server exports for oxide-pestcontroljob — IsOnShift, GetSetting, SetSetting, GetInfo, GetShift, GetProgression and SetUpdateHandler, with integration examples.
Reference for developers integrating another resource with oxide-pestcontroljob. Everything here is a server-side export; there are no client exports. If you just run the resource as a server owner, you don't need anything here.
Server Exports
All exports are called on the server.
IsOnShift
Tells you whether a player is currently on a pest control shift. Use it to stop a player from starting another job while they're already working this one.
local onShift = exports['oxide-pestcontroljob']:IsOnShift(source)Parameters:
| Name | Type | Description |
|---|---|---|
source | number | The player's server id |
Returns: boolean: true from the moment the player clocks in until the shift ends, whether they clock out, log out, disconnect, or the shift is ended for them (going down, too many missed calls, the complaint limit, a destroyed van). false otherwise, including for an id that isn't a player.
A player who joined someone else's shift through a crew invite counts as on shift too.
GetSetting
Reads one of the resource's database-backed settings, keyed by the setting name without the Config. prefix (see Configuration for the names).
local maxSlots = exports['oxide-pestcontroljob']:GetSetting('MaxSlotsPerLocation')
local unlocks = exports['oxide-pestcontroljob']:GetSetting('Unlocks') -- tableParameters:
| Name | Type | Description |
|---|---|---|
key | string | The setting name, for example 'Payment' or 'usePhone' |
Returns: any: the current value, as the server uses it now. nil for a name that isn't a setting.
SetSetting
Changes one of the resource's settings.
local ok = exports['oxide-pestcontroljob']:SetSetting('uniformsEnabled', true)Parameters:
| Name | Type | Description |
|---|---|---|
key | string | The setting name, without Config. |
value | any | The new value. For a table setting, pass the whole table |
Returns: boolean: true when the change was applied. The change is live: the server uses it right away, it saves to the database, and it syncs to players' games (except Properties, which stays on the server). It persists across restarts. Returns false if:
- the resource hasn't finished loading its settings yet
- the value contains a number that isn't finite
- the value is for
PropertiesorCrewand fails the same checks the settings editor uses - the database write fails (the old value stays)
Other settings are not checked against the editor's limits, so pass values of the same shape as the factory default.
GetInfo
The depots, whether each has room for another technician, and the base pay. Read-only.
local info = exports['oxide-pestcontroljob']:GetInfo()Returns: table:
| Field | Type | Description |
|---|---|---|
locations | table[] | One entry per depot: name (string, translated), coords (vector3, the clerk's spot) and open (boolean, false while it has as many technicians as MaxSlotsPerLocation allows) |
basePay | table | min and max: the lowest and highest base pay per call in Config.Payment.basePay, before the property multiplier and bonuses |
crew | boolean | Whether players can work as a crew of two (Config.Crew.maxSize 2) |
GetShift
A player's shift as the job holds it now. Read-only.
local shift = exports['oxide-pestcontroljob']:GetShift(source)Parameters:
| Name | Type | Description |
|---|---|---|
source | number | The player's server id |
Returns: table|false: false when the player is not on a shift; otherwise:
| Field | Type | Description |
|---|---|---|
location | string or nil | The depot the shift started at (translated name); nil if that depot has since been removed |
role | string | 'solo', 'leader' (leads a crew) or 'partner' (in someone else's crew) |
stage | string | 'waiting' for a call, 'offer' while a call waits to be accepted, 'working' on an accepted call |
task | string or nil | The name of the property for the call on offer or in progress; nil otherwise |
done, total | number | Always 0, 0 (calls come one at a time) |
jobs | number | Calls reported this shift so far |
earned | number | Pay earned so far: the calls' totals plus the co-op bonus, before the gas fee, tax, van damage and treatment charges taken at clock-out |
GetProgression
A player's level with this job. Read-only: a character who has never worked the job reads as level 1, and no record is created.
local progression = exports['oxide-pestcontroljob']:GetProgression(source)Parameters:
| Name | Type | Description |
|---|---|---|
source | number | The player's server id |
Returns: table|false: false when the player has no character; otherwise:
| Field | Type | Description |
|---|---|---|
level | number | Current level |
title | string | The level's name, translated (for example "Senior Technician") |
count | number | Calls reported in total |
streak | number | Daily streak, in days |
prevAt | number | The call count where the current level starts |
nextAt | number or nil | The call count where the next level begins; nil at the top level |
nextTitle | string or nil | The next level's name; nil at the top level |
timedOut | boolean | Suspended after too many complaints |
timeoutRemaining | number | Seconds left on the suspension (0 when not suspended) |
SetUpdateHandler
Lets another resource, such as a phone app, show this job's shift updates itself. While a handler is set and its resource runs, each update goes to it first. When the handler returns true, the job sends nothing else; when it returns anything else or errors, the job sends the update as before (text, mail or phone notification). One handler at a time: setting one replaces the last.
Updates are only produced while the job's usePhone setting is on. They are:
- each phone message from the depot: the welcome, a destroyed van, a cancelled shift, customer callbacks and the pay summary
- each incoming call (title "Incoming Call", message such as "Pest complaint at Diner, La Mesa" or "Wasp nest at Forum Drive (Strawberry)")
exports['oxide-pestcontroljob']:SetUpdateHandler(function(source, update)
-- update.title: the sender, for example "Bugstars - Bugstars Pest Control", or "Incoming Call"
-- update.message: the message
-- update.summary: { jobs = number, net = number } on the pay summary, sent when a shift ends
-- (on a clock-out only with Config.shiftSummary = 'text'; never on a disconnect or logout)
return true -- shown; the job sends nothing else
end)Parameters:
| Name | Type | Description |
|---|---|---|
handler | function or nil | handler(source, update). nil (or false) removes the handler your resource set |
Returns: boolean: true, or false when you tried to remove a handler another resource set (it stays). A handler is also removed automatically when the resource that set it stops.
Integration Examples
Stop a player from working two jobs at once
Check before your own job lets the player clock in:
RegisterNetEvent('my-job:server:clockIn', function()
local src = source
if GetResourceState('oxide-pestcontroljob') == 'started'
and exports['oxide-pestcontroljob']:IsOnShift(src) then
-- tell the player to finish their pest control shift first
return
end
-- start your job
end)Check every gig job at once
The same IsOnShift(source) export exists on oxide-postaljob, oxide-pizzajob, oxide-landscapingjob, oxide-roadsideassistancejob, oxide-animalcontroljob, oxide-newspaperjob and oxide-pestcontroljob. Skip any you don't run:
local GIG_JOBS = {
'oxide-postaljob',
'oxide-pizzajob',
'oxide-landscapingjob',
'oxide-roadsideassistancejob',
'oxide-animalcontroljob',
'oxide-newspaperjob',
'oxide-pestcontroljob',
}
local function isOnGigShift(src)
for _, resource in ipairs(GIG_JOBS) do
if GetResourceState(resource) == 'started' and exports[resource]:IsOnShift(src) then
return true
end
end
return false
endShow a player's current call
local function describeShift(src)
if GetResourceState('oxide-pestcontroljob') ~= 'started' then return nil end
local shift = exports['oxide-pestcontroljob']:GetShift(src)
if not shift then return 'Not on a pest control shift' end
if shift.stage == 'working' then
return ('Working a call at %s, $%d earned so far'):format(shift.task, shift.earned)
end
return ('Waiting for a call at %s'):format(shift.location or 'the depot')
endGuard your calls. Check GetResourceState('oxide-pestcontroljob') == 'started' before calling an export so your resource still works when this job isn't installed.
Next Steps
Admin & Developer Tools
Commands, permissions, the in-world property editor, the menu camera, testing every stage and progression SQL for oxide-pestcontroljob.
Troubleshooting Guide
Common oxide-pestcontroljob issues and fixes — install, shifts, dispatch, the van, the inspection, treatments, wasp nests, reports and callbacks, crews, pay, phone and screen, and settings.