Skip to content

Hacks in Your Scripts ​

The ten hacking minigames are not only for Command Prompt. Any resource can put one in front of a door, a vault or a terminal, and get a result it can trust: a win is checked on the server, so a modified client cannot skip the hack.

There are three ways to do it:

Use it whenYou call
From the serverYour server code decides when the hack happensRunHack
From the clientYour robbery's flow runs on the client (a target option, an animation, then the hack)StartHack, then ConsumeHackProof on the server
At a computerThe player has to sit at a security PC and break in from Command PromptA target in configs/server/targets.lua

How a Win Is Verified ​

A player never gets to say "I won". Every run, from Command Prompt or from your script, goes the same way:

  1. The server starts the run. It reads the difficulty's settings from configs/shared/hacks.lua, works out the least time the game can be played in and how many milestones it has, and hands the client a token. For maze, pipe, typing and signal it also generates the puzzle itself and keeps the answer.
  2. The client plays, reporting each milestone in order. Milestones that come faster than a person could play are refused.
  3. The client submits the win. The server checks that the token is still good, that every milestone arrived, that the run took at least the minimum time, and that the puzzle's answer is right.
  4. Only then does something happen: at a computer, the next step of a breach or its reward; for your script, a proof.

A proof is good once, only for the player who played it, and only for ProofTTL seconds (120). The minimum play time is worked out from the same settings as the difficulty, so a harder preset is also a longer proof of play.

From the Server: RunHack ​

The simplest way. Your server code asks for a hack, the player plays it, and the call returns true only for a verified win. There is no proof to handle: the server redeems it itself.

lua
RegisterNetEvent('my-robbery:server:hackVault', function()
    local src = source
    -- your usual checks first: near the vault, has the item, enough police, not on cooldown
    if exports['sd-computers']:RunHack(src, 'pipe', 'hard') then
        -- open the door, start the loot
    end
end)

RunHack waits while the player plays, so call it from an event handler or a thread, as above. It returns false straight away if that player already has a hack window open.

From the Client: StartHack ​

When your robbery's flow runs on the client, open the hack there and send the proof to the server, which redeems it before giving anything. This is a straight swap for another minigame resource:

lua
-- client: before
exports['some-minigame']:Start(function(success)
    if success then TriggerServerEvent('my-robbery:server:openVault') end
end)

-- client: after
local won, proof = exports['sd-computers']:StartHack({ game = 'pipe', difficulty = 'hard', title = 'Vault Door' })
if won then
    TriggerServerEvent('my-robbery:server:openVault', proof)
end
lua
-- server
RegisterNetEvent('my-robbery:server:openVault', function(proof)
    local src = source
    local info = exports['sd-computers']:ConsumeHackProof(src, proof, { game = 'pipe', difficulty = 'hard' })
    if not info then return end -- no proof, someone else's, already used, expired, or an easier game
    -- check src is really at the vault, then open it
end)

Redeem the proof on the server

Most robberies trust a bare TriggerServerEvent sent after their minigame, so anyone with an executor can fire that event and skip the hack altogether. Swapping only the client half looks the same in game but stays exactly that exploitable. The server half, ConsumeHackProof, is what closes it. The expect table also stops a proof from an easy run being used for a hard lock.

At a Computer: Breach Targets ​

For a heist where the hack happens at a computer - a bank's security office, a server room - add a breach target. The player types breach <command> in Command Prompt, plays every step in order, and the server fires your event when the last one is won:

lua
-- configs/server/targets.lua
{
    id = 'pacific_vault_lock',
    computers = { 'pacific_security_pc' }, -- a computer id from computers.lua
    command = 'vault-lock',                -- the player types: breach vault-lock
    label = 'Pacific Standard - vault time lock',
    hidden = true,                         -- not listed by `targets`: they need the intel
    steps = {
        { game = 'maze', difficulty = 'hard' },
        { game = 'cipher', difficulty = 'hard' },
    },
    requires = { item = 'trojan_usb', count = 1, consume = true },
    cooldown = 3600,
    failCooldown = 120,
    exclusive = true,
    alert = { chance = 60, onFail = true, title = 'Vault Intrusion', code = '10-90', jobs = { 'police' } },
    reward = { event = 'my-robbery:server:vaultUnlocked' },
},
lua
-- your robbery, server side
AddEventHandler('my-robbery:server:vaultUnlocked', function(src, info)
    -- info = { target = 'pacific_vault_lock', computer = 'pacific_security_pc', instance = 'pacific_security_pc' }
    -- unlock the vault door
end)

AddEventHandler, not RegisterNetEvent

The reward event is fired on the server with TriggerEvent. Listen with AddEventHandler: an event that is never registered as a net event cannot be fired by a client.

Every successful breach also fires sd-computers:server:breachCompleted, if you would rather hear about all of them in one place. Put a target like this on a location computer: a prop computer's position comes from the player's client, so the server cannot prove they are really there.

Things to Know ​

  • A run from your script is not tied to a place. sd-computers proves the player won the game, not that they were standing at your vault. Check where they are on the server, as you would without it.
  • One run at a time per player. Starting a new one voids the one in progress, and two starts by the same player need a second between them. StartHack returns false while its window is already open.
  • Difficulty is a preset, not a setting per call. 'easy', 'medium' and 'hard' are the presets in configs/shared/hacks.lua; changing one changes it for every robbery and for Command Prompt.
  • Taking a game out of Games in configs/shared/hacks.lua turns it off for the exports too. Practice = false does not: it only stops practice runs in Command Prompt.
  • Leave a trail. AddLog writes to a computer's System Logs: log "Vault door opened" to the bank's security PC, and whoever looks into it afterwards finds it in that computer's Event Viewer.
lua
exports['sd-computers']:AddLog('pacific_security_pc', 'warning', 'Vault Control', 'Vault time lock overridden', { door = 'Main vault' })

The Minigames ​

KeyGameKeyGame
mazeNetwork MazeevasionHighway Evasion
captureNode CapturetypingCode Injection
cipherCipher LocksignalSignal Replay
snakeData WormpipePacket Routing
firewallFirewall DefensebreakoutICE Breaker