Electus LogoElectus Documentation

Electus Computers Exports

Exports for integrating Electus Computers with your own FiveM scripts: desktop apps, computers, hacking and laptop hardware.

Call exports with exports.electus_computers:<Name>(...). Everything marked Stable API v1 keeps its name, parameters, results and reason codes for the whole 1.x series. New optional fields may be added.

Registrations and handlers live in electus_computers, so register again whenever it (re)starts:

if GetResourceState("electus_computers") == "started" then RegisterMyApp() end

AddEventHandler("onClientResourceStart", function(resourceName)
    if resourceName == "electus_computers" then RegisterMyApp() end
end)

Desktop apps

Stable API v1. Adds your own app to the computer: an icon on the desktop, or a listing in the in-game software store, that opens a window with your web page in it.

Register the app on both the client and the server, with the same options. The client draws the app. The server lets players install it from the store, move it to Trash and keep its desktop position. A shared script does both:

-- shared_script in your fxmanifest.lua
local function RegisterMyApp()
    exports.electus_computers:AddDesktopApp({ id = "my-notes", label = "My Notes", url = "web/index.html" })
end

if GetResourceState("electus_computers") == "started" then RegisterMyApp() end

AddEventHandler(IsDuplicityVersion() and "onResourceStart" or "onClientResourceStart", function(resourceName)
    if resourceName == "electus_computers" then RegisterMyApp() end
end)

The server reads id and store and ignores the client-only options. Apps registered only on the client still open, but nothing about them is saved. When your resource stops, its apps are removed on both sides: their icons stay put and show a "cannot open" notice until the resource starts again.

AddDesktopApp

Client and server.

local ok, reason = exports.electus_computers:AddDesktopApp({
    id = "my-notes",
    label = "My Notes",
    url = "web/index.html",
    icon = "terminal",
    width = 800,
    height = 500,
    onOpen = function(data)
        exports.electus_computers:SendDesktopAppMessage(data.id, "load", { notes = GetMyNotes() })
    end,
})
OptionTypeDefaultDescription
idstringrequiredUnique app id: letters, digits, - and _, up to 64 characters. Registering the same id again replaces the app.
labelstringrequiredName on the desktop, in the window title and in the store. Up to 64 characters.
urlstringrequiredPage shown in the window: a file in your resource ("web/index.html"), "nui://<resource>/<path>", or an http(s):// URL. Files must be listed in your fxmanifest.lua files.
iconstringbrowser iconA built-in icon name, or an image resolved like url. Use a full-bleed square image; it is drawn with rounded corners.
widthnumber640Window width in pixels, 200 to 3840.
heightnumber420Window height in pixels, 150 to 2160.
storageGbnumber1Disk space the app takes on a computer, 0 to 1000.
storebooleanfalsefalse: the app is added to the desktop of every computer. true: it is listed in the software store and players install it.
summary, version, publisher, categorystringStore listing text. publisher defaults to your resource name.
onOpenfunctionClient only. Called with { id = string } each time the player opens the app window. It does not run for players watching that screen, or when your page navigates inside the window.

Built-in icon names: binary, briefcase, browser, calculator, cast, cloud, explorer, flame, folder, key, mail, map, paint, radar, radio, settings, shield, siren, snake, terminal, tray, wallet.

Returns true, or false and a reason: invalid_options, invalid_id, invalid_label, invalid_url, invalid_icon, invalid_width, invalid_height, invalid_storageGb, invalid_store, invalid_summary, invalid_version, invalid_publisher, invalid_category or invalid_onOpen. The server export only returns invalid_options, invalid_id and invalid_store. Every failure also prints an error naming the option. Unknown options print a warning and are ignored.

Paid apps

Prices are server authority. To sell a store app, add its id to Config.Software.apps in config/apps/software.lua. The store then shows that price and the server charges it in Config.Crypto.asset. Store apps without an entry are free once the server export has registered them.

["my-notes"] = { title = "My Notes", price = 500 },

RemoveDesktopApp

Client only. Takes the app off this player's computers, icons included, for apps a player can lose, such as a job or gang app. Add it again with AddDesktopApp.

exports.electus_computers:RemoveDesktopApp("my-notes")

Returns true, or false and unknown_app.

SendDesktopAppMessage

