Configuration — ns-illegal
Editable under escrow: config.lua, data/*.lua (server-only content), locales/*.lua, bridge/*.lua.
The configuration is validated on every start (/illegal validate re-runs it); errors name the exact key and keep
the system disabled (err_not_ready) until fixed.
config.lua
| Option | Default | Description |
|---|---|---|
Framework / Locale / FallbackLocale / Debug | 'auto' / 'en' / 'en' / false | Standard ns-core options (DebugCategories filters debug output, e.g. { career = true }) |
UseOxLib / UseOxTarget / Inventory | true / true / 'auto' | ox_lib UI, ox_target zones, inventory bridge ('ox', 'framework') |
Features.* | all true | Directory, Missions, Locations, Contracts, VehicleTheft, HouseRobbery, Dismantle, Cultivation, Trade, ArrestPenalty |
Database.autoMigrate | true | Create / update tables on start |
Security.* / Admin.* | see file | RPC flood bucket, rate limit, kick threshold; ACE ns-illegal.admin, useFrameworkAdmin, command = 'illegal' |
Editor.range / interiorRange | 15.0 / 60.0 | Location editor: a saved position must be this close to the admin; interior points belong to the interior whose entry is within interiorRange |
Editor.viewDistance / maxGhosts | 150.0 / 12 | World view: markers within this distance, ghost peds / vehicles at the nearest points |
Editor.previewVehicle / previewPed | 'sultan' / 'g_m_y_mexgoon_01' | Ghost models (contacts with a ped show their own) |
Tablet.item | 'dark_tablet' | The tablet item |
Tablet.registerUsable | true | Register the item through the framework (set false when using the ox_inventory server.export) |
Tablet.requiredForActivities | true | Starting activities / trading also requires carrying the tablet |
Tablet.openCooldownMs / watchIntervalMs | 1500 / 4000 | Open rate limit; item re-check interval while open |
Tablet.directoryPageSize / recentEvents | 8 / 8 | Dashboard sizes |
Tablet.username | min 3, max 16, pattern, allowChange = false, changeCooldownDays = 30, reserved = {...} | Username policy (the pattern is a Lua pattern; uniqueness is case-insensitive) |
Progression.maxLevel / thresholds | 30 / 30 values | thresholds[L] = total XP for level L (strictly increasing). Level 0 = 0 XP |
Progression.maxXp | 2000000000 | XP keeps counting after level 30, capped here |
Progression.cycleOffsetHours | 0 | Daily / weekly objectives and daily caps reset at 00:00 UTC + this (Turkey: 3) |
Tiers | entry 0, operator 10, distributor 20, kingpin 30 | Milestones (id → locale tier_<id> / tier_<id>_desc) |
Reputation.start / min / max | 0 / -100 / 100 | Reputation range |
Reputation.ranks | Snitch … Known Figure | { min, max, key }, sorted, no gaps / overlaps, must cover min..max (key → locale rank_<key>) |
Arrest.penalty / repeatWindowHours / repeatExtraPenalty / maxPenalty | 10 / 24 / 5 / 25 | Penalty = min(max, penalty + extra × earlier arrests in the window) |
Arrest.dedupeSeconds | 120 | Second report for the same character inside this window is ignored |
Arrest.requireOfficerJob | true | A passed officer id must have a Police.jobs job |
Arrest.allowedResources | policejob, qbx_police, qb-policejob, esx_policejob | Resources allowed to call ReportArrest |
Police.jobs | police, sheriff | Used for minPolice and officer checks |
Economy.payout | { method = 'cash' } | 'cash', 'bank', 'item' (+ item = 'black_money') or 'custom' (Economy.custom(src, amount, reason) -> bool) |
ItemCategories | valuables, vehicle_parts, drugs, rare | Buyers only accept items of their categories |
Hud.enabled / durationMs / levelUpDurationMs / sound / reducedMotion | true / 6500 / 4500 / true / false | Progress HUD |
Contracts.maxActive / defaultHours / maxHours / maxRequired | 6 / 24 / 168 / 50 | Anonymous contract limits |
Contracts.limits | xp 5000, rep 20, money 250000, itemCount 25 | Max reward an admin can set |
Contracts.showWinnerName | true | Players see the winner's username |
Contracts.deliveryItems | {} | Allowed delivery items (empty = any item of ItemCategories) |
Contracts.dropPoints | 2 examples | { id, label, coords = vector3, radius } hand-in points for delivery contracts |
ClientHooks.policeAlert(kind, coords) | no-op | Client: send a dispatch alert with your dispatch resource |
ClientHooks.giveVehicleKeys(vehicle, plate) | no-op | Client: give keys with your key resource |
World.pedSpawnDistance / tabletProp / tabletAnim | 50.0 / prop / anim | Contact peds and the tablet animation |
Integrations.carthief | 'auto' | 'auto': while ns-carthief runs, theft contract vehicles are opened with its lockpick + hotwire (the built-in break-in is off); false: never |
Integrations.houserobbery | 'auto' | 'auto': while ns-houserobbery runs, house contacts open its contract offers (the built-in robbery is off); false: never |
Integrations.activityReporters | ns-carthief, ns-houserobbery, ns-storerobbery | [resource] = { activity ids } allowed to call ReportActivity (defaults: ['ns-carthief'] = { 'vehicle_hotwire' }, ['ns-houserobbery'] = { 'house_robbery', 'house_safe' }, ['ns-storerobbery'] = { 'store_robbery', 'store_safe' }) |
data/activities.lua — rewards and gameplay
Config.Activities[id] = { minLevel, minRep?, xp, rep, failRep, cooldown, dailyCap, minPolice, tierMultiplier? } is
the only source of XP / reputation per completion. cooldown (seconds) and dailyCap are per character and survive relogs:
a completion inside them is recorded but awards nothing. failRep is applied only when a started activity runs
out of time. tierMultiplier = { [label] = number 0..2 } scales the XP of completions reported with a tier label
(house_robbery: low 1.0 / mid 1.5 / high 2.0; vehicle_hotwire: easy 0.75 … expert 2.0; vehicle_theft:
the ns-carthief vehicle tier, easy 0.8 … expert 1.6). Unknown label = 1; the final multiplier is capped at 2.
Activities reported by the NullSect resources: vehicle_hotwire (a street theft with ns-carthief: 40 XP, cooldown
120 s, 20 per day — over the cap the theft still happens, it just gives no XP) and house_safe (the member who
cracked a safe in an ns-houserobbery contract: 60 XP, 12 per day). ns-houserobbery contracts count as house_robbery,
so missions that list house_robbery keep counting. ns-storerobbery reports store_robbery (every robber who got
cash: 110 XP × store tier low 1.0 / mid 1.4 / high 1.8, cooldown 600 s, 10 per day) and store_safe (the one who
emptied the safe: 70 XP, 10 per day).
Gameplay blocks: Config.VehicleTheft (models, spots, drop-offs, payout, lockpick), Config.Dismantle (parts),
Config.HouseRobbery (interiors, houses, loot, cash, buckets), Config.Cultivation (seed, yield, grow time).
Config.VehicleTheft.locksmithSpots ('add'): also use the vehicle spots recorded in ns-locksmith (/lslocations,
its data/locations.json) as parking spots; 'replace' = only those while the file has any, false = never.
spotClearRadius (3.0): a parking spot is skipped while a vehicle or a player is this close to it.
Every position in these files is a default: /illegal locations moves, adds and switches points off in game and
stores that in ns_illegal_points. Empty spots / dropoffs / houses lists are allowed (a start warning) when
you add them in game.
Location editor (/illegal locations)
Admins only (same permission as /illegal). The panel lists every point on a map and a list:
| Kind | Comes from | In game |
|---|---|---|
| Contact | data/locations.lua coords + radius | move, radius |
| Parking spot | VehicleTheft.spots + ns-locksmith vehicle spots | add, move, switch off, delete (added) |
| Vehicle drop-off | VehicleTheft.dropoffs | add, move, switch off, delete (added) |
| House door | HouseRobbery.houses (door + interior) | add (pick the interior), move, interior, switch off, delete (added) |
| Contract drop point | Contracts.dropPoints | add (name), move, name, radius, switch off, delete (added) |
| Interior entry / exit | HouseRobbery.interiors[name].entry / exit | move (inside that interior) |
| Search spot | HouseRobbery.interiors[name].spots | add, move (inside the interior), switch off, delete (added) |
Placing: pick a kind (or Move a point), walk / drive to the spot, a live preview follows you, E saves,
Backspace cancels. The direction you face (the car's heading on parking spots) is the point's heading. The
server only accepts a position within Editor.range of the admin. Reset removes the in-game change of a configured
point. A drop point an open delivery contract uses cannot be switched off or deleted. Running jobs keep the positions
they started with. Every change is written to ns_illegal_audit (point_add, point_move, …). World view draws
markers, labels and ghosts for nearby points; Map blips shows every point on the pause map.
Add an activity module
- Add
my_activity = { minLevel = 10, xp = 100, rep = 2, cooldown = 300, dailyCap = 10 }and the locale keyact_my_activity(EN + TR). - Validate the gameplay server-side (state, distance, timing, items) and then call
Career.RecordActivity(src, 'my_activity', { uid = <server-generated unique id>, units = n, money = paid }). Never take an amount from the client. Objectives and contracts update automatically. - From another resource use the export instead (see below) and allow it in
Config.Integrations.activityReporters.
data/locations.lua — hidden contacts
{
id = 'my_buyer', tier = 2, codename = 'loc_my_buyer', hint = 'loc_my_buyer_hint', -- locale keys or text
minLevel = 12, minRep = 20, price = 9000, currency = 'cash',
coords = vector4(0.0, 0.0, 0.0, 0.0), radius = 2.0, ped = 's_m_y_dealer_01',
hours = { from = 20, to = 4 }, -- optional
permanentAccess = false, -- true: reputation is not re-checked after buying
services = {
sell = { categories = { 'vehicle_parts' }, prices = { car_door = 140 }, maxPerSale = 10,
dailyUnits = 60, cooldown = 30, activity = 'parts_sale' },
},
},
Services: theft, house, chop, grow, sell, shop = { items = { [item] = { price, max } } }.
autoUnlock = true makes a contact known for free once its requirements are met; visibleFrom shows the blurred
card before the requirement level. Every sell price must belong to one of the buyer's categories.
data/objectives.lua — automatic missions
{ id = 'my_goal', title = 'obj_my_goal', description = 'obj_my_goal_desc', minLevel = 10, minRep = nil,
activities = { 'vehicle_dismantle' }, required = 10, measure = 'completions', cycle = 'weekly',
reward = { xp = 500, rep = 2, money = 2500, item = { name = 'lockpick', count = 2 } } },
measure = 'units' counts units (e.g. items sold). cycle: once, daily, weekly. Progress only counts while
the player meets minLevel / minRep, and only for rewarded completions.
Anonymous contracts
Admins open the tablet → Anonymous → Post contract: title, description, type (activity: complete N of an
activity after publishing; delivery: hand in N of an item at a drop point), requirements, duration and rewards
(within Contracts.limits). The first eligible player wins; others see it as taken. Manage with /illegal contracts, /illegal cancel <id>, /illegal retry <id> (re-delivers a failed / pending reward without paying a
part twice). Every create / cancel / retry is written to ns_illegal_audit.
Admin commands (/illegal, ACE ns-illegal.admin, framework admin or console)
profile <id> · setxp <id> <xp> · setrep <id> <rep> · clearname <id> · contracts · cancel <contract> ·
retry <contract> · testarrest <id> (only with Config.Debug = true, applies a real audited penalty) · validate ·
locations (the location editor, in game).
Players can cancel their own job with /illegalcancel.
Integration API (server exports)
| Export | Allowed callers | Returns / errors |
|---|---|---|
ReportArrest(targetSrc, incidentId?, officerSrc?) | Config.Arrest.allowedResources | { penalty, reputation, rank } / nil, 'err_no_permission' | 'err_duplicate' | 'err_player_unknown' | 'err_profile' | 'err_database' |
ReportArrestByIdentifier(identifier, incidentId?) | same | same |
ReportActivity(targetSrc, activityId, completionId, { units?, ref?, detail?, tier? }) | Config.Integrations.activityReporters[resource] contains activityId | career result (xp, rep, after, levels, capped, …) / nil, 'err_no_permission' | 'err_duplicate' | 'err_level_required' | 'err_rep_required' | 'err_not_registered' |
ReportHotwire(targetSrc, netId, tier, completionId, opts?) | ns-carthief | { contract = true } (the player's own theft contract vehicle moves on) or a vehicle_hotwire career result / nil, errKey. opts.quick = true: contract hand-off only (nil, 'err_not_contract' otherwise) |
AtContact(targetSrc, service) | ns-houserobbery (off with Integrations.houserobbery = false) | true at an unlocked, open contact with that service (registered players only) |
GetCareer(targetSrc) | any server resource (read-only) | { registered, username, level, xp, reputation, rank, tier, kingpin, arrests } / nil; creates no career row |
CanDoActivity(targetSrc, activityId) | any (read-only) | true / false, errKey (level, reputation, cooldown, police) |
GetItemCategory(item) | any (read-only) | category id / nil |
useDarkTablet | ox_inventory only | ox item export |
Hook: AddEventHandler('ns-illegal:hook:securityFlag', function(src, reason, detail) end) — suspicious requests.