NullSect
Systems nominal

Docs / Illegal System / Installation

Installation — ns-illegal

1. Files and start order

Put the ns-illegal folder into your resources (e.g. resources/[nullsect]/ns-illegal) and start it after oxmysql, your framework, your inventory and (optionally) ox_lib / ox_target:

ensure oxmysql
ensure ox_lib
ensure qbx_core          # or qb-core / es_extended
ensure ox_inventory      # or your framework inventory
ensure ox_target         # optional
ensure ns-illegal
add_ace group.admin ns-illegal.admin allow

ns-illegal does not need ns-core at runtime: the ns-core modules are vendored in core/.

2. Database

Config.Database.autoMigrate = true (default) creates the tables on the first start (the DB user needs CREATE / ALTER). Otherwise import sql/ns-illegal.sql once. Tables: ns_illegal_meta, _profiles, _stats, _ledger, _unlocks, _objectives, _contracts, _contract_progress, _grow, _audit, _points (positions edited with /illegal locations, 1.2.0). The SQL never drops anything.

3. Items

The Dark Tablet is a real item. Without an inventory (Inventory = 'none') the tablet cannot open. Every item below is used by the default configuration; rename them in config.lua / data/*.lua if your server already has equivalents (the console lists configured items your inventory does not know at start).

ItemUsed by
dark_tabletthe tablet (not consumed)
lockpickvehicle theft, house robbery (lockpickItem = false to disable)
stolen_jewels, stolen_electronics, stolen_watchhouse robbery loot, pawn fence
car_door, car_wheel, car_engine_part, car_radiodismantling, parts buyer
weed_seed, fertilizer, weed_bagcultivation, supplier, distributors
gold_bar, stolen_artKingpin buyer (sourced from your other resources / contracts)
black_moneyonly when Config.Economy.payout.method = 'item'

ox_inventory (ox_inventory/data/items.lua)

['dark_tablet'] = {
    label = 'Dark Tablet', weight = 600, stack = false, close = true, consume = 0,
    description = 'An encrypted tablet for the underground network.',
    server = { export = 'ns-illegal.useDarkTablet' },
},
['stolen_jewels'] = { label = 'Stolen Jewels', weight = 150 },
['stolen_electronics'] = { label = 'Stolen Electronics', weight = 800 },
['stolen_watch'] = { label = 'Stolen Watch', weight = 100 },
['car_door'] = { label = 'Car Door', weight = 9000 },
['car_wheel'] = { label = 'Car Wheel', weight = 6000 },
['car_engine_part'] = { label = 'Engine Part', weight = 7000 },
['car_radio'] = { label = 'Car Radio', weight = 1500 },
['weed_seed'] = { label = 'Weed Seed', weight = 10 },
['fertilizer'] = { label = 'Fertilizer', weight = 1000 },
['weed_bag'] = { label = 'Bag of Weed', weight = 50 },
['gold_bar'] = { label = 'Gold Bar', weight = 1000 },
['stolen_art'] = { label = 'Stolen Art', weight = 3000 },
-- 'lockpick' and 'black_money' usually exist already

With the server.export line, set Config.Tablet.registerUsable = false (the export opens the tablet; it only accepts calls coming from ox_inventory and cancels the generic "use" so the tablet is never consumed).

QBCore (qb-core/shared/items.lua)

dark_tablet = { name = 'dark_tablet', label = 'Dark Tablet', weight = 600, type = 'item', image = 'dark_tablet.png',
    unique = true, useable = true, shouldClose = true, description = 'An encrypted tablet for the underground network.' },
-- add the other items from the table above the same way (useable = false)

Keep Config.Tablet.registerUsable = true: ns-illegal registers the item through QBCore.Functions.CreateUseableItem (the core bridge). Qbox with CreateUseableItem works the same way.

ESX

Insert the items into the items table (name, label, weight) and keep registerUsable = true (ESX.RegisterUsableItem).

4. Police arrests (required for the reputation penalty)

The workspace this resource was built in had no police resource, so the arrest hook is an adapter that your police resource must call. Call it only server-side, at the point where an arrest is confirmed (sentence / booking), never from a client event and not for routine stops:

-- in your police resource, server side, after it validated the officer and the target
exports['ns-illegal']:ReportArrest(targetServerId, incidentId, officerServerId)
-- offline / by character id:
exports['ns-illegal']:ReportArrestByIdentifier(citizenId, incidentId)
  • The calling resource must be listed in Config.Arrest.allowedResources (checked with GetInvokingResource()).
  • incidentId should be the police resource's own unique id of this arrest (booking / case / jail record id). Retries with the same id are ignored. Without an id, a second report for the same character inside Config.Arrest.dedupeSeconds is ignored.
  • officerServerId is optional; when given, that player must have a job from Config.Police.jobs.
  • The character is resolved immediately: a player who disconnects right after the arrest still gets the penalty.

Example for qb-policejob (verify the event in your version — this was not tested against a real copy): in qb-policejob/server/main.lua, inside RegisterNetEvent('police:server:JailPlayer', function(playerId, time) ... end), after the existing officer / distance checks and after the target was jailed, add:

exports['ns-illegal']:ReportArrest(playerId, ('jail:%s:%d'):format(playerId, os.time()), src)

5. Coordinates and content

All shipped coordinates are marked -- example. The easy way is in game: /illegal locations (admins) shows every position on a map and a list. Go to a spot and press E to move or add one, switch the examples off, done; it applies at once and is stored in the database (ns_illegal_points). The files stay the defaults: data/locations.lua (contacts), data/activities.lua (theft spots / drop-offs, houses / interiors) and config.lua (Config.Contracts.dropPoints). Reset in the editor goes back to the file value. data/*.lua is a server-only script, so hidden coordinates are never downloaded by clients.

With ns-locksmith installed, the vehicle spots recorded there with /lslocations are also used as theft parking spots (Config.VehicleTheft.locksmithSpots = 'add'; 'replace' uses only them, false none). ns-illegal only reads its data/locations.json, it never writes it.

6. Verify

CheckExpected
Server console[ns-illegal:SV] [INFO] ready (framework=qbox, inventory=ox, core=1.0.0) and Contracts loaded: N active.
Config errorsConfig: ... lines name the exact key; the tablet answers "network is starting" until they are fixed (/illegal validate)
Using the Dark Tabletregistration screen on first use, dashboard afterwards
Without the item"You need your Dark Tablet for that."
/illegal helpadmin command list (normal players: refused and logged)

7. In-game test checklist

The offline tests (tests/run_*.lua) cover the server rules with a simulated runtime. These need a real server:

  1. Use the item → register → reconnect → still registered. Two players try the same name at the same time.
  2. Drop the tablet while it is open → it closes within watchIntervalMs, actions are refused.
  3. Vehicle theft end to end (spawn, lockpick, keys hook, drop-off payout) with OneSync; restart the resource mid-job → vehicle removed, no penalty.
  4. House robbery: two players in different houses never see each other (routing buckets); disconnect inside → back at the door after reconnecting.
  5. Arrest through your police resource → HUD shows the reputation loss, arrest count +1; repeat the same incident → no second penalty.
  6. Buy a clue twice quickly (double click / two clients) → charged once; GPS works only after buying.
  7. Two players finish the same contract within the same second → one winner, one reward.
  8. NUI at 1920x1080 and 1280x720, EN and TR; ESC always frees the mouse.
  9. /illegal setxp <id> 7000 → level 10, Tier 2 contacts appear in Locations.
  10. Location editor (/illegal locations), 1.2.0 (confirmed in game 2026-10-22):
    • the panel opens with every point; map drag / wheel zoom; search; ESC frees the mouse;
    • Move a contact: walk, press E → its ped and target zone move for every player without a restart;
    • Add a parking spot from a car (heading = the car) and switch the examples off → the next theft contract spawns there; a car parked on a spot makes the job pick another one;
    • Add a contract drop point (name form) → it is offered in the tablet's contract form;
    • in an interior (teleport to its entry): add / move search spots, move the exit; a running robbery keeps the old positions;
    • ns-locksmith spots are listed with the ns-locksmith tag; restart ns-locksmith → still there;
    • restart ns-illegal → every change is still there; world view ghosts and blips are removed.

8. With ns-carthief and ns-houserobbery (optional)

Nothing to configure: start them in any order (ensure [nullsect] is fine). While they run:

  • ns-carthief: a theft contract vehicle is opened with the ns-carthief lockpick and hotwire instead of the progress bar; the contract moves on to the drop-off when the player who took it hotwires it. Every other hotwired vehicle is a street theft worth career XP (vehicle_hotwire, 20 per day; stealing is never blocked). Keep the contract models (Config.VehicleTheft.models) out of ns-carthief's blacklist, or they cannot be opened.
  • ns-houserobbery: house contacts (services.house) open the ns-houserobbery contract offers; its own informant keeps working. Finished contracts give career XP to every member who went inside (house_robbery by house tier) and to the safe cracker (house_safe). The career level unlocks the house tiers (mid at level 5, high at 10 by default: Config.Tiers[x].illegalMinLevel in ns-houserobbery).
  • Both use the career level for their minigames. Players without a Dark Tablet username keep stealing, but get no career XP and only the cheapest houses.

Switch a link off with Config.Integrations.carthief = false / houserobbery = false here, or Config.Integrations.illegal = false in the other resource.

In-game checks (not tested in game yet):

  1. Theft contract with ns-carthief: no progress-bar option at the car; lockpick + hotwire → drop-off blip; delivery → XP.
  2. Hotwire a street car → tablet HUD shows XP; after 20 in a day the car still starts, no XP.
  3. house_tipster at night → ns-houserobbery offers; mid locked below career level 5.
  4. restart ns-illegal during a house contract → the contract finishes, no errors in either console.

Troubleshooting

  • oxmysql is not started → start oxmysql before ns-illegal.
  • Migration v1 failed → DB permissions, or import sql/ns-illegal.sql.
  • No inventory detected → start ox_inventory / your framework inventory before ns-illegal.
  • Could not register "dark_tablet" as a usable item → use the ox_inventory server.export line above.
  • Items used by the configuration but unknown to your inventory → add them (section 3) or change the names.
  • Arrests do nothing → the calling resource is not in Config.Arrest.allowedResources, or the call runs on the client.

Cart

Your cart is empty.