Client only. Posts a message to your app page on this player's own screen.

exports.electus_computers:SendDesktopAppMessage("my-notes", "load", { notes = notes })

Returns true, or false and unknown_app or invalid_action (action is not a non-empty string). data defaults to {}.

Your page receives it as a message event. The page also gets the string "componentsLoaded" whenever it loads and before each message:

window.addEventListener("message", (event) => {
    if (typeof event.data !== "object" || event.data.appId !== "my-notes") return
    const { action, data } = event.data
    if (action === "load") render(data.notes)
})

The page's root font size is set to the desktop's (16px at 1080p), so size your page in rem and it scales with the computer, not with the window. Inside a computer window window.name is "electus_computers_app", and your resource's SendNUIMessage does not reach the page: send it data with SendDesktopAppMessage, or return it from your NUI callbacks.

To send data back, call your own resource's RegisterNUICallback handlers from the page:

fetch("https://<your_resource>/<callback>", { method: "POST", body: JSON.stringify(payload) })

Desktop app events

Both fire on the client of the player who opened the app, after onOpen, with { id = string }:

AddEventHandler("electus_computers:desktopAppOpened", function(data) end)          -- any app
AddEventHandler("electus_computers:desktopAppOpened:my-notes", function(data) end) -- one app

Other client exports

ExportReturns
GetComputerApps(){ id: string, label: string }[]: built-in, store and registered desktop apps, sorted by label.
GetComputerModels(){ laptop: string, desktop: string }: prop models of those computer types.

Server API

Stable API v1. Every export returns ok, result: true and a result table, or false and a reason code string.

CreateComputer

Places a computer in the world.

local ok, result = exports.electus_computers:CreateComputer({
    computerType = "desktop",
    coords = vector4(-510.9, 287.3, 83.3, 180.0),
    computerName = "Front desk",
    apps = { "documents", "calculator" },
    allowedJobs = { police = 0 },
})

Returns true, { serialNumber, computerType, placement }.

OptionTypeDescription
computerTypestringRequired. A key of Config.Computer.types: "desktop", "laptop" or "tv".
coordsvector3, vector4 or { x, y, z, w? }Required. w is used as heading when heading is not set.
headingnumberHeading in degrees.
rotationvector3 or { x, y, z }Full rotation. Defaults to { 0, 0, heading }.
bucketintegerRouting bucket. Defaults to 0.
modelstringDefaults to the computer type's model.
serialNumberstringFixed serial. Generated when omitted.
ownerIdentifierstringCharacter identifier of the owner.
computerNamestringName shown on the computer.
passwordstringPassword needed to unlock it, 1 to 128 characters.
apps(string | { appId, data })[]Apps to install. See InstallComputerApp.
replaceDefaultAppsbooleanInstall only apps, not the default starting apps.
allowedJobstable<string, number>Job name to minimum grade. Empty allows everyone.
temporarybooleanRemove the computer when your resource stops or electus_computers restarts.

Reason codes: invalid_options, invalid_type, coords_missing, invalid_bucket, model_missing, invalid_password, serial_exists, placement_failed, owner_resource_stopped, plus any InstallComputerApp reason.

DestroyComputer

Deletes the computer, its placement and everything stored on it.

local ok, result = exports.electus_computers:DestroyComputer(serialNumber)

Returns true, { serialNumber }, or false and serial_missing or computer_not_found.

InstallComputerApp

Installs an app on one computer. For note apps ("note", "notes", "notepad") it adds the notes in data instead.

local ok, result = exports.electus_computers:InstallComputerApp(serialNumber, "notes", {
    note = { title = "Vault code", content = "4412" },
})

Returns true, { serialNumber, appId, notes }.

data fieldDescription
location"desktop", "folder" or "stored".
folderIdFolder to place the app in when location is "folder".
grid{ column, row } position on the desktop.
note, notesOne note or a list of notes: { id?, title?, content?, updatedAt? }.

Reason codes: serial_missing, app_missing, computer_not_found, computer_destroyed, busy, invalid_location, folder_missing, invalid_note.

UninstallComputerApp

Removes an app, including from the trash.

local ok, result = exports.electus_computers:UninstallComputerApp(serialNumber, "notes")

Returns true, { serialNumber, appId }, or false and app_not_installed or one of the InstallComputerApp reasons.

