Categories
A category is a named group of contracts that rotates on its own timer. A contract is never shown to players on its own: it appears only when a category lists it and that category’s rotation picks it. Categories are how you pace content (hourly, daily, weekly, whole-wipe) and decide how many contracts a player can juggle at once.
Categories are stored in the contract_category_data.json data file and authored with the web
editor.
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
name |
string (non-empty) | The category name players see, e.g. “Daily Contracts”. |
description |
string | A short blurb for the category. May be empty. |
contractIds |
string[] | The ids of contracts in this category’s pool. Each rotation draws from this list. |
permanentContractIds |
string[] | The ids of contracts that are always active in this category, on top of the random draw (see below). |
rotation |
object | The rotation timer and how many contracts go active each cycle (see below). |
schedule |
object or null |
When the category is open, counted from the map wipe. null means always open (see below). |
replayCooldown |
number (0 or more) or null |
Default wait before a contract in this category can be taken again, in seconds. null means contracts here are not replayable unless they set their own. |
ignoreContractCooldown |
boolean | When on, contract-level replay cooldowns are ignored and this category’s replayCooldown decides alone. |
maxActiveContracts |
map (permission → number) | Per-permission cap on how many of this category’s contracts a player can hold at once (see below). |
Rotation
Section titled “Rotation”The rotation block decides when the category re-rolls and how many contracts it surfaces:
| Field | Type | Default | Description |
|---|---|---|---|
duration |
number (-1 or > 0) | 86400 |
How long a rotation lasts, in seconds. 86400 is one day. Set to -1 (or 0) for no rotation: the contracts stay active for the whole wipe. |
minContractsAmount |
integer (min 1) | 3 |
Fewest contracts to activate each rotation. |
maxContractsAmount |
integer (min 1) | 3 |
Most contracts to activate each rotation. Cannot be lower than minContractsAmount. |
Each rotation picks a random number of contracts between minContractsAmount and maxContractsAmount
(inclusive), shuffles the pool, and activates that many. If the pool holds fewer contracts than the chosen
number, every contract in the pool goes active. Set min equal to max for a fixed count every time.
These two fields work with the contract’s own replayCooldown. See Replayable
contracts for the full rule and what players see.
Endless categories
Section titled “Endless categories”A category with duration of -1 never rotates: its contracts are active for the entire wipe. This is
how the default Wipe Contracts category works.
Dormant categories
Section titled “Dormant categories”A category with no active rotation and nothing to draw from either list (or only ids that don’t match any contract) becomes dormant: it doesn’t start a new rotation and logs a console warning instead. It stays visible in the menu with no active contracts, and starts a rotation automatically the next time the plugin loads with a contract in its list. This avoids confusion when you add a category with no contracts, realize it, fix it by adding at least one contract to it and then wonder why it still doesn’t have any active contracts (because a rotation had started with no contracts).
Permanent contracts
Section titled “Permanent contracts”permanentContractIds is a second contract list. Every contract in it is always part of the category’s
active rotation. They’re included on top of the random draw and do not count toward minContractsAmount or
maxContractsAmount, so a category with 3 permanent contracts and a max of 5 can show 8 at once.
Keep a contract in one of the two lists. contractIds is the random pool, permanentContractIds is the
always-on list. If the same id is in both, the plugin logs a warning on load and treats the contract as
permanent, and the editor flags it too.
Permanent means always in the rotation, not persistent progress. Progress still resets when the category rotates, same as every other contract.
Schedule
Section titled “Schedule”Unlike rotation, which says how long a cycle lasts, schedule says when the category runs at all.
A category with a schedule is open when the current time falls inside one of its entries, and closed otherwise. A closed category stops rotating and ends any active rotation, as if the rotation had ended on its own.
Schedules are useful when you want harder contracts to show up at a specific point in the wipe. They are an advanced feature and are not used in the default setup.
The schedule block holds the entries and one display setting:
| Field | Type | Default | Description |
|---|---|---|---|
entries |
array | The time ranges when the category is open. At least one. | |
hideWhenClosed |
boolean | true |
Hide the category from the player menu while it is closed. |
Each entry has three fields:
| Field | Type | Description |
|---|---|---|
start |
integer (min 0) | When the category opens, in seconds after the wipe. |
end |
integer or null |
When it closes, in seconds after the wipe. null keeps it open for the rest of the wipe. Must be after start. |
repeatEvery |
integer or null |
How long until the entry happens again, in seconds. null means it happens once. |
The category is open when any entry covers the current time. Two entries that overlap work the same way a single entry would.
schedule: null means “always open”, which is what all default categories use. An empty entries list is rejected and treats the schedule as null.
Showing a closed category
Section titled “Showing a closed category”By default a closed category is gone from the menu. Set hideWhenClosed inside the schedule to false to keep it listed with its
name and a countdown to its next opening. It shows no contracts either way, so players never see what is
coming. Once the schedule has no opening left, the category hides no matter what the flag says.
Examples
Section titled “Examples”An easy starter category that stops at the end of week 2:
{ "entries": [ { "start": 0, "end": 1209600, "repeatEvery": null } ], "hideWhenClosed": true}A hard category that only shows up from week 2 onward and then stays for the rest of the wipe:
{ "entries": [ { "start": 1209600, "end": null, "repeatEvery": null } ], "hideWhenClosed": true}A weekend category, on a server that wipes Thursday at 2pm. Friday 6pm is 28 hours after the wipe
(100800), Sunday midnight is 82 hours after it (295200), and a week is 604800:
{ "entries": [ { "start": 100800, "end": 295200, "repeatEvery": 604800 } ], "hideWhenClosed": false}That last one sets hideWhenClosed to false, so players see the category all week with a countdown to
Friday instead of it vanishing on Sunday night.
The web editor does that math for you, so you do not have to count hours by hand.
Per-player cap
Section titled “Per-player cap”maxActiveContracts limits how many of this category’s contracts a single player can have active at the same
time, keyed by permission. On the lite edition this cap is not enforced: every player is uncapped at the
category level (the global cap still binds). Each key is a permission name (for example contracts.vip) and each value is the
cap for players who hold it:
- A player gets the highest cap among the permissions they hold.
- A value of
-1means unlimited. - If the map is empty, or a player holds none of the listed permissions, they are uncapped for this category.
{ "categories": { "daily": { "name": "Daily Contracts", "description": "Achievable mid-effort contracts.", "contractIds": [ "hunt_bears", "craft_ammo", "loot_crates" ], "rotation": { "duration": 86400, "minContractsAmount": 3, "maxContractsAmount": 5 }, "schedule": null, "replayCooldown": null, "ignoreContractCooldown": false, "maxActiveContracts": { "contracts.use": 2, "contracts.vip": 5 } } }, "version": { "Major": 1, "Minor": 14, "Patch": 0 }}This category rotates daily, surfaces three to five of its contracts each day, lets ordinary players hold two at once, and VIPs five.
