Documentation

Exports & Events

Exports, local events, and Discord logs for laptop integrations.

General Information

A laptop that is out has a laptop id: the holder's server id while it is in their hands, and a negative id of its own while it is put down. A desktop computer has a negative id like a placed laptop. Laptop ids are handed out again on every start, so keep a laptop's serial and look the laptop up again on the server zyke_laptop:Ready event.

A laptop's serial belongs to the chassis and stays with it when parts are swapped. Its drive serial is the serial of the SSD inside, which keeps the OS, accounts, files, and system log, and goes wherever the SSD goes. A laptop's factory SSD shares the laptop's serial until it is swapped.

Exports marked Yields wait on the database or another resource. Call them from a thread, an event handler, or a callback.

Apps, sites, device types, Shroud contacts and rooms, Natter businesses, and monitored sites belong to the resource that registers them and are removed when it stops.

Values the client reports, such as IsConnected on the server, are fine for flavour but not for anything that pays out.

Types / Classes

All types / classes can be found in types/*.lua inside zyke_laptop, such as types/laptop.lua, types/hardware.lua, and types/devices.lua.

#Complete Export Index

ContextExports
ClientOpenScreen, CloseScreen, IsScreenOpen, IsConnected, GetActivity, TakeOut, PutAway, GetLaptopDevices, GetLaptopPorts, OpenPorts, ClosePorts, IsServicing, OpenApp, OpenUrl, Notify, AddBookmark, OpenMessenger, OpenMail, SendAppMessage, SendSiteMessage, SetAppBadge, SendMdtAlert
Server: Apps & SitesRegisterApp, UnregisterApp, RegisterSite, UnregisterSite
Server: LaptopsIsConnected, GetLaptop, IsLaptopOwner, SetLaptopFinish, SetLaptopSkin, IsAtLaptop, GetLaptopSerial, GetLaptopDriveSerial, GetLaptopOs, RemoveLaptop
Server: Ports & DevicesGetLaptopDevices, GetSessionDevices, PlugDevice, UnplugDevice, GetLaptopPorts, RegisterDeviceType
Server: HardwareGetLaptopHardware, CreateLaptopMetadata, CreatePartMetadata, GetPartInfo, SetLaptopHardwareSlot, RepairLaptop, GetLaptopSpecs, WearLaptop
Server: StatusGetLaptopStatus, GetLaptopStatuses, GetPlacedLaptops
Server: OwnershipGetLaptopOwner, SetLaptopOwner, ReleaseLaptopOwner, GetCharacterLaptops, IsLaptopReportedStolen
Server: CharactersPurgeLaptopCharacter
Server: Drives & RecoveryGetLaptopLogs, IsLaptopDriveUnlocked, GetLaptopRecoveryReport
Server: Network & BrowsingGetLaptopNetwork, GetBrowsingTier, GetSiteVisitTraces, SetSiteMonitored
Server: ShroudGetChatHandle, GetChatTraces, RegisterChatRoom, RemoveChatRoom, RegisterChatContact, RemoveChatContact, SetChatListings, SendChatMessage, UpdateChatMessage, OpenChat
Server: NatterGetMessengerNumber, HasMessengerAccount, RegisterMessengerBusiness, SendMessengerMessage, GetMessengerMetadata
Server: CasinoIsCasinoEnabled, GetCasinoBalance, AdjustCasinoCredits
Server: MDT & BusinessesSee MDT & Businesses
Server: Crypto & PaymentsSee Crypto & Payments
Server: Mail & DocumentsSee Mail & Documents
Server: Domains, Sites & ReviewsSee Domains, Sites & Reviews
Server: Racks & HackingSee Racks & Hacking
Server: App ServicesSee App Services

#Client Sided Exports

#Open Screen

Opens the OS for a resource with its own computer in the world, such as a police MDT. The player takes out a stand-in laptop that goes away with the screen, on their lap while they sit in a vehicle, and the camera zooms onto it. The OS runs on a drive the server keeps for the character, not on a laptop item.

Returns false while the player already uses a laptop or has the screen open, and when CanUseLaptop("screen") or IsPlayerIncapacitated refuses. The player is notified then.

Example:

lua
---@return boolean opened
local opened = exports["zyke_laptop"]:OpenScreen()

#Close Screen

Closes the OS screen, whichever way it was opened. Does nothing while it is closed.

Example:

lua
exports["zyke_laptop"]:CloseScreen()

#Is Screen Open

Returns whether the OS is on the player's screen.

Example:

lua
---@return boolean open
local open = exports["zyke_laptop"]:IsScreenOpen()

#Is Connected

Returns whether the player's OS is connected to the Onion network.

Example:

lua
---@return boolean connected
local connected = exports["zyke_laptop"]:IsConnected()

#Get Activity

Returns what the player is doing with a laptop, or nil. holding is a laptop in the hands, using a placed laptop, screen a screen opened with OpenScreen, ports the port view, servicing the service view, placing picking a spot to put one down, and racking the slot picker of a laptop rack.

Example:

lua
---@return "holding" | "using" | "screen" | "ports" | "servicing" | "placing" | "racking" | nil activity
local activity = exports["zyke_laptop"]:GetActivity()

#Take Out

Takes the laptop out as if the player used the item. Does nothing while the player is busy with a laptop. CanUseLaptop("takeOut") is asked first.

Example:

lua
exports["zyke_laptop"]:TakeOut()

#Put Away

Puts a held laptop away, closing its screen. Does nothing unless the player holds a laptop.

Example:

lua
exports["zyke_laptop"]:PutAway()

#Get Laptop Devices

Returns what this client sees in the ports of a laptop, by port id, or nil for a laptop it does not see. Only the device type and item name are synced to clients; use the server export for metadata.

Example:

lua
---@param ownerId integer @ Laptop id
---@return table<string, {type: string, item: string}>? devices
local devices = exports["zyke_laptop"]:GetLaptopDevices(ownerId)

#Get Laptop Ports

Returns every port a laptop has, in the port view's order. A desktop computer only has hdmi, usba, and audio, all on its rear side.

Example:

lua
---@return LaptopPort[] ports @ {id, kind, side}
local ports = exports["zyke_laptop"]:GetLaptopPorts()

The ports of a laptop:

PortKindSide
usbc1, usbc2usbcleft
hdmihdmileft
usbausbaright
audioaudioright

#Open Ports

Opens the port view of a placed laptop within reach. The player steps round to its front first. Does nothing while the player is busy with a laptop. CanUseLaptop("ports") is asked first.

Example:

lua
---@param ownerId integer @ The placed laptop's id
exports["zyke_laptop"]:OpenPorts(ownerId)

#Close Ports

Closes the port view.

Example:

lua
exports["zyke_laptop"]:ClosePorts()

#Is Servicing

Returns whether the player has the service view open. See Hardware & Servicing.

Example:

lua
---@return boolean servicing
local servicing = exports["zyke_laptop"]:IsServicing()

#Open App

Opens an app, passing params to its window. An app that does not run on the player's OS is not opened.

The OS runs while a laptop is at hand and for a few minutes after. Like a real laptop that is off, an OS that is not running misses the call: it returns false and nothing is kept for the next boot.

Example:

lua
---@param appId string
---@param params? table
---@return boolean delivered
local delivered = exports["zyke_laptop"]:OpenApp("my_app", {tab = "orders"})

#Open Url

Opens an address in the OS's own browser. Returns false while no OS runs.

Example:

lua
---@param url string
---@return boolean delivered
local delivered = exports["zyke_laptop"]:OpenUrl("lscustoms.com")

#Notify

Shows a desktop notification. Clicking it opens url in the OS's browser, or else the app appId with params passed to its window. Returns false without a title, and while no OS runs; the notification is not kept.

id replaces a notification with the same id. actions adds up to two buttons; an action needs an id of up to 32 letters, digits, _, or - and a label of up to 24 characters, and an invalid list is dropped. A click on a button, or on the notification itself, fires the client event zyke_laptop:NotificationAction for your resource with data, which is dropped above 1 KiB of JSON. sound names a sound played instead of the chime, false for a silent banner.

Example:

lua
---@param notification LaptopNotification
---@return boolean delivered
local delivered = exports["zyke_laptop"]:Notify({
    title = "Order ready", -- Required
    text = "Your parts are at the counter.", -- Optional
    icon = "package", -- Optional, a Lucide icon name or an image URL
    appId = "my_app", -- Optional, opened on click
    url = "lscustoms.com", -- Optional, opened on click before appId
    params = {tab = "orders"}, -- Optional, passed to appId's window
    duration = 6000, -- Optional
    id = "order_ready", -- Optional, replaces the notification with this id
    actions = {{id = "collect", label = "On my way"}}, -- Optional, two at most
    data = {orderId = 12}, -- Optional, handed back with an action
    sound = false, -- Optional, a sound name, or false for no sound
})

#Add Bookmark

Adds a bookmark to the bookmarks every OS's browser shares, once per id, for example when a contact in the world hands the player an address. While no OS runs it waits on the client, through relogs, until a browser saves it. force adds it again after the player deleted it.

Returns whether the bookmark was handed over or kept, not whether the player lacked it. Returns false without an id and url.

Example:

lua
---@param bookmark LaptopBookmark
---@return boolean kept
exports["zyke_laptop"]:AddBookmark({
    id = "market", -- Unique within your resource
    url = "market5xq.onion",
    title = "The Market", -- Optional
    force = false, -- Optional
})

#Open Messenger

Opens Natter, on a chat when chatId is given. Returns false while no OS runs. See Messaging.

Example:

lua
---@param chatId? string
---@return boolean delivered
local delivered = exports["zyke_laptop"]:OpenMessenger()

#Open Mail

Opens Mail, writing a new message to to when an address is given. Returns false while no OS runs.

Example:

lua
---@param to? string @ An address to write to
---@return boolean delivered
local delivered = exports["zyke_laptop"]:OpenMail("[email protected]")

#Send App Message

Sends data to the app's open page as a message event. Returns false while no OS runs.

Example:

lua
---@param appId string
---@param data any
---@return boolean delivered
local delivered = exports["zyke_laptop"]:SendAppMessage("my_app", {orders = 3})

#Send Site Message

Sends data to the site's open page as a message event. Returns false while no OS runs.

Example:

lua
---@param siteId string
---@param data any
---@return boolean delivered
local delivered = exports["zyke_laptop"]:SendSiteMessage("my_site", {stock = 12})

#Server Sided Exports

Server exports are grouped below by area. Shroud's exports are only registered while chat.enabled is on, Natter's while messenger.enabled is on, and PurgeLaptopCharacter while Config.Admin.enabled is on. The casino exports are always registered and answer as disabled while the casino is off.

The MDT, business, crypto, payment, mail, document, domain, site, review, rack, hacking, and app service exports have pages of their own, listed in the Complete Export Index.

Exports such as SetLaptopOwner, PlugDevice, SetLaptopHardwareSlot, RepairLaptop, WearLaptop, AdjustCasinoCredits, and PurgeLaptopCharacter are trusted server operations. Do not expose them directly through an unrestricted network event.

#Apps & Sites

Apps and sites are registered from a server script, and every client gets them. The laptop forgets every registration when it restarts, so register on the server zyke_laptop:Ready event as well as when your own resource starts. See Apps & Sites for every field.

#Register App

Adds an app to the OS for the calling resource. Registering an id again from the same resource replaces the app. Returns false and "invalid" for an id that isn't up to 64 letters, digits, _, or -, the id of a built-in app, an app without a label and ui, a ui that is neither a path inside your resource nor an https:// address, an os list with no known OS, or a bad access or price. Returns false and "taken" for an id another started resource has. The reason is printed in the server console. A field with any other bad value is left out with a warning.

Example:

lua
---@param app SdkAppRegistration
---@return boolean registered
---@return string? err @ "invalid" or "taken"
local registered, err = exports["zyke_laptop"]:RegisterApp({
    id = "my_app",
    label = "Dispatch",
    ui = "web/app.html", -- A path inside your resource, or an https:// address
})

#Unregister App

Removes an app the calling resource registered.

Example:

lua
---@param appId string
exports["zyke_laptop"]:UnregisterApp("my_app")

#Register Site

Adds a site to the browser for the calling resource. Returns false and "invalid" for a bad id, a site without a url, title, and ui, or a bad ui, and false and "taken" for an id or address another started resource has. The reason is printed in the server console.

Example:

lua
---@param site SdkSiteRegistration
---@return boolean registered
---@return string? err @ "invalid" or "taken"
local registered, err = exports["zyke_laptop"]:RegisterSite({
    id = "my_site",
    url = "market5xq.onion",
    title = "The Market",
    ui = "web/site.html",
})

#Unregister Site

Removes a site the calling resource registered.

Example:

lua
---@param siteId string
exports["zyke_laptop"]:UnregisterSite("my_site")

#Laptops

#Is Connected

Returns whether the player's OS reports a connection to the Onion network. It comes from the client, so use it for flavour only.

Example:

lua
---@param plyId integer
---@return boolean connected
local connected = exports["zyke_laptop"]:IsConnected(plyId)

#Get Laptop

Returns a laptop that is out by its laptop id, or nil. Treat it as read-only.

skin is the backplate id from Config.Settings.skins, nil for the stock lid. finish is the id from Config.Settings.finishes, nil for the stock graphite. usb is the boot stick a session runs from while the player carries it. ports holds the plugged devices. dead is true while the laptop can't power on. kind is computer for a desktop computer, vehicle for a laptop on a vehicle mount with vehicle the network id of that vehicle, and nil for a laptop. user is the player whose screen is open on a placed laptop.

Example:

lua
---@param ownerId integer @ Laptop id
---@return LaptopState? laptop @ {mode, kind?, lidOpen, coords?, bucket, skin?, finish?, usb?, ports?, service?, dead?, user?, vehicle?}
local laptop = exports["zyke_laptop"]:GetLaptop(ownerId)

#Is Laptop Owner

Returns whether the player's character put that placed laptop down, or holds that laptop. This is who put it there, not the registered owner; use GetLaptopOwner for that.

Example:

lua
---@param plyId integer
---@param ownerId integer @ Laptop id
---@return boolean isOwner
local isOwner = exports["zyke_laptop"]:IsLaptopOwner(plyId, ownerId)

#Set Laptop Finish

Sets the finish of the laptop item in the player's inventory slot, nil for the stock graphite. When that laptop is out, everyone who sees it gets the new colour at once.

Example:

lua
---@param plyId integer
---@param slot integer @ Inventory slot of the laptop item
---@param finishId? string @ Id from Config.Settings.finishes
---@return boolean changed
---@return string? err @ "invalidFinish", "noLaptopItem" or "notSaved"
local changed, err = exports["zyke_laptop"]:SetLaptopFinish(plyId, slot, "midnight")

#Set Laptop Skin

Puts a backplate skin on the laptop item in the player's inventory slot, nil for the stock lid. The skin it had is replaced. When that laptop is out, everyone who sees it gets the new backplate at once.

Example:

lua
---@param plyId integer
---@param slot integer @ Inventory slot of the laptop item
---@param skinId? string @ Id listed under Config.Settings.skins
---@return boolean changed
---@return string? err @ "invalidSkin", "noLaptopItem" or "notSaved"
local changed, err = exports["zyke_laptop"]:SetLaptopSkin(plyId, slot, "carbon")

#Is At Laptop

Returns whether the player holds a laptop or stands at one put down, theirs or anyone's. Returns false while the player is dead, downed, or cuffed, or carries a box off Digital Den's shelves. Use it to gate server callbacks behind a laptop page.

Example:

lua
---@param plyId integer
---@return boolean atLaptop
local atLaptop = exports["zyke_laptop"]:IsAtLaptop(plyId)

#Get Laptop Serial

Returns the serial of the laptop the player's OS runs on right now, their own or one another player put down, or nil while their OS is off. Use it to key data to the laptop itself.

Example:

lua
---@param plyId integer
---@return string? serial
local serial = exports["zyke_laptop"]:GetLaptopSerial(plyId)

#Get Laptop Drive Serial

Returns the serial of the SSD in the laptop the player's OS runs on right now, or nil while their OS is off or the laptop has no SSD. Use it to key data that should travel with the drive.

Example:

lua
---@param plyId integer
---@return string? drive
local drive = exports["zyke_laptop"]:GetLaptopDriveSerial(plyId)

#Get Laptop OS

Returns the OS the player runs right now, or nil while it is off. veil boots from a USB stick and keeps nothing on the drive.

Example:

lua
---@param plyId integer
---@return "facade" | "lumen" | "solace" | "veil" | nil osId
local osId = exports["zyke_laptop"]:GetLaptopOs(plyId)

#Remove Laptop

Takes the laptop out of the player's hands and closes their screen. A laptop they put down stays where it is.

Example:

lua
---@param plyId integer
exports["zyke_laptop"]:RemoveLaptop(plyId)

#Ports & Devices

#Get Laptop Devices

Returns what is in the ports of a laptop that is out, by port id, or nil when it is not out. plugId changes every time a device is plugged in. type is nil for an item no registered device type has any more.

The built-in device types are boot_stick, data_stick, power_bank, and hdmi_cable, asic_stick for the rack miner sticks, and hack_adapter and hack_cable while the intrusion toolkit (Config.Hacking.enabled) is on.

Example:

lua
---@param ownerId integer @ Laptop id
---@return table<string, LaptopDeviceInfo>? devices @ {port, kind, side, type?, item, metadata, plugId}
local devices = exports["zyke_laptop"]:GetLaptopDevices(ownerId)

#Get Session Devices

Returns the same for the laptop the player's OS runs on right now, their own or another player's. nil while their OS is off or runs on a screen opened without a laptop prop. Use it to let a hacking app work only with an antenna plugged in, for example.

Example:

lua
---@param plyId integer
---@return table<string, LaptopDeviceInfo>? devices
local devices = exports["zyke_laptop"]:GetSessionDevices(plyId)

#Plug Device

Plugs the item in the player's inventory slot into a port, with the port view's reach checks and the CanPlugLaptopDevice hook. The exact item, with its metadata, moves into the laptop. Yields for a data stick, or another storage device, plugged in for the first time.

Example:

lua
---@param plyId integer
---@param ownerId integer @ Laptop id
---@param port string @ Such as "usba"
---@param slot integer @ Inventory slot of the device item
---@return boolean plugged
---@return string? reason @ Locale key, such as "ports:doesNotFit" or "laptopTooFar"
local plugged, reason = exports["zyke_laptop"]:PlugDevice(plyId, ownerId, "usba", slot)

#Unplug Device

Pulls the device out of a port into the player's inventory, with its metadata, after the reach checks and the CanUnplugLaptopDevice hook.

Example:

lua
---@param plyId integer
---@param ownerId integer @ Laptop id
---@param port string
---@return boolean unplugged
---@return string? reason @ Locale key, such as "ports:portEmpty" or "ports:noRoom"
local unplugged, reason = exports["zyke_laptop"]:UnplugDevice(plyId, ownerId, "usba")

#Get Laptop Ports

Same as the client export. Returns every port, {id, kind, side}, in the port view's order.

Example:

lua
---@return LaptopPort[] ports
local ports = exports["zyke_laptop"]:GetLaptopPorts()

#Register Device Type

Adds a kind of device that goes into the laptop's ports, such as an antenna. Registering an id again replaces it. An item can be only one device type, so an item another type already has is refused.

The type belongs to the calling resource and is removed when it stops. Devices already in a port stay there and can still be pulled out. Register it again when the laptop starts, since the laptop forgets other resources' device types when it restarts.

attach sets where the prop sits in each port kind, in the port's own frame: X points out of the side wall and Z up, in metres and degrees. Ports on the left side turn the prop half a turn and a computer's rear ports a quarter turn, so one offset fits every port. storage makes the device a drive the OS mounts, like the data stick, with its files kept on the server by the stick's serial. serial is two capital letters: a device without a serial gets one starting with them the first time it is plugged in, such as WA-7F3K-92QD.

err is one of id must be a non-empty string, items must list at least one item, ports must list at least one port kind, every item must be a non-empty string, unknown port kind <kind>, storage needs an integer maxBytes and a capacity, serial must be two capital letters, or item <item> is already the device <id>.

Example:

lua
---@param def LaptopDeviceRegistration
---@return boolean registered
---@return string? err @ Why it was refused, in English
local function registerDevices()
    local registered, err = exports["zyke_laptop"]:RegisterDeviceType({
        id = "wifi_antenna",
        items = {"big_usb_antenna", "small_usb_antenna"},
        ports = {"usba", "usbc"}, -- Port kinds it fits: "usba", "usbc", "hdmi" or "audio"
        model = "my_usb_antenna", -- One prop for every item, or models = {big_usb_antenna = "...", small_usb_antenna = "..."}
        attach = {
            usba = {offset = vec3(0.0006, 0.0, 0.0)},
            usbc = {offset = vec3(0.0005, 0.0, 0.0), rotation = vec3(0.0, 0.0, 0.0)},
        },
        -- Optional: a drive the OS mounts. maxBytes is what its files may take, capacity the size players see
        -- storage = {maxBytes = 131072, capacity = 34359738368},
        serial = "AN", -- Optional, serials such as AN-7F3K-92QD
    })

    if (not registered) then print(("The antenna was not registered: %s"):format(err)) end
end

AddEventHandler("onResourceStart", function(resource)
    if (resource == "zyke_laptop" or resource == GetCurrentResourceName()) then registerDevices() end
end)

---@param device LaptopDeviceInfo
AddEventHandler("zyke_laptop:DevicePlugged", function(device)
    if (device.type ~= "wifi_antenna") then return end

    print(("%s plugged %s into port %s of laptop %s"):format(device.plyId, device.item, device.port, device.serial))
end)

#Hardware

#Get Laptop Hardware

Returns a copy of a laptop's hardware, or nil. target is a player with a laptop out, {plyId, slot} for a laptop item in an inventory slot, or {serial} for a laptop that is out, or any laptop while Config.Settings.item is false. A laptop item that was not taken out since hardware was added to it returns nil until its next take-out.

Example:

lua
---@param target integer | LaptopHardwareTarget
---@return LaptopHardware? hardware @ Slots board, ram0, ram1, ssd, wifi, battery, each {name, metadata}
local hardware = exports["zyke_laptop"]:GetLaptopHardware({plyId = plyId, slot = 4})

#Create Laptop Metadata

Returns metadata for a new laptop item, built as a prebuilt from shared/unlocked/hardware.lua, hardware.default when left out, with every serial new. The second argument adds metadata such as a finish or a skin. An owner in it, a character identifier, becomes the laptop's registered owner instead of staying on the item. Yields.

Example:

lua
---@param prebuiltId? string
---@param metadata? table
---@return table | false metadata
---@return string? err @ "unknownPrebuilt"
local metadata = exports["zyke_laptop"]:CreateLaptopMetadata("performance", {finish = "midnight"})

exports["ox_inventory"]:AddItem(plyId, "laptop", 1, metadata)

#Create Part Metadata

Returns metadata for a new part item, with the label and description ox_inventory shows in place of the item's own, such as "Nordwell NV4 1 TB SSD". used = true gives the part a random condition and makes some memory sticks faulty, and condition sets it from 0 to 1. Yields for an SSD.

Example:

lua
---@param itemName string
---@param model string @ Model id from the catalogue
---@param opts? {used?: boolean, condition?: number}
---@return table | false metadata
---@return string? err @ "unknownItem" or "unknownModel"
local ssd = exports["zyke_laptop"]:CreatePartMetadata("laptop_ssd", "nv4_1tb")
local stick = exports["zyke_laptop"]:CreatePartMetadata("laptop_ram", "d5_16", {used = true})
local battery = exports["zyke_laptop"]:CreatePartMetadata("laptop_battery", "zb61", {condition = 0.8})

#Get Part Info

Returns what a part item is and what shape it is in, for a shop listing or a tooltip of your own, or nil for an item that is not a part. fits lists the board generations the part fits. A memory fault only shows once the stick was tested.

Example:

lua
---@param itemName string
---@param metadata table
---@return LaptopPartInfo? info @ {kind, model, label, spec, condition?, faulty, fits, description}
local info = exports["zyke_laptop"]:GetPartInfo(item.name, item.metadata)

#Set Laptop Hardware Slot

Fits a part into a slot of the laptop item in the player's inventory slot, or takes the part out when part is nil, with the service view's checks and the CanInstallPart and CanRemovePart hooks. The installed part is not taken from any inventory, and a removed part is returned to your script without being given to anyone. A laptop that is out is refused while someone services it or an OS runs on it.

The slots are board, cooler, ram0, ram1, ssd, wifi, and battery.

Example:

lua
---@param plyId integer
---@param laptopSlot integer @ Inventory slot of the laptop item
---@param slotId string
---@param part? {name: string, metadata: table}
---@return boolean done
---@return LaptopStoredPart | string | nil result @ The removed part, or the locale key of the reason
local done, err = exports["zyke_laptop"]:SetLaptopHardwareSlot(plyId, laptopSlot, "ssd", {name = "laptop_ssd", metadata = ssd})
local taken, removed = exports["zyke_laptop"]:SetLaptopHardwareSlot(plyId, laptopSlot, "ram1")

#Repair Laptop

A repair shop's shortcut for the laptop item in the player's inventory slot, without the service view or its supplies. clean blows the fan clean, repaste puts fresh paste on, and battery brings the battery back to full health with no cycles.

Example:

lua
---@param plyId integer
---@param laptopSlot integer @ Inventory slot of the laptop item
---@param kinds ("clean" | "repaste" | "battery")[]
---@return boolean repaired
---@return string? err @ Locale key, such as "hardware:service:slotEmpty"
local repaired, err = exports["zyke_laptop"]:RepairLaptop(plyId, laptopSlot, {"clean", "repaste"})

#Get Laptop Specs

Returns what a laptop's parts are and how they run, worked out the way the OS gets them: the processor, its built-in graphics, the working memory, the heat from dust and dried paste, and the speed factor it all comes to. Use it for scripts that build on a laptop's parts, such as a mining script that works out what each laptop makes.

target is the serial of a laptop that is out, {plyId, slot} for a laptop item, or {metadata} or {hardware} for a laptop item kept anywhere else, such as a stash. Absent parts are nil.

Example:

lua
---@param target string | LaptopItemTarget
---@return LaptopSpecs | false specs @ {board?, cooler?, memory, ssd?, wifi?, battery?, heat, speed, canBoot}
---@return string? err @ "invalid", "notOut", "noLaptopItem" or "noHardware"
local specs, err = exports["zyke_laptop"]:GetLaptopSpecs(serial)

if (specs and specs.canBoot) then
    print(specs.board.cpu, specs.memory.gb, specs.speed) -- speed is 0.70 to 1.80, lower is faster
end

#Wear Laptop

Wears a laptop on top of the wear it gathers on its own, for a load such as mining. Every amount is optional and 0 or more. hours wears the laptop as if it ran that long. dust, ssd, paste, and battery change those values directly; paste and battery health stop at their floors in the config.

A laptop that is out is written at once and every OS on it gets its parts as they are now. A laptop item in a slot is written in that slot. A {metadata} or {hardware} target is never written back; the worn copy is returned for your script to keep. A laptop that is open for service is refused.

Example:

lua
---@param target string | LaptopItemTarget
---@param wear LaptopWear @ {hours?, dust?, paste?, ssd?, battery?}
---@return boolean worn
---@return table | string | nil result @ The worn metadata or hardware for a passed copy, otherwise "invalid", "notOut", "noLaptopItem", "noHardware", "inService" or "notSaved"
local worn, err = exports["zyke_laptop"]:WearLaptop(serial, {hours = 1.0})

-- A laptop item in a rack's stash
local ok, metadata = exports["zyke_laptop"]:WearLaptop({metadata = item.metadata}, {hours = 1.0})

#Status

#Get Laptop Status

Returns the status of a laptop that is out, held or put down, by its serial, or nil while it is not out. powered is true while an OS runs on it or it was left on, and players lists the players whose OS runs on it. power holds the battery and whether it runs on mains or a power bank.

Example:

lua
---@param serial string
---@return LaptopStatus? status @ {laptopId, mode, kind, coords?, bucket, owner?, area?, lidOpen, powered, standby, players, dead, service, power}
local status = exports["zyke_laptop"]:GetLaptopStatus(serial)

#Get Laptop Statuses

Returns the status of many laptops in one call, by serial. Laptops that are not out are left out.

Example:

lua
---@param serials string[]
---@return table<string, LaptopStatus> statuses
local statuses = exports["zyke_laptop"]:GetLaptopStatuses({"ZL-7Q2K-9F4M", "ZL-3B8P-1D6X"})

#Get Placed Laptops

Returns the laptops and desktop computers put down right now, and the laptops on vehicle mounts with kind = "vehicle". A laptop waiting to go back to its character's inventory is left out. Every filter field is optional and matches anything when left out.

Example:

lua
---@param filter? {owner?: string, area?: string, kind?: "laptop" | "computer" | "vehicle"}
---@return LaptopPlacedEntry[] laptops @ {serial, laptopId, kind, owner, area?, coords, bucket}
local laptops = exports["zyke_laptop"]:GetPlacedLaptops({area = "mining:serverroom"})

#Ownership

The ownership exports wait while the owners are still loading on start. See Ownership & Recovery.

#Get Laptop Owner

Returns the character identifier of the laptop's registered owner, or nil.

Example:

lua
---@param serial string @ The laptop's serial
---@return string? identifier
local identifier = exports["zyke_laptop"]:GetLaptopOwner(serial)

#Set Laptop Owner

Makes the character the laptop's owner, and clears a stolen report when the owner changes. zyke_laptop:LaptopOwnerChanged names the calling resource as what set it.

Example:

lua
---@param serial string @ The laptop's serial
---@param identifier string @ Character identifier
---@return boolean set
---@return string? err @ "invalid"
local set, err = exports["zyke_laptop"]:SetLaptopOwner(serial, identifier)

#Release Laptop Owner

Leaves the laptop without an owner, so whoever takes it out next claims it. Returns false when it had no owner.

Example:

lua
---@param serial string
---@param origin? string @ Passed on to LaptopOwnerChanged, "release" when left out
---@return boolean released
local released = exports["zyke_laptop"]:ReleaseLaptopOwner(serial)

#Get Character Laptops

Returns the laptops a character owns, newest first. since is when they became the owner, in seconds since the epoch.

Example:

lua
---@param identifier string
---@return {serial: string, stolen: boolean, since: integer}[] laptops
local laptops = exports["zyke_laptop"]:GetCharacterLaptops(identifier)

#Is Laptop Reported Stolen

Returns whether the owner reported the laptop stolen. Nothing in the laptop acts on the flag; it is there for police and pawn shop scripts.

Example:

lua
---@param serial string
---@return boolean stolen
local stolen = exports["zyke_laptop"]:IsLaptopReportedStolen(serial)

#Characters

Registered while Config.Admin.enabled is on in shared/unlocked/admin.lua.

#Purge Laptop Character

Removes what the laptop keeps for a deleted character, or with dryRun only counts it. The character has to be offline. What belongs to them goes: files, Nimbus, Shroud, Natter, mail, crypto, casino, domains, sites, jobs, MDT subject rows, SDK data, and Zykelink. Their laptops and domains are released, their Nimbus drives unlinked, and the rows deleted in one transaction. Shroud handles stay without their creator, and what other players keep stays: messages they received, crypto transfers, SDK payments, department reports, and placed laptops, racks, and routers, which are counted in kept. Yields.

rows counts the rows removed or anonymised by table, or that would be with dryRun. error is "invalid" for an identifier that is empty or over 64 characters, "online" while the character is on the server, and "failed" when the transaction did not go through. Fires zyke_laptop:CharacterPurged unless it was a dry run or refused. The laptop_purge <identifier> command counts, and laptop_purge <identifier> confirm removes.

Example:

lua
---@param identifier string @ Character identifier
---@param options? {dryRun?: boolean}
---@return {ok: boolean, error?: "invalid" | "online" | "failed", dryRun: boolean, rows: table<string, integer>, total: integer, laptops: integer, domains: integer, kept: table<string, integer>} result
local result = exports["zyke_laptop"]:PurgeLaptopCharacter(identifier, {dryRun = true})

print(("%d rows, %d laptops, %d domains"):format(result.total, result.laptops, result.domains))

#Drives & Recovery

These exports take a drive serial. A laptop's serial reads the SSD in that laptop while it is out, or any time while Config.Settings.item is false.

#Get Laptop Logs

Returns a drive's system log, oldest first, or an empty list. kind is boot, signIn, signInFailed, accountCreated, passwordChanged, passwordReset, install, network, cloudLinked, cloudSignedOut, or hack. A hack entry holds the job's target, traceId, outcome, and the hardware serials by slot or port. Yields.

Example:

lua
---@param serial string
---@return LaptopLogEntry[] logs @ {at, kind, device?, username?, os?, ssid?, reason?, target?, traceId?, outcome?, hardware?}
local logs = exports["zyke_laptop"]:GetLaptopLogs(serial)

#Is Laptop Drive Unlocked

Returns whether Crowbar, the recovery toolkit, got past a drive's password, until an OS is installed on it again. Yields.

Example:

lua
---@param serial string
---@return boolean unlocked
local unlocked = exports["zyke_laptop"]:IsLaptopDriveUnlocked(serial)

#Get Laptop Recovery Report

Returns what Crowbar reads off a drive, or nil when no drive has this serial. files and history are left out while the drive is locked, which a Lumen drive is until Crowbar unlocks it. logs and history are newest first. shroudKey is a Shroud sign-in key found in the visible files. Yields.

Example:

lua
---@param serial string
---@return LaptopRecoveryReport? report @ {serial, os?, hostname?, account?, locked, files?, history?, logs, shroudKey?, progress?, canSetPassword}
local report = exports["zyke_laptop"]:GetLaptopRecoveryReport(serial)

#Network & Browsing

See Wi-Fi & Browsing.

#Get Laptop Network

Returns the network the laptop on the player's screen is online through, or nil when they have no laptop on screen, are offline, or are on a Zykelink plan with no time left. kind is phone, hotspot, zykelink, or router. id is phone, the hotspot's id, zykelink, or router:<uid>. coords is the hotspot's position, the router or extender the laptop is nearest to, or the player's for their phone and Zykelink. The range is checked again on every call.

Example:

lua
---@param plyId integer
---@return {kind: "phone" | "hotspot" | "zykelink" | "router", id: string, ssid: string, coords: vector3}? network
local network = exports["zyke_laptop"]:GetLaptopNetwork(plyId)

#Get Browsing Tier

Returns how the player's browser reaches sites right now, or nil while their OS is off. Veil is always onion. The Onion connection on other OSes comes from the client and only counts while the laptop is online.

Example:

lua
---@param plyId integer
---@return "direct" | "vpn" | "onion" | nil tier
local tier = exports["zyke_laptop"]:GetBrowsingTier(plyId)

#Get Site Visit Traces

Returns recorded visits to monitored sites, newest first. Every filter field is optional and all of them have to match. url compares only the host. since and before are seconds since the epoch. limit defaults to 100, at most 500. Yields.

Example:

lua
---@param filter? {url?: string, serial?: string, character?: string, tier?: "direct" | "vpn" | "onion", since?: integer, before?: integer, limit?: integer}
---@return BrowsingTraceRecord[] traces @ {host, url, tier?, character?, serial?, network?, coords?, createdAt}
local traces = exports["zyke_laptop"]:GetSiteVisitTraces({url = "market5xq.onion", since = os.time() - 86400})

#Set Site Monitored

Records visits to the site while browsing.record is "monitored", until the calling resource stops. false stops it. Returns false for an address that can't be read.

Example:

lua
---@param url string
---@param monitored? boolean @ true when left out
---@return boolean changed
local changed = exports["zyke_laptop"]:SetSiteMonitored("market5xq.onion")

#Shroud

Shroud is the onion chat app. The contact exports are covered in full in Shroud Contacts.

#Get Chat Handle

Returns the handle the player is signed in to Shroud with, or nil.

Example:

lua
---@param plyId integer
---@return string? handle
local handle = exports["zyke_laptop"]:GetChatHandle(plyId)

#Get Chat Traces

Returns what senders left behind for a handle, oldest first, from since on. Which fields a trace has depends on the OS the sender ran: Facade and Solace record the character, serial, network, and position, Lumen the serial and network, and Veil nothing. Yields.

Example:

lua
---@param handle string
---@param since? integer @ Seconds since the epoch
---@return ChatTraceRecord[] traces @ {kind, handle, tier, conversationId?, messageId?, character?, serial?, network?, coords?, createdAt}
local traces = exports["zyke_laptop"]:GetChatTraces("ghost", os.time() - 86400)

for _, trace in ipairs(traces) do
    if (trace.network) then print(("ghost was online through %s"):format(trace.network.ssid)) end
end

#Register Chat Room

Adds a room, for example for a gang or a job. listed = false keeps it out of the directory, so only players who know its id can join. Returns false without a valid id and name.

Example:

lua
---@param room {id: string, name: string, description?: string, listed?: boolean}
---@return boolean registered
local registered = exports["zyke_laptop"]:RegisterChatRoom({id = "grove", name = "Grove Street", listed = false})

#Remove Chat Room

Removes a room. Conversations in it stay readable, but nobody can write there until it is back.

Example:

lua
---@param roomId string
---@return boolean removed
local removed = exports["zyke_laptop"]:RemoveChatRoom("grove")

#Register Chat Contact

Adds a contact owned by your resource, such as a dealer or a fixer, with handlers for messages, card buttons, and purchases. Its handle is reserved. See Shroud Contacts.

Example:

lua
---@param contact ChatContactRegistration
---@return boolean registered
local registered = exports["zyke_laptop"]:RegisterChatContact({id = "greenthumb", handle = "greenthumb", name = "Greenthumb"})

#Remove Chat Contact

Removes a contact.

Example:

lua
---@param contactId string
---@return boolean removed
local removed = exports["zyke_laptop"]:RemoveChatContact("greenthumb")

#Set Chat Listings

Replaces a contact's listings and updates the listing messages players have open.

Example:

lua
---@param contactId string
---@param listings ChatListing[]
---@return boolean updated
local updated = exports["zyke_laptop"]:SetChatListings("greenthumb", listings)

#Send Chat Message

Sends a message from a contact to a player's server id or a handle, also while they are offline or signed out. Returns the message id, or nil when the contact, target, or message is invalid or the player blocked the contact. Yields.

Example:

lua
---@param contactId string
---@param target integer | string @ Server id or handle
---@param message string | ChatMessageInput
---@return string? messageId
local messageId = exports["zyke_laptop"]:SendChatMessage("greenthumb", plyId, "Got a run for you.")

#Update Chat Message

Changes a message one of the calling resource's contacts sent, for example to disable a card's buttons. text, listings, location, and frame are replaced, card fields are merged into the card, and false removes a field. Yields.

Example:

lua
---@param messageId string
---@param patch table
---@return boolean updated
local updated = exports["zyke_laptop"]:UpdateChatMessage(messageId, {card = {actions = {{id = "accept_job", label = "Accepted", disabled = true}}}})

#Open Chat

Opens the contact's conversation in the player's Shroud if their screen is on, otherwise tells them a message is waiting. Yields.

Example:

lua
---@param plyId integer
---@param contactId string
---@return boolean sent
local sent = exports["zyke_laptop"]:OpenChat(plyId, "greenthumb")

#Natter

Natter is the everyday messaging app, keyed by the character's phone number. See Messaging.

#Get Messenger Number

Returns the key Natter stores the character's number by and the number as their phone shows it. reason is noPhone when a phone resource runs but they carry no phone or SIM. Yields.

Example:

lua
---@param plyId integer
---@return string? number
---@return string? phone
---@return string? reason
local number, phone, reason = exports["zyke_laptop"]:GetMessengerNumber(plyId)

#Has Messenger Account

Returns whether the number has a Natter account. Yields.

Example:

lua
---@param number string
---@return boolean hasAccount
local hasAccount = exports["zyke_laptop"]:HasMessengerAccount(number)

#Register Messenger Business

Adds a business players can message. id is up to 20 letters, digits, _, or -. Returns its sender, biz:<id>, or nil for an invalid id or name. Replies reach onMessage.

Example:

lua
---@param id string
---@param business {name: string, about?: string, photo?: string, verified?: boolean, onMessage?: fun(plyId: integer, number: string, text?: string, chatId: string)}
---@return string? sender
exports["zyke_laptop"]:RegisterMessengerBusiness("lsc", {
    name = "Los Santos Customs",
    about = "Open all day",
    photo = "web/lsc.webp", -- An https URL or a path inside your resource
    verified = true,
    onMessage = function(plyId, number, text, chatId)
        exports["zyke_laptop"]:SendMessengerMessage("lsc", number, "Thanks, a mechanic will text you back shortly.")
    end,
})

#Send Messenger Message

Sends a message from a business id or the built-in system account (Natter itself). message is a string, or a table with text and at most one of image (an https URL), location, or contact. Returns the message id, or nil when the number has no account or blocked the sender. Yields.

Example:

lua
---@param from string @ "system" or a business id
---@param number string
---@param message string | {text?: string, image?: string, location?: vector3 | {x: number, y: number, z?: number, label?: string}, contact?: {number: string, name?: string}}
---@return string? messageId
local messageId = exports["zyke_laptop"]:SendMessengerMessage("lsc", number, {
    text = "Your car is ready.",
    location = {x = -337.4, y = -136.9, z = 39.0, label = "LS Customs Burton"},
})

#Get Messenger Metadata

Returns who messaged whom and when for a number, newest first, never the content. nil when CanReadMessengerMetadata refuses. since and before are milliseconds since the epoch, and limit defaults to 100, at most 1000. drive is the sending laptop's drive serial, nil for Veil and resources. Yields.

Example:

lua
---@param plyId integer @ The player asking, such as an officer
---@param number string
---@param filter? {since?: integer, before?: integer, limit?: integer}
---@return MessengerMetadataRow[]? rows @ {id, chatId, chatKind, from, to?, kind, drive?, sentAt}
local rows = exports["zyke_laptop"]:GetMessengerMetadata(plyId, number, {limit = 50})

#Casino

Amounts are credits in cents. See Casino.

#Is Casino Enabled

Returns whether the casino is on.

Example:

lua
---@return boolean enabled
local enabled = exports["zyke_laptop"]:IsCasinoEnabled()

#Get Casino Balance

Returns the credits in a wallet, by server id or character identifier, or nil without a wallet or while the casino is off. Yields.

Example:

lua
---@param player integer | string @ Server id or character identifier
---@return integer? balance @ Cents
local balance = exports["zyke_laptop"]:GetCasinoBalance(plyId)

#Adjust Casino Credits

Gives credits (positive) or takes them (negative) outside the games, for example as a reward from another resource. The change is written to the ledger with the reason, the calling resource's name when left out. Yields.

Example:

lua
---@param player integer | string @ Server id or character identifier
---@param amount integer @ Cents, negative to take
---@param reason? string
---@return boolean adjusted
---@return integer | string balanceOrError @ The balance after it, or "disabled", "invalid", "noAccount" or "insufficient"
local adjusted, result = exports["zyke_laptop"]:AdjustCasinoCredits(plyId, 50000, "Event prize")

#Client Local Events

These events are local client notifications. Listen with AddEventHandler on the client. zyke_laptop:Ready only fires on the server; register apps and sites there.

#Screen Opened

Fires when the OS comes on screen.

Example:

lua
---@param mode "handheld" | "placed" | "screen"
AddEventHandler("zyke_laptop:ScreenOpened", function(mode)
    print(mode)
end)

#Screen Closed

Fires when the OS leaves the screen.

Example:

lua
AddEventHandler("zyke_laptop:ScreenClosed", function()
    print("closed")
end)

#App Opened

Fires when the first window of an app opens.

Example:

lua
---@param appId string
AddEventHandler("zyke_laptop:AppOpened", function(appId)
    print(appId)
end)

#App Closed

Fires when the last window of an app closes.

Example:

lua
---@param appId string
AddEventHandler("zyke_laptop:AppClosed", function(appId)
    print(appId)
end)

#Connection Changed

Fires when the OS connects to or loses the Onion network.

Example:

lua
---@param connected boolean
AddEventHandler("zyke_laptop:ConnectionChanged", function(connected)
    print(connected)
end)

#Power Changed

Fires when the OS boots or powers off. serial is the laptop it ran on.

Example:

lua
---@param on boolean
---@param serial? string
AddEventHandler("zyke_laptop:PowerChanged", function(on, serial)
    print(on, serial)
end)

#Devices Changed

Fires when what is in a laptop's ports changes, as this client sees it. devices is what the client GetLaptopDevices export returns, nil once the client no longer sees that laptop.

Example:

lua
---@param ownerId integer @ Laptop id
---@param devices? table<string, {type: string, item: string}>
AddEventHandler("zyke_laptop:DevicesChanged", function(ownerId, devices)
    print(ownerId, json.encode(devices))
end)

#Service Started

Fires when the player opens the service view of a laptop.

Example:

lua
---@param ownerId integer @ Laptop id
AddEventHandler("zyke_laptop:ServiceStarted", function(ownerId)
    print(ownerId)
end)

#Service Ended

Fires when the service view closes.

Example:

lua
---@param ownerId integer @ Laptop id
AddEventHandler("zyke_laptop:ServiceEnded", function(ownerId)
    print(ownerId)
end)

#Activity Changed

Fires when the player's activity changes, with the value GetActivity returns, nil when they are done with the laptop.

Example:

lua
---@param activity? "holding" | "using" | "screen" | "ports" | "servicing" | "placing" | "racking"
AddEventHandler("zyke_laptop:ActivityChanged", function(activity)
    print(activity)
end)

#Notification Action

Fires when the player clicks a notification sent with the client Notify export, or one of its actions. resource is the resource that sent it, so check it is yours. actionId is nil for a click on the notification itself, and data is what the notification carried.

Example:

lua
---@param resource string
---@param notificationId? string
---@param actionId? string
---@param data? any
AddEventHandler("zyke_laptop:NotificationAction", function(resource, notificationId, actionId, data)
    if (resource ~= GetCurrentResourceName() or actionId ~= "collect") then return end

    print(data.orderId)
end)

#Server Local Events

These events are local server integration notifications. They are not network event entry points and cannot block an action; use the Can-Check Hooks for that. Listen with AddEventHandler on the server.

#Ready

Fires once the placed laptops of the last run are restored on start. The laptop forgets other resources' apps, sites, device types, and site blocks when it restarts, so register them again here. Laptop ids are new on every start, so resources that track placed laptops read them again here too.

Example:

lua
local function register()
    exports["zyke_laptop"]:RegisterApp({id = "my_app", label = "Dispatch", ui = "web/app.html"})
end

AddEventHandler("zyke_laptop:Ready", function()
    register()

    local laptops = exports["zyke_laptop"]:GetPlacedLaptops()
end)

if (GetResourceState("zyke_laptop") == "started") then register() end

#Server Ready

Fires once the SDK's database tables exist on start, after every module that creates them is done.

Example:

lua
AddEventHandler("zyke_laptop:ServerReady", function()
    print("zyke_laptop SDK ready")
end)

#Network Changed

Fires when the laptop on the player's screen joins another network, as GetLaptopNetwork returns it, or goes offline or off screen, network nil.

Example:

lua
---@param plyId integer
---@param network? {kind: "phone" | "hotspot" | "zykelink" | "router", id: string, ssid: string, coords: vector3}
AddEventHandler("zyke_laptop:NetworkChanged", function(plyId, network)
    print(plyId, network and network.ssid)
end)

#Laptop Drive Unlocked

Fires when Crowbar gets past a drive's password. serial is the drive's. plyId is the player whose toolkit worked on it, nil when none did.

Example:

lua
---@param serial string
---@param plyId? integer
AddEventHandler("zyke_laptop:LaptopDriveUnlocked", function(serial, plyId)
    print(serial, plyId)
end)

#Chat Message Sent

Fires when a player sends a message in Shroud. trace holds what the sender's OS records, with tier and only the fields that OS records.

Example:

lua
---@param handle string
---@param conversationId string
---@param kind "direct" | "room"
---@param trace ChatTrace
AddEventHandler("zyke_laptop:ChatMessageSent", function(handle, conversationId, kind, trace)
    print(handle, kind, trace.character)
end)

#Messenger Message Sent

Fires when a message is sent in Natter, with the metadata row GetMessengerMetadata returns, never its content.

Example:

lua
---@param row MessengerMetadataRow @ {id, chatId, chatKind, from, to?, kind, drive?, sentAt}
AddEventHandler("zyke_laptop:MessengerMessageSent", function(row)
    print(row.from, row.to, row.kind)
end)

#Site Visited

Fires when a player opens a monitored site and their tier records something. url is the address without its scheme.

Example:

lua
---@param host string
---@param url string
---@param trace {tier: "direct" | "vpn" | "onion", character?: string, serial?: string, network?: table, coords?: vector3}
AddEventHandler("zyke_laptop:SiteVisited", function(host, url, trace)
    print(host, trace.tier, trace.character)
end)

#Device Plugged

Fires when a device goes into a port, through the port view or PlugDevice. ownerId is the laptop id, serial the laptop's, and plyId the player who plugged it in.

Example:

lua
---@param device LaptopDeviceInfo @ {ownerId, serial, plyId, port, kind, side, type, item, metadata, plugId}
AddEventHandler("zyke_laptop:DevicePlugged", function(device)
    print(device.serial, device.port, device.item)
end)

#Device Unplugged

Fires when a device comes out of a port, with the same fields. plyId is the player who pulled it out.

Example:

lua
---@param device LaptopDeviceInfo
AddEventHandler("zyke_laptop:DeviceUnplugged", function(device)
    print(device.serial, device.port, device.item)
end)

#Laptop Owner Changed

Fires when a laptop gets another owner, identifier nil once released. origin is shop, den (a Digital Den sale), claim, admin, character, release, purge, or the resource that called the export.

Example:

lua
---@param serial string
---@param identifier? string
---@param origin string
AddEventHandler("zyke_laptop:LaptopOwnerChanged", function(serial, identifier, origin)
    print(serial, identifier, origin)
end)

#Cloud Device Linked

Fires when a drive is linked to a character's Nimbus account.

Example:

lua
---@param identifier string
---@param drive string
AddEventHandler("zyke_laptop:CloudDeviceLinked", function(identifier, drive)
    print(identifier, drive)
end)

#Cloud Device Signed Out

Fires when a drive leaves a Nimbus account.

Example:

lua
---@param identifier string
---@param drive string
---@param reason "removed" | "reinstalled" | "mode" | "repair" | "recovery" | "user"
AddEventHandler("zyke_laptop:CloudDeviceSignedOut", function(identifier, drive, reason)
    print(identifier, drive, reason)
end)

#Part Installed

Fires when a part goes into a laptop, from the service view or SetLaptopHardwareSlot.

Example:

lua
---@param plyId integer
---@param serial string @ The laptop's
---@param slotId string
---@param item {name: string, metadata: table}
AddEventHandler("zyke_laptop:PartInstalled", function(plyId, serial, slotId, item)
    print(serial, slotId, item.name)
end)

#Part Removed

Fires when a part comes out of a laptop, from the service view or SetLaptopHardwareSlot.

Example:

lua
---@param plyId integer
---@param serial string
---@param slotId string
---@param item {name: string, metadata: table}
AddEventHandler("zyke_laptop:PartRemoved", function(plyId, serial, slotId, item)
    print(serial, slotId, item.name)
end)

#Laptop Serviced

Fires when a laptop's fan is cleaned (clean), its processor re-pasted (repaste), or its battery restored (battery, from RepairLaptop only).

Example:

lua
---@param plyId integer
---@param serial string
---@param kind "clean" | "repaste" | "battery"
AddEventHandler("zyke_laptop:LaptopServiced", function(plyId, serial, kind)
    print(serial, kind)
end)

#Part Worn Out

Fires when wear turns an SSD read-only (ssd) or a memory stick faulty (ram0, ram1), from normal use or WearLaptop. A copy passed to WearLaptop as {metadata} or {hardware} does not fire it.

Example:

lua
---@param serial string
---@param slotId "ssd" | "ram0" | "ram1"
AddEventHandler("zyke_laptop:PartWornOut", function(serial, slotId)
    print(serial, slotId)
end)

#Laptop Power Changed

Fires when a laptop powers on or off: an OS started or stopped on it, or it was left on or went off. A laptop put away counts as off. Changes are noticed within about 10 seconds.

Example:

lua
---@param serial string
---@param on boolean
AddEventHandler("zyke_laptop:LaptopPowerChanged", function(serial, on)
    print(serial, on)
end)

#Laptop Dead Changed

Fires when a laptop that is out can no longer power on, or can again. A dead laptop has no board, no working memory, or a flat battery off mains, and its screen stays black.

Example:

lua
---@param serial string
---@param dead boolean
AddEventHandler("zyke_laptop:LaptopDeadChanged", function(serial, dead)
    print(serial, dead)
end)

#Laptop Placed

Fires when a player puts a laptop down. area is the placement area it went into, nil outside every area.

Example:

lua
---@param plyId integer
---@param serial string
---@param coords vector4 @ w is the heading
---@param bucket integer
---@param area? string
AddEventHandler("zyke_laptop:LaptopPlaced", function(plyId, serial, coords, bucket, area)
    print(serial, coords, bucket, area)
end)

#Laptop Picked Up

Fires when a player picks up a placed laptop. placedBy is the character who put it down.

Example:

lua
---@param plyId integer
---@param serial string
---@param coords vector4
---@param bucket integer
---@param placedBy string
---@param area? string
AddEventHandler("zyke_laptop:LaptopPickedUp", function(plyId, serial, coords, bucket, placedBy, area)
    print(serial, placedBy, area)
end)

#Computer Placed

Fires when a desktop computer is set up. owner is the character it was set up for.

Example:

lua
---@param plyId integer
---@param serial string
---@param coords vector4
---@param bucket integer
---@param owner string
AddEventHandler("zyke_laptop:ComputerPlaced", function(plyId, serial, coords, bucket, owner)
    print(serial, owner)
end)

#Computer Removed

Fires when a desktop computer is taken away.

Example:

lua
---@param plyId integer
---@param serial string
---@param coords vector4
---@param bucket integer
---@param owner string
AddEventHandler("zyke_laptop:ComputerRemoved", function(plyId, serial, coords, bucket, owner)
    print(serial, owner)
end)

#Placed Laptop Expired

Fires when placement.removeAfterDays removes a placed laptop nobody used. Its item's metadata is printed to the server console.

Example:

lua
---@param serial string
---@param placedBy string
---@param coords vector4
---@param bucket integer
---@param days number
---@param area? string
AddEventHandler("zyke_laptop:PlacedLaptopExpired", function(serial, placedBy, coords, bucket, days, area)
    print(serial, placedBy, days)
end)

#Laptop Shop Bought

Fires after a purchase at the shop. offerId is such as laptop:performance:midnight and total is the money paid. A new laptop bought with a backplate fires once for the laptop and once for the skin:<id> offer, each with its own price.

Example:

lua
---@param plyId integer
---@param offerId string
---@param total integer
AddEventHandler("zyke_laptop:LaptopShopBought", function(plyId, offerId, total)
    print(plyId, offerId, total)
end)

#Casino Deposited

Fires when dollars go from the bank into a casino wallet. balance is the wallet's credits after it, in cents.

Example:

lua
---@param plyId integer
---@param identifier string
---@param dollars integer
---@param balance integer
AddEventHandler("zyke_laptop:CasinoDeposited", function(plyId, identifier, dollars, balance)
    print(identifier, dollars, balance)
end)

#Casino Withdrawn

Fires when credits are paid out to the bank.

Example:

lua
---@param plyId integer
---@param identifier string
---@param dollars integer
---@param balance integer
AddEventHandler("zyke_laptop:CasinoWithdrawn", function(plyId, identifier, dollars, balance)
    print(identifier, dollars, balance)
end)

#Casino Credits Adjusted

Fires when another resource changes a wallet with AdjustCasinoCredits.

Example:

lua
---@param identifier string
---@param cents integer @ Negative when taken
---@param reason string
---@param resource? string @ The resource that called the export
---@param balance integer
AddEventHandler("zyke_laptop:CasinoCreditsAdjusted", function(identifier, cents, reason, resource, balance)
    print(identifier, cents, reason, resource, balance)
end)

#Laptop Reported Stolen

Fires when the owner reports a laptop stolen or withdraws the report.

Example:

lua
---@param plyId integer
---@param serial string
---@param stolen boolean
AddEventHandler("zyke_laptop:LaptopReportedStolen", function(plyId, serial, stolen)
    print(serial, stolen)
end)

#Laptop Password Reset

Fires when the installer stick of a drive's own OS sets a new password on its account.

Example:

lua
---@param plyId integer
---@param serial string @ The laptop's
---@param drive string
AddEventHandler("zyke_laptop:LaptopPasswordReset", function(plyId, serial, drive)
    print(serial, drive)
end)

#Laptop OS Installed

Fires when an installer stick erases a drive and installs its OS.

Example:

lua
---@param plyId integer
---@param serial string
---@param drive string
---@param osId "facade" | "lumen" | "solace"
AddEventHandler("zyke_laptop:LaptopOsInstalled", function(plyId, serial, drive, osId)
    print(serial, drive, osId)
end)

#Laptop Admin Command

Fires when laptop_owner, laptop_charge, laptop_computer, the vehicle mount command (laptopmounts by default), laptop_site_takedown, laptop_site_lock, or laptop_site_unlock is used, and for the logged staff actions in Marshal, the staff app. plyId is 0 from the server console, and result is the locale key the command answered with. A Marshal action comes as marshal:<action> with args {target, reason} and result done or the error code.

Example:

lua
---@param plyId integer
---@param command string
---@param args string[]
---@param result string
AddEventHandler("zyke_laptop:LaptopAdminCommand", function(plyId, command, args, result)
    print(plyId, command, result)
end)

#Cast Screen Saved

Fires when the screen creator places a screen or changes one.

Example:

lua
---@param plyId integer
---@param screenId string
---@param label string
---@param bucket integer
---@param isNew boolean
AddEventHandler("zyke_laptop:CastScreenSaved", function(plyId, screenId, label, bucket, isNew)
    print(screenId, label, isNew)
end)

#Cast Screen Deleted

Fires when the screen creator deletes a screen.

Example:

lua
---@param plyId integer
---@param screenId string
AddEventHandler("zyke_laptop:CastScreenDeleted", function(plyId, screenId)
    print(screenId)
end)

#Applicant Hired

Fires when an employer hires an applicant on the Hireline job board. Hiring does not change the player's job; set it from here. See Job Board.

Example:

lua
---@param application {id: integer, listingId: integer, listingTitle: string, job: string, identifier: string, applicantName: string, hiredBy: string, hiredAt: integer}
AddEventHandler("zyke_laptop:jobs:ApplicantHired", function(application)
    print(application.identifier, application.job, application.hiredBy)
end)

#Police Application Reviewed

Fires when a reviewer accepts or declines an application in the example LSPD Recruitment app, with the application and the applicant's identifier. Use it to hire accepted applicants. See Example Sites.

Example:

lua
---@param application {id: integer, name: string, status: "accepted" | "declined", identifier: string, note?: string, reviewerName?: string, reviewedAt?: integer}
AddEventHandler("zyke_laptop:examples:police:ApplicationReviewed", function(application)
    if (application.status ~= "accepted") then return end

    print(("Hire %s"):format(application.identifier))
end)

#Character Purged

Fires after PurgeLaptopCharacter removed a character's data, with the result the export returns. result.ok is false and error "failed" when the transaction did not go through.

Example:

lua
---@param identifier string
---@param result {ok: boolean, error?: string, dryRun: boolean, rows: table<string, integer>, total: integer, laptops: integer, domains: integer, kept: table<string, integer>}
AddEventHandler("zyke_laptop:CharacterPurged", function(identifier, result)
    print(identifier, result.ok, result.total)
end)

Fires when a player buys Zykelink time on zykelink.com. planKey is account:<identifier> for a Nimbus account's plan or laptop:<serial> for a laptop's. offer.kind is pack with hours, or week with days.

Example:

lua
---@param plyId integer
---@param planKey string
---@param offer {kind: "pack" | "week", hours?: integer, days?: integer, price: integer}
AddEventHandler("zyke_laptop:ZykelinkBought", function(plyId, planKey, offer)
    print(planKey, offer.kind, offer.price)
end)

#Data Stick Changed

Fires when the files on a data stick change. by is "player" when a player changed them on a laptop, or the name of the resource that changed them through the data stick exports.

Example:

lua
---@param serial string @ The stick's serial
---@param by string
AddEventHandler("zyke_laptop:DataStickChanged", function(serial, by)
    local content = exports["zyke_laptop"]:ReadDataStickFile(serial, "found/codes.txt")
end)

#Hack Trace

Fires when an intrusion job starts, with reason started, and again with the result when it ends, both times with the same trace.id. Which fields are filled depends on the player's OS, VPN, and Onion connection and on Config.Hacking.traces. The OnHackTrace hook in server/unlocked/hooks.lua gets the same, for police alerts.

Example:

lua
---@param plyId integer
---@param trace LaptopHackTrace @ {id, target, resource, mode, game, os, tier, reason, success?, createdAt, character?, serial?, network?, coords?, hardware?}
AddEventHandler("zyke_laptop:HackTrace", function(plyId, trace)
    if (trace.reason ~= "started") then print(trace.target, trace.success) end
end)

#Payment Settled

Fires when an SDK payment's money reached the payee, and again when staff resolve a payment in review as settled. Fulfil the order here and mark it with MarkPaymentFulfilled.

Example:

lua
---@param payment SdkPayment @ {id, resource, app, identifier, reference?, label, amount, currency, method?, coin?, payee, status, refunded, fulfilled, lines?, metadata?, createdAt, expiresAt, settledAt?}
AddEventHandler("zyke_laptop:PaymentSettled", function(payment)
    if (payment.resource ~= GetCurrentResourceName()) then return end

    exports["zyke_laptop"]:MarkPaymentFulfilled(payment.id)
end)

#Payment Failed

Fires when an SDK payment fails. code is failed when the payee could not be paid and the player got the money back, or review when staff resolve a payment in review as failed or returned.

Example:

lua
---@param payment SdkPayment
---@param code "failed" | "review"
AddEventHandler("zyke_laptop:PaymentFailed", function(payment, code)
    print(payment.id, code)
end)

#Payment Refunded

Fires when RefundPayment paid money back. amount is this refund's, and payment.refunded the total refunded so far.

Example:

lua
---@param payment SdkPayment
---@param amount integer
---@param reason? string
AddEventHandler("zyke_laptop:PaymentRefunded", function(payment, amount, reason)
    print(payment.id, amount, reason)
end)

#Payment Review

Fires when a payment is put in the review state: one the server stopped in the middle of, found on start, or one whose licence or online order could not be written after it settled. The money may have moved without the payee getting it, so staff check it by hand in Marshal.

Example:

lua
---@param payment SdkPayment
AddEventHandler("zyke_laptop:PaymentReview", function(payment)
    print(payment.id, payment.resource, payment.amount)
end)

#Room Event

Fires when a player joins or leaves an SDK room of an app. data is the member data the player joined with.

Example:

lua
---@param appId string
---@param room string
---@param kind "join" | "leave"
---@param plyId integer
---@param data any
AddEventHandler("zyke_laptop:RoomEvent", function(appId, room, kind, plyId, data)
    if (appId == "my_app" and kind == "join") then exports["zyke_laptop"]:SendRoomMessage(appId, room, {welcome = true}, plyId) end
end)

#Feature Events

The apps and services below fire their own events, named zyke_laptop:<area>:<Event>. The arguments are listed in order, ? marks one that can be nil, and the tables are described on the feature's own page.

Most of them are listed on the page of the feature that fires them:

The rest are below.

#Crypto

See Crypto & Payments.

EventArgumentsFires when
crypto:Bought, crypto:SoldplyId?, orderAn order fills, {identifier, orderId, coin, units, amount, fiat, fee, account}. plyId is nil when the player left before it filled.
crypto:SentplyId, transferA player sends coins, with its transferId, priority, and confirmAt.
crypto:ConfirmedtransferA transfer confirms, {id, kind, coin, units, amount, feeUnits, sender, recipient, resource, note, confirmedAt}, kind being send, spend, or payout.
crypto:Charged, crypto:Givenidentifier, result, resource, reasonChargeCrypto or GiveCrypto moved coins.
crypto:PricesUpdatedsnapshotThe prices move, every tick.

#DMV and Routers

EventArgumentsFires when
dmv:PlateChangedplyId, receiptA plate change is finished and paid.
routers:Placed, routers:PickedUpplyId, uid, kindA router or extender (kind) is set down or picked up.

#Discord Logs

The laptop sends Discord logs through zyke_lib. Set a webhook URL per action in zyke_lib/webhooks/laptop.lua; an action left empty sends nothing. Each log names who did it, or the server, and carries its raw data. The texts are the logs: keys in the locale files. See Discord Webhooks / Logs.

server/unlocked/webhooks.lua sends them. It only listens to the server events on this page, so change a log there freely, or listen to the same events from your own resource.

ActionEventLogged when
LaptopPlacedLaptopPlaced, ComputerPlacedA player puts a laptop down or sets a desktop computer up.
LaptopPickedUpLaptopPickedUp, ComputerRemovedA player picks up a placed laptop or takes a desktop computer away.
LaptopExpiredPlacedLaptopExpiredplacement.removeAfterDays removes an unused placed laptop.
ShopPurchaseLaptopShopBoughtSomething is bought at the shop, with the offer and what was paid.
ZykelinkPurchaseZykelinkBoughtZykelink time is bought, with the plan and the price.
DomainPurchasedomains:Registered, domains:Renewed, domains:BoughtOutA domain is bought, rented, granted, renewed, or bought out.
DomainReleaseddomains:ReleasedA domain is released.
DomainTransferreddomains:OwnerChangedA domain gets another owner.
SitePublishedsites:PublishedA Sitewright site is published.
SiteEditorChangedsites:EditorChangedA site editor is added, changed, or removed.
SiteModeratedsites:TakenDown, sites:TakedownLiftedStaff take a site down or lock it, or lift the takedown.
MdtReportmdt:ReportSubmitted, mdt:ReportReviewedA report is submitted, approved, or returned.
MdtBulletinmdt:BulletinPostedA bulletin is posted.
MdtStatusmdt:StatusChangedA unit changes its status.
MdtWarrantmdt:WarrantRequested, mdt:WarrantDecided, mdt:WarrantServedA warrant is requested, decided, or served.
MdtBolomdt:BoloPostedA BOLO is posted.
MdtChargesmdt:ChargesProcessedCharges are processed.
MdtImpoundmdt:VehicleImpoundedA vehicle is impounded.
MdtLicensemdt:LicenseRevokedA licence is revoked.
MdtCasemdt:CaseOpened, mdt:CaseClosedA case file is opened or closed.
MdtEvidencemdt:DeviceSeized, mdt:DriveImaged, mdt:ImageUnlocked, mdt:DeviceReleasedA device is seized, imaged, unlocked, or released.
MdtCareBillmdt:CareBilledA patient is billed for care.
MdtBoardmdt:PreAlertSent, mdt:PatientAdmitted, mdt:PatientDischargedA pre-alert is sent, or a patient admitted or discharged.
MdtPrescriptionmdt:PrescriptionWrittenA prescription is written.
MdtCertificatemdt:CertificateIssuedA death certificate is issued.
MdtOrdermdt:OrderBilled, mdt:OrderPaid, mdt:OrderApprovalAnsweredA repair order is billed or paid, or the customer answers an approval.
MdtTowmdt:TowRequestedA tow is requested.
MdtRoadworthymdt:RoadworthinessIssuedA roadworthiness certificate is issued.
MdtCataloguemdt:CatalogueChangedA workshop catalogue entry is saved or deleted.
MdtCallmdt:CallCreated, mdt:CallClosedA dispatch call is created or closed.
CasinoDepositCasinoDepositedDollars go from the bank into a casino wallet.
CasinoWithdrawCasinoWithdrawnCredits are paid out to the bank.
CasinoAdjustedCasinoCreditsAdjustedAnother resource changes a wallet with AdjustCasinoCredits.
CryptoTradecrypto:Bought, crypto:SoldA crypto order fills.
CryptoTransfercrypto:SentA player sends coins.
CryptoAdjustedcrypto:Charged, crypto:GivenA resource charges or gives coins with ChargeCrypto or GiveCrypto.
OwnerChangedLaptopOwnerChangedA laptop gets an owner or loses one.
ReportedStolenLaptopReportedStolenThe owner reports a laptop stolen or withdraws the report.
DriveUnlockedLaptopDriveUnlockedCrowbar gets past a drive's password.
PasswordResetLaptopPasswordResetAn installer stick resets a drive's password.
OsInstalledLaptopOsInstalledAn installer stick erases a drive and installs its OS.
PartInstalledPartInstalledA part goes into a laptop.
PartRemovedPartRemovedA part comes out of a laptop.
AdminActionLaptopAdminCommand, CastScreenSaved, CastScreenDeletedAn admin command or Marshal action is used, or the screen creator places, changes, or deletes a screen.
RackPlacedracks:PlacedA player sets a rack down.
RackRemovedracks:PickedUp, racks:RemovedA player picks a rack up, or RemoveRack takes one away.
RackLaptopracks:LaptopInserted, racks:LaptopRemovedA laptop goes into a rack slot or comes out.
SdkPaymentPaymentSettledAn SDK payment settles.
SdkRefundPaymentRefundedAn SDK payment is refunded.
SdkPaymentReviewPaymentReviewAn SDK payment is put in review.

For custom permission rules within the laptop itself, use Can-Check Hooks.