Server events

Server-local: listen with AddEventHandler, never RegisterNetEvent.

EventArguments
electus_computers:computerCreatedserialNumber, { computerType, ownerIdentifier?, source?, resource? }. Fired by CreateComputer (resource set) or when a laptop, desktop or TV item gets its serial (source set).
electus_computers:computerDestroyedserialNumber
electus_computers:appInstalledserialNumber, appId
electus_computers:appUninstalledserialNumber, appId
electus_computers:hackCompletedsuccess, source, appId, serialNumber, { targetId, minigameId, durationMs, reason? }. reason = "abandoned" for a hack that expired unfinished, was pushed out by newer hacks, or whose player left.

Hacking API

Stable API v1. Run the minigame on the client, then verify the result on the server before paying out. Never trust a client-side success on its own.

RunHack

Client only.

local ok, result = exports.electus_computers:RunHack({ appId = "port-breacher", targetId = "vault-1" })

if ok then
    TriggerServerEvent("my_heist:hacked", result.token)
end
OptionTypeDescription
appIdstringRequired. A hacking app from Config.HackingApps. The computer must have it installed.
serialNumberstringComputer to hack from. Defaults to the open or last used computer.
computerSource"placed"Wait up to 10 seconds for that placed computer to stream in.
autoOpenComputerbooleanOpen the last used computer when none is open. Default true.
autoOpenbooleanOpen the hacking app window. Default true.
minigameIdstringDefaults to the app's minigame.
targetId, targetLabelstringWhat is being hacked.
difficultynumberMinigame difficulty.
timeoutnumberMinigame time limit in milliseconds.
loginTimeout, openTimeoutnumberTime in milliseconds to unlock the computer and for it to boot.

Returns true, result with result.token on success, or false, reason[, result]. Reasons include invalid_app, computer_not_open, computer_not_nearby, computer_closed, computer_open_timeout, read_only, app_not_installed, login_timeout, timeout and failed.

ConsumeHackResult

Server only.

RegisterNetEvent("my_heist:hacked", function(token)
    local ok, details = exports.electus_computers:ConsumeHackResult(source, token)
    if not ok or details.appId ~= "port-breacher" or details.targetId ~= "vault-1" then return end
    -- reward the player
end)

Returns true, { appId, serialNumber, targetId, minigameId, durationMs } once per successful hack by that player, otherwise false. Results expire after Config.HackSessions.resultLifetimeMs.

CanRunHackingApp

Server only.

local ok, reason = exports.electus_computers:CanRunHackingApp(source, serialNumber, "port-breacher")

Returns true when the player can run that hacking app on that computer, otherwise false, reason.

Laptop hardware

Server only. Laptops have parts that wear, break and get repaired or upgraded at a repair bench. Desktops, TVs and world screens have none. All three exports do nothing while Config.LaptopHardware.enabled is false.

GetLaptopHardware

local hardware = exports.electus_computers:GetLaptopHardware(serialNumber)
if hardware and hardware.bootFailure then return end -- the laptop can't start

Returns nil, or { serialNumber, parts, battery, effects, bootFailure?, screenDamage }:

FieldDescription
parts{ id, label, tier, tierLabel, tierIndex, tierCount, condition, status }[] in bench order. id is battery, cpu, fan, ssd, wifi, screen or security. condition is 0 to 100. status is good, worn or broken.
battery{ enabled, charge, charging, pluggedIn, runtimeMinutes, low, minutesLeft?, minutesToFull? }. Placed laptops count as plugged in. minutesLeft is set while unplugged, minutesToFull while charging below 100%.
effects{ hackTimeBonusSeconds, storageGb, routerRangeMultiplier, wifiBroken }.
bootFailureWhy the laptop won't start: cpu, ssd, screen, battery_broken or battery_empty.

A laptop that was never damaged or opened reports factory parts at full condition.

DamageLaptopPart

Takes amount condition points off one part, for your own accidents such as an EMP, a taser or a fire.

local ok, reason = exports.electus_computers:DamageLaptopPart(serialNumber, "screen", 40)

Returns true, or false and disabled or invalid_part.

RepairLaptopHardware

Restores every part to full condition without changing tiers.

local ok = exports.electus_computers:RepairLaptopHardware(serialNumber)

Returns true or false.