Documentation

Config

Explanations and examples for the current laptop configuration.

The laptop is configured in several files under zyke_laptop/shared/unlocked/ and one under zyke_laptop/server/unlocked/:

FileTableWhat it holds
shared/unlocked/config.luaConfig.SettingsEvery setting of the laptop itself, from the item and props to the apps and sites built into it.
shared/unlocked/shop.luaConfig.ShopThe Digital Den shop: its counter, staff shelves, door locks, display laptops and everything it sells.
shared/unlocked/hardware.luaConfig.PrebuiltsThe laptops as they leave the factory, the parts each one is built with.
shared/unlocked/hotspots.luaConfig.HotspotsThe public Wi-Fi hotspots on the map.
server/unlocked/hotspots.luaHotspotPasswordsThe passwords of the secured hotspots. This file only runs on the server.
shared/unlocked/examples.luaConfig.ExamplesThe example sites that ship with the laptop.
shared/unlocked/casino/*.luaConfig.CasinoOne file per casino game, plus the case catalogue and its items.

Distances are metres. Durations are in the unit their key names, such as Ms for milliseconds, Seconds, Minutes, Hours or Days, unless the entry says otherwise. Sizes in bytes are bytes, shop prices are whole dollars and casino amounts are credits.

The interact keybind can be changed in GTA / FiveM settings. Framework, inventory, target, phone, language and permission settings live in zyke_lib. Discord logs are set per action in zyke_lib/webhooks/laptop.lua; an empty webhook sends nothing.

Rules of your own, such as keeping a hotspot for a job or refusing a placement spot, go in the hooks in client/unlocked/hooks.lua and server/unlocked/hooks.lua. See Can-Check Hooks.

#Full Default Config

#General

#debug

Registers the laptop_hold command in the F8 console, which tries other hold poses live and prints the lines to paste into handheld. Leave it off on a live server.

Example:

lua
debug = false

#item

Usable inventory item that takes the laptop out. Using it again puts the laptop away. The item definition is in extras/items/.

Set it to false for no laptop item. Laptops then only open through exports such as OpenScreen, and each character has one laptop of their own, with its parts and ports kept in the server's KVP by its serial. Without a laptop item:

  • desktop computers are off, they need the laptop item,
  • the shop sells no laptops, refinishes or backplates,
  • every skin is free to pick in Settings,
  • only the character can pick up their own laptop once it is put down.

Example:

lua
item = "laptop"

#interaction

How players use a laptop or computer that is put down, browse the shop, open the staff shelves and read the spec signs of the display laptops.

  • "point" puts a zyke_lib interest point on the middle of the keyboard, the shop keeper, the shelves and the signs. The mouse wheel picks an option and E does it.
  • "target" lists the same options in the target menu, through ox_target or qb-target. Without either running it falls back to interest points and prints a warning in the F8 console.

Example:

lua
interaction = "point"

#clock

Time the OS clocks, lock screens, calendars and the terminal's date show.

  • "game" follows the game's clock, as your time script sets it, frozen time included, on the real date. Every player sees the same day.
  • "real" shows each player's own computer time.

Example:

lua
clock = "game"

#renderDistance

Metres from a laptop other players still see it. The display laptops in the shop also only exist within this distance.

Example:

lua
renderDistance = 60.0

#Models & Lid

#models.base

Prop of the laptop's base, the keyboard half. The finishes, ports and screen are built into the laptop's own props, so only change it for a replacement built the same way.

Example:

lua
base = "zyke_laptop_base"

#models.lid

Prop of the laptop's lid. The same note applies as for models.base.

Example:

lua
lid = "zyke_laptop_lid"

#skins

The covers for the back of the lid, listed by category in the order Settings and the shop studio show them, after the stock lid. The default list has 115 skins in 12 categories: solid, gradient, pattern, camo, material, animal, art, nature, space, tech, street and retro.

  • Delete an id to take that skin away. Laptops that had it show the stock lid again.
  • Move ids or whole categories to reorder them. A category without ids is hidden.
  • To add a skin, stream a model named zyke_laptop_skin_<id>, put a <id>.webp thumbnail in nui/skins/, add a "shell:skin:<id>" label to every locales/*.lua file and list the id under a category. A new category needs a "shell:skinCategory:<id>" label too.

The skin is stored in the laptop item's metadata, so it goes with the laptop. Whether players buy skins at the shop or pick them for free is set by skins.bought in Config.Shop. CanUseLaptopSkin in server/unlocked/hooks.lua can keep skins for a job or donors. See Using the Laptop and Shop.

Example:

lua
skins = {
    {category = "camo", skins = {"camo_urban", "camo_woodland", "camo_digital"}},
    {category = "material", skins = {"carbon", "walnut", "marble"}},
}

#finishes

The anodised colours a laptop's chassis can have, next to the stock graphite: silver, black, midnight, sky_blue, sage, gold, rose_gold, red and lavender. The colours are built into the props, so only these ids work.

Players don't pick a finish themselves. A laptop gets one at the shop, or from another resource through the SetLaptopFinish server export. Delete an id to take that finish away; laptops that had it show graphite again, and the shop stops selling it.

Example:

lua
finishes = {"silver", "black", "midnight", "sky_blue", "sage", "gold", "rose_gold", "red", "lavender"}

#lid.openAngle

Degrees the lid opens to, measured from closed. The hold animation is made for the default of 110 degrees.

Example:

lua
openAngle = 110.0

#lid.openDuration

Milliseconds the lid takes to swing open.

Example:

lua
openDuration = 900

#lid.closeDuration

Milliseconds the lid takes to swing shut.

Example:

lua
closeDuration = 650

#Handheld

Holding the open laptop in the left hand. The default animation is made for this laptop: the take out, the idle and the put away are one clip, played on the upper body only so the legs still walk, and it fits both freemode peds.

#handheld.anim

Animation played while the laptop is held. flag = 50 plays it on the upper body and keeps it looping.

Example:

lua
anim = {dict = "zyke_laptop_held", clip = "hold", flag = 50}

#handheld.bone

Ped bone the laptop is attached to, the left hand's prop bone.

Example:

lua
bone = 60309

#handheld.offset

Offset of the laptop from the bone, in metres. The bone's X runs to the fingertips. The default clip is made around this offset. With debug on, laptop_hold tries other values live.

Example:

lua
offset = vec3(0.1439, -0.0825, 0.0528)

#handheld.rotation

Rotation of the laptop from the bone, in degrees.

Example:

lua
rotation = vec3(-51.9, -40.65, -51.86)

#handheld.disableSprint

Keeps the player walking while they hold the laptop. Sprinting and jumping would break the pose.

Example:

lua
disableSprint = true

#Placement

A laptop put down leaves the inventory and stays where it is, through disconnects and resource and server restarts, until someone picks it up. It is kept in the zyke_laptop_placed table, created on start, and comes back powered off, charged for the time it stood there. Anyone can use a placed laptop; its account's password is what keeps it safe. See Using the Laptop.

#placement.maxDistance

Metres from the player a laptop can be put down.

Example:

lua
maxDistance = 2.0

#placement.maxPerCharacter

Laptops one character can have put down at once, outside the placement areas. false for no limit.

Example:

lua
maxPerCharacter = 30

#placement.areas

Places with a laptop limit of their own, such as a server room in an MLO, a warehouse or a housing interior. Laptops put down in an area count only against that area's limit, never against maxPerCharacter or another area's. A character can fill a server room with a hundred and still put thirty down elsewhere. The first area that matches is used.

FieldDescription
idUnique text of up to 64 characters. It is stored with each laptop put down in the area.
coordsCentre of the sphere or box, a vec3.
radiusMakes the area a sphere of this many metres around coords.
sizeMakes the area a box of this size in metres, a vec3, for a room.
headingOptional turn of the box in degrees.
bucketsOptional list of routing buckets the area is limited to, any bucket when left out. An area with buckets and no shape covers the whole of those buckets, for housing that puts each property in a bucket of its own.
limitLaptops one character may have put down in the area, false or left out for no limit.

The server can't see interiors, so an MLO's room is matched by its coordinates. Laptops only stay put down across restarts in persistentBuckets, so add an area's buckets there too. An area with a broken entry is left out with a warning in the server console.

For your own rules, such as asking a housing or mining script, replace GetLaptopPlacementArea(plyId, coords, bucket) in server/unlocked/hooks.lua. It returns the area id and its limit. See Can-Check Hooks.

Example:

lua
areas = {
    {id = "serverroom", coords = vec3(1003.0, -3108.0, -39.0), size = vec3(20.0, 12.0, 6.0), limit = 100},
    {id = "warehouse", coords = vec3(-80.0, -1800.0, 28.0), radius = 25.0, limit = 60},
    {id = "apartments", buckets = {1001, 1002, 1003}, limit = 50},
}

#placement.ownerOnlyPickUp

Only the laptop's owner can pick it up, or the character who put it down when it has no owner. Off, anyone can carry a laptop off, as the recovery toolkit expects of stolen laptops. Without a laptop item only the owner can, either way. See Ownership & Recovery.

Example:

lua
ownerOnlyPickUp = false

#placement.removeAfterDays

Days a placed laptop nobody used or opened stays before it is removed, with its item and whatever is plugged into it. Each removal prints a line in the server console with the laptop's serial, owner, place and item metadata, enough to give it back. Checked on start and every hour. Desktop computers are never removed this way. false keeps laptops until someone picks them up.

Example:

lua
removeAfterDays = false

#placement.persistentBuckets

Routing buckets laptops stay put down in across restarts. Housing scripts hand out instance buckets whose numbers are reused, so a laptop left in one could stand in a stranger's instance later.

A laptop put down in any other bucket stays only while the character who put it down is in that bucket, and the player is told it is temporary. Once they leave or disconnect, or after a restart, its item goes back into their inventory, at their next login when they are offline. Desktop computers follow the same rule.

Add the buckets your housing keeps per property, or decide per bucket in IsLaptopBucketPersistent(bucket) in server/unlocked/hooks.lua.

Example:

lua
persistentBuckets = {0}

#placement.clearProps.permission

Who clears map props from around a laptop put down or a computer set up, such as the monitor, keyboard and mouse already on a police station desk. An ACE permission or ESX group, true for everyone, false for nobody. While the spot is picked the props that will go are outlined, and they stay hidden for everyone until the laptop is picked up or the computer removed. Set clearProps = false to turn clearing off altogether.

Example:

lua
permission = "command"

#placement.clearProps.radius

Metres around the spot the listed props are cleared from.

Example:

lua
radius = 1.0

#placement.clearProps.models

Map prop models that are cleared. Only these go; the laptop can still stand on any other prop, such as the desk. The default list has 101 models in five groups: monitors, keyboards, mice and mouse mats, computer towers, and laptops. Add the models of an MLO's own desk equipment to clear those too.

Example:

lua
models = {
    -- Monitors
    "prop_monitor_01a", "prop_monitor_01b", "prop_monitor_02",
    -- Keyboards
    "prop_keyboard_01a", "prop_keyboard_01b",
    -- Mice and mouse mats
    "prop_mouse_01", "v_res_mousemat",
}

#placement.maxSlope

Degrees a surface may tilt for the laptop to stand on it.

Example:

lua
maxSlope = 12.0

#placement.lowHeight

Metres above the player's feet that count as the floor. A laptop or computer below it uses the low animations to put down and pick up, and the player kneels to use it.

Example:

lua
lowHeight = 0.5

#placement.lowAnim

Animation for putting a laptop or computer down on the floor. duration is in milliseconds.

Example:

lua
lowAnim = {dict = "pickup_object", clip = "putdown_low", duration = 1000}

#placement.highAnim

Animation for putting a laptop or computer down at table height.

Example:

lua
highAnim = {dict = "mp_common", clip = "givetake1_a", duration = 900}

#placement.lowPickupAnim

Animation for picking a laptop up from the floor, or removing a computer there.

Example:

lua
lowPickupAnim = {dict = "pickup_object", clip = "pickup_low", duration = 1000}

#placement.highPickupAnim

Animation for picking a laptop up from table height, or removing a computer there.

Example:

lua
highPickupAnim = {dict = "mp_common", clip = "givetake1_a", duration = 900}

#Use

#use.distance

Metres from a placed laptop's keyboard the player can use it, open its lid or ports, service it and pick it up.

Example:

lua
distance = 1.5

#use.standOffset

Where the player stands in front of a placed laptop while using it, in the laptop's own space. Use laptop walks the player there.

Example:

lua
standOffset = vec3(0.0, -0.62, 0.0)

#use.standingAnim

Animation played while the player uses a laptop at table height.

Example:

lua
standingAnim = {dict = "anim@heists@prison_heiststation@cop_reactions", clip = "cop_b_idle", flag = 1}

#use.kneelingAnim

Animation played while the player uses a laptop on the floor, below placement.lowHeight.

Example:

lua
kneelingAnim = {dict = "amb@medic@standing@tendtodead@idle_a", clip = "idle_a", flag = 1}

#Desktop Computer

The desktop computer is a display with a keyboard and mouse that stays where it is set up, for an office or a business. It runs every OS a laptop does, on mains power, so it never needs charging and its parts never wear. It is kept in zyke_laptop_placed like a laptop put down. See Using the Laptop.

#computer.enabled

Enables desktop computers. They also need the laptop item and computer.item; without either they stay off and the server console says so.

Example:

lua
enabled = true

#computer.item

Usable item that sets a computer up where the player picks. Its hardware, OS and devices are kept in the item's metadata, so removing the computer gives the same item back with everything on it.

Example:

lua
item = "desktop_computer"

#computer.jobs

Jobs that may set a computer up, as job names or {name = "police", minGrade = 2}. false, the default, lets anyone. CanPlaceComputer in server/unlocked/hooks.lua decides further.

Example:

lua
jobs = {"police", {name = "mechanic", minGrade = 2}}

#computer.maxPerCharacter

Computers one character can have set up at once. false for no limit.

Example:

lua
maxPerCharacter = 3

#computer.staffPermission

ACE permission, or ESX group, of the staff who may remove anyone's computer. false for nobody besides the character who set it up. Staff with the ACE command.laptop_computer set computers up without an item with /laptop_computer [player id], see Commands.

Example:

lua
staffPermission = "command"

#computer.prebuilt

Prebuilt from Config.Prebuilts a new computer is built as. The default desktop has no battery.

Example:

lua
prebuilt = "desktop"

#Ports

The ports on the laptop's sides: two USB-C ports and an HDMI port on the left, a USB-A port and an audio jack on the right. A plugged device stays in the laptop, saved in the laptop item's metadata, until someone pulls it out. Other resources add devices with the RegisterDeviceType export. See Ports & Devices.

#ports.others

Lets players other than the character who put a laptop down plug devices into it and pull them out, as with a real laptop. CanPlugLaptopDevice and CanUnplugLaptopDevice in server/unlocked/hooks.lua limit it further.

Example:

lua
others = true

#ports.anim

Animation played while a device is plugged in or pulled out. The server is asked halfway through.

Example:

lua
anim = {dict = "mp_common", clip = "givetake1_a", duration = 900}

#ports.dataStick

The data stick players keep files on. Its files are stored on the server by the stick's serial and go wherever the item goes. Creates the zyke_laptop_sticks table on start. Set to false for no data stick.

FieldDescription
itemThe stick's item.
modelProp shown in the laptop's USB-A port.
maxBytesBytes its files may take encoded, checked on the server. Every save sends them all to the server, so keep it under 327680.
capacitySize in bytes shown to players, 8 GB by default. The free space shown is scaled to it.

Example:

lua
dataStick = {item = "usb_drive", model = "zyke_laptop_usb_blank", maxBytes = 131072, capacity = 8589934592}

#ports.powerBank

The power bank. Plugged into a USB-C port of a laptop in the hands it powers the laptop and charges its battery until the bank runs flat, also while the laptop is put away with it in. Plugged into a laptop put down, it fills from mains through the laptop. Set to false for none, and take it out of the shop's accessories too.

Its charge is kept in the item's metadata as charge, from 0 to 1, and a bank without one is full. Each bank gets a serial, such as PB-7F3K-92QD, durability follows its charge from 0 to 100 for the inventory's durability bar, and description shows its capacity and charge.

FieldDescription
itemThe bank's item.
whWatt-hours it holds. A 20,000 mAh bank holds about 72.
chargeMinutesMinutes it takes to charge an empty laptop battery, slower than mains (hardware.wear.chargeMinutes).
fillMinutesMinutes a laptop put down takes to fill an empty bank.
efficiencyShare of the bank's energy that reaches the laptop.

Example:

lua
powerBank = {item = "laptop_powerbank", wh = 72, chargeMinutes = 35, fillMinutes = 45, efficiency = 0.85}

#Hardware

The parts inside every laptop: a mainboard with its cooler, two memory sockets, an SSD, a Wi-Fi card and a battery. Each part is the exact part item, kept in the laptop item's metadata as hardware, or in the server's KVP by the laptop's serial without a laptop item. Laptops come as one of the prebuilts. See Hardware & Servicing for the parts, what they do and the service view.

#hardware.default

Prebuilt a laptop item without hardware becomes the first time it is taken out, such as one given without metadata. Shops give the others with the CreateLaptopMetadata export.

Example:

lua
default = "basic"

#hardware.migrated

Prebuilt that laptops from before hardware get on their next take-out. Their drive is kept as the SSD, and their serial, OS, skin, finish and ports stay as they were. The default legacy has the specs those laptops always showed.

Example:

lua
migrated = "legacy"

#hardware.items

One item per kind of part. The exact model is in the item's metadata.model. Rename them to match your inventory; the definitions are in extras/items/.

Example:

lua
items = {board = "laptop_mainboard", cooler = "laptop_cooler", ram = "laptop_ram", ssd = "laptop_ssd", wifi = "laptop_wifi", battery = "laptop_battery"}

#hardware.used

Parts the CreatePartMetadata export makes with used = true: a random condition between minCondition and maxCondition, 1 being as new, and memory that is faulty faultChance of the time. A fault stays hidden until the stick is tested.

Example:

lua
used = {minCondition = 0.55, maxCondition = 0.9, faultChance = 0.15}

#hardware.leads

Hotspot range with 0, 1 or 2 of the Wi-Fi card's antenna leads plugged in, times the hotspot's own range. A Wi-Fi 7 card reaches a tenth further on top.

Example:

lua
leads = {[0] = 0.25, [1] = 0.6, [2] = 1.0}

#hardware.needsBattery

Whether laptops need their battery. Set to false and a laptop in the hands runs and charges as if plugged in: its battery never drains, a laptop without a battery still powers on, and a power bank in it is left alone. Nothing needs the power bank then, so take it out of the shop's accessories as well.

Example:

lua
needsBattery = true

#hardware.wear.dustHours

Powered hours until the fan is fully clogged with dust. Powered hours count while an OS runs on the laptop. Dust and dried paste make a laptop run hot, which slows it down.

Example:

lua
dustHours = 40

#hardware.wear.pasteDays

Days thermal paste takes to dry from new down to pasteFloor, counted by the server's clock whether the laptop runs or not.

Example:

lua
pasteDays = 30

#hardware.wear.padDays

Days a phase-change pad, on the coolers that have one, takes to dry down to pasteFloor.

Example:

lua
padDays = 90

#hardware.wear.pasteFloor

Lowest the paste dries to, from 0 to 1.

Example:

lua
pasteFloor = 0.3

#hardware.wear.batteryMinutes

Minutes a full battery at full health lasts in the hands; a worn battery lasts as much less as it lost. It only drains while the laptop is powered on, so the default of 12 hours means a charge about every 12 hours of near constant use, and a laptop that is barely used can go days without one.

Example:

lua
batteryMinutes = 720

#hardware.wear.chargeMinutes

Minutes an empty battery takes to charge full while the laptop is put down.

Example:

lua
chargeMinutes = 20

#hardware.wear.healthPerCycle

Battery health lost per full charge cycle.

Example:

lua
healthPerCycle = 0.002

#hardware.wear.minHealth

Health a battery never drops under.

Example:

lua
minHealth = 0.5

#hardware.wear.ramFaultChance

Chance each memory stick turns faulty per 10 powered hours. An SSD wears out over the powered hours its model is rated for and then turns read-only; that is set per model in the parts catalogue, not here.

Example:

lua
ramFaultChance = 0.005

#hardware.service.enabled

Enables the service view, Service in a placed laptop's options: the laptop turns over for its bottom screws, then opens up so parts come out and go in, the fan is cleaned and the processor re-pasted. Every OS on the laptop has to be shut down first.

Example:

lua
enabled = true

#hardware.service.others

Lets players service laptops they didn't put down. The CanServiceLaptop hook in server/unlocked/hooks.lua decides who, for example a repair shop's job.

Example:

lua
others = false

#hardware.service.items

Items the service view uses: tool to open the laptop, paste to re-paste the processor and air to clean the fan. Set one to false for no item needed for that step.

Example:

lua
items = {tool = "laptop_screwdriver", paste = "thermal_paste", air = "compressed_air"}

#hardware.service.pasteUses

Uses in a new tube of paste. An item without a uses value in its metadata is new.

Example:

lua
pasteUses = 5

#hardware.service.airUses

Uses in a new can of compressed air.

Example:

lua
airUses = 10

#hardware.service.blowOut

Share of the fan's dust a blow-out takes with the cooler in place. A cooler that is lifted off is cleaned fully.

Example:

lua
blowOut = 0.7

#Mirror

The screen of an open laptop shows the players near it a simple copy of what is on that laptop: black while it is off, the OS's boot and lock screens, or its desktop with the open windows as outlines in their places, each with its app's icon and name and the browser's site. Never the page itself or anything typed.

#mirror.enabled

Enables the mirrored screens. Off, every laptop's screen shows a stock lock screen to other players.

Example:

lua
enabled = true

#mirror.range

Metres from a laptop its screen is mirrored.

Example:

lua
range = 6.0

#mirror.maxScreens

Laptops one player sees mirrored at once, the nearest open ones in view. Each takes a DUI of its own. At most 8. Laptops further away, out of view or past this number show the lock screen of the OS the watching player runs.

Example:

lua
maxScreens = 8

#mirror.titles

Shows window names and the browser's page title and site address. false leaves only the app icons.

Example:

lua
titles = true

#mirror.veilPrivacy

Leaves out the names, page titles and site addresses while the laptop runs Veil, the privacy OS, even with titles on. Its windows and app icons still show.

Example:

lua
veilPrivacy = true

#Casting

Casting a window to a screen in the world, such as a TV or a briefing room's screen, for everyone near it to see. Only that window's content goes to the screen, and every player's game draws it itself, so text stays sharp. Placed screens are kept in the zyke_laptop_cast_screens table, created on start. The CanCastToScreen and CanCreateScreen hooks in server/unlocked/hooks.lua limit who may do what. See Casting.

#cast.enabled

Enables casting. Off, the Cast button is gone and nothing of it runs.

Example:

lua
enabled = true

#cast.liveScreens

Screens one player sees a cast on at once, the nearest in front of them. Each costs a browser in the game of every player who sees it, so keep it low.

Example:

lua
liveScreens = 2

#cast.viewRange

Metres from a screen its cast is seen.

Example:

lua
viewRange = 25.0

#cast.cableReach

Metres of HDMI cable between a laptop and a screen, for screens that don't set their own reach.

Example:

lua
cableReach = 5.0

#cast.wirelessRange

How far a wireless screen takes a cast from a laptop, for screens that don't set their own range.

Example:

lua
wirelessRange = 8.0

#cast.hdmi

The HDMI cable item, plugged into a placed laptop's HDMI port from the Ports prompt. Set hdmi = false for wireless screens only.

Example:

lua
hdmi = {item = "hdmi_cable"}

#cast.resolution

Pixels across the picture of a cast is drawn at. Its height follows the screen's shape.

Example:

lua
resolution = 1280

#cast.limits

How much one player may send while casting. A snapshot is the window's whole content, a delta a change on it such as a pen stroke, and a signal a passing thing such as a pointer. snapshotBytes, deltaBytes and signalBytes are the most one may take as JSON. snapshot, delta and signal allow a burst of count and then rate a second. The server drops what goes over them.

Example:

lua
limits = {
    snapshotBytes = 32768,
    deltaBytes = 2048,
    signalBytes = 256,
    snapshot = {count = 3, rate = 1},
    delta = {count = 30, rate = 15},
    signal = {count = 10, rate = 8},
}

#cast.imageHosts

Hosts whose images a cast may show besides the laptop's own pictures. Every viewer's game loads them, which tells that host who looked at the screen. Empty by default, which allows none.

Example:

lua
imageHosts = {"r2.fivemanage.com"}

#cast.creator

The screen creator's command and the ACE permission CanCreateScreen checks by default. Admins aim at a wall or a TV to place a screen and set its label, input, cable reach and wireless range.

Example:

lua
creator = {command = "laptopscreens", permission = "command"}

#cast.screens

Screens fixed in the config. They show in the creator's menu but are only changed here.

FieldDescription
idUnique id.
labelName shown in the Cast menu.
cornersThree corners as seen from in front: topLeft, topRight and bottomLeft.
coords, width, heightInstead of corners: the centre of the picture with the heading it faces as a vec4, and its size in metres.
input"hdmi", "wireless" or "both".
reachOptional cable reach, at most 30 metres.
rangeOptional wireless range, at most 60 metres.
bucketOptional routing bucket, 0 when left out.

Screens are 0.2 to 12 metres wide and tall. A screen the config gets wrong is skipped with a warning in the server console.

Example:

lua
screens = {
    -- Michael's living room, on the pull-down projector screen above the fireplace, by its corners
    {id = "michael_projector", label = "Living room projector", corners = {topLeft = vec3(-800.667, 174.880, 75.145), topRight = vec3(-800.165, 173.573, 75.145), bottomLeft = vec3(-800.667, 174.880, 74.355)}, input = "both", reach = 6.0},
}

#Boot

The startup sequence of the OS.

#boot.bootloader

Shows the OS's own boot loader screen when the player boots it from the laptop's boot menu (F12 at the startup screen). false goes straight on to the OS's boot screen.

Example:

lua
bootloader = true

#boot.greeter

Shows each OS's greeter after it boots: the first-run setup and the sign-in and lock screens of Facade, Lumen and Solace, and Veil's Welcome Screen. false skips it and goes straight to the desktop, so accounts are not asked for their password. Leave it on for a normal server.

Example:

lua
greeter = true

#boot.speed

Multiplies the length of every boot and shutdown step. The laptop's own hardware speeds it up or slows it down on top. 0 skips straight to the desktop.

Example:

lua
speed = 1.0

#Standby

A laptop stays on like a real one that sleeps: closed, put away, through a relog or a server restart it wakes on its lock screen with the windows and browser tabs it had. Only Facade, Lumen and Solace booted from the drive stay on; a USB stick leaves with its player. Powering off or restarting, an empty battery, a service or a change of parts turns it off. Creates the zyke_laptop_standby table on start. See Using the Laptop.

#standby.enabled

Enables standby. Off, a laptop that is put away, or left through a relog or a restart, boots fresh the next time it is opened.

Example:

lua
enabled = true

#standby.days

Days a laptop nobody woke stays on. After that it boots again.

Example:

lua
days = 30

#Connection

Milliseconds connecting to the Onion network takes. The Wi-Fi card's latency stretches the whole connection, so a Wi-Fi 7 card connects faster.

#connection.minDuration

Shortest the connection takes, in milliseconds. Each attempt picks a time between minDuration and maxDuration.

Example:

lua
minDuration = 4000

#connection.maxDuration

Longest the connection takes, in milliseconds.

Example:

lua
maxDuration = 9000

#connection.bridgeDelay

Milliseconds added when the player connects through bridges in the connection app's settings.

Example:

lua
bridgeDelay = 3000

#Wi-Fi

The laptop is online through a hotspot in range, listed in Config.Hotspots, or through the player's phone. See Wi-Fi & Browsing.

#wifi.phone.enabled

Lets players tether to their phone anywhere, under a name like "Linus's Phone". Set to false to only be online at hotspots.

Example:

lua
enabled = true

#wifi.phone.item

Item the player has to carry to tether. false, the default, for none.

Example:

lua
item = "phone"

#Files

Like Tails, Veil forgets every file when it shuts down except those in ~/Persistent. See Operating Systems.

#files.storage

Where ~/Persistent is kept.

  • "character" stores it in the database per character, in the zyke_laptop_files table created on start. It follows the character to any PC and survives a cleared cache.
  • "client" stores it on the player's PC, shared by every character played there. It is gone when the player clears their FiveM cache.

Example:

lua
storage = "character"

#files.maxBytes

Bytes one player's ~/Persistent may take. Client storage is capped at 120000, under the 128 KiB a client keeps under one key, and character storage at 327680, as every save sends the whole folder to the server. Past it the OS tells the player the storage is full.

Example:

lua
maxBytes = 262144

#OS

The operating systems a laptop runs. Facade, Lumen and Solace are installed on the laptop's drive, Veil boots live from a USB stick and never touches the drive. Pressing F12 at the laptop's startup screen opens its boot menu. See Operating Systems.

#os.default

OS installed on new laptops and on laptop items from before operating systems: "facade", "lumen", "solace" or "veil".

Example:

lua
default = "facade"

#os.usb

Items that show up in the boot menu while the player carries them, by OS. veil boots Veil live; lumen, solace and facade erase the drive and install that OS. Set one to false to offer that option to everyone without an item.

Example:

lua
usb = {veil = "veil_usb", lumen = "lumen_installer", solace = "solace_installer", facade = "facade_installer"}

#os.carriedSticks

Lets the boot menu offer the sticks a player carries, as well as the ones plugged into the laptop's USB port. Set to false to make players plug the stick in first.

Example:

lua
carriedSticks = true

#App Store

The app store on the desktop of Facade, Lumen and Solace. The system apps come installed; the rest are downloaded from it: Shroud, Natter, the Onion network app, the drawing and slides apps, and the apps other resources register. Veil and the Crowbar recovery toolkit run live with every app. See Operating Systems.

#appStore.enabled

Enables the app store. false installs every app from the start.

Example:

lua
enabled = true

#appStore.preinstalled

Apps a new account starts with besides the system apps: "connection" (the Onion network app), "chat" (Shroud), "messenger" (Natter), "paint", "slides" or the id of a registered app. Empty by default. A resource can also register its app with preinstalled = true to skip the store, see Apps & Sites. Accounts from before the store keep every app they had.

Example:

lua
preinstalled = {"messenger", "paint"}

#Accounts

The accounts of Facade, Lumen and Solace. The account the first-run setup makes is the drive's administrator. In Settings it can add more accounts, turn on a Guest account and let anyone sign in with their own Nimbus account as Other user, as a business's computer needs.

#accounts.maxAttempts

Wrong passwords in a row before the laptop locks. The count is kept per laptop, so it carries over to the next player who tries.

Example:

lua
maxAttempts = 5

#accounts.lockoutSeconds

Seconds the laptop refuses passwords once it locked.

Example:

lua
lockoutSeconds = 60

#accounts.maxUsers

Accounts one drive keeps besides the one setup made, the Nimbus users who signed in as Other user included.

Example:

lua
maxUsers = 10

#Cloud

Where the Facade, Lumen and Solace account and everything it keeps are stored. Creates the zyke_laptop_cloud_accounts, zyke_laptop_cloud_data and zyke_laptop_cloud_devices tables on start. See Operating Systems.

#cloud.mode

  • "synced": the account is a Nimbus account that belongs to the character and follows them to every laptop they sign in on, so a lost laptop loses nothing.
  • "drive": everything stays on the laptop's SSD, and losing the SSD loses it all.

Example:

lua
mode = "synced"

#cloud.localAccounts

Lets players set up a local account kept only on the drive while mode is "synced", the hardcore choice.

Example:

lua
localAccounts = true

#cloud.maxDevices

Laptops one account can be signed in on at once.

Example:

lua
maxDevices = 10

#cloud.reset

How "Forgot password" proves the player is the account's character. "phone" texts a code to their phone, and works as "instant" without a phone resource that takes texts. "instant" resets at once. false turns resetting off. Only works while mode is "synced".

Example:

lua
reset = "phone"

#cloud.alerts

Texts the account's character when a new laptop signs in, a reset is asked for or a laptop is signed out. Replace SendLaptopCloudAlert in server/unlocked/hooks.lua to send these another way.

Example:

lua
alerts = true

#Recovery

Crowbar, the recovery toolkit a thief boots from a USB stick to get into a laptop's drive. Facade and Solace show the account's files at once, and their timer only gates setting a new password. Lumen keeps its home folder and history locked until the timer finishes. Veil never writes to the drive, so there is nothing to find. See Ownership & Recovery.

#recovery.enabled

Enables the recovery toolkit. Off, it never shows in the boot menu.

Example:

lua
enabled = true

#recovery.item

Item that shows up in the boot menu while the player carries it. false offers the toolkit to everyone.

Example:

lua
item = "recovery_usb"

#recovery.durations

Seconds the toolkit works on a password, by the OS on the drive and how strong the password is: trivial, weak, fair or strong. The timer only runs while a player keeps the toolkit open on that laptop, and keeps its progress in between.

Example:

lua
durations = {
    facade = {trivial = 30, weak = 45, fair = 60, strong = 180},
    lumen = {trivial = 120, weak = 300, fair = 1200, strong = 1200},
    solace = {trivial = 30, weak = 45, fair = 60, strong = 180},
}

#recovery.exhausts

Strength tiers the toolkit gives up on once their duration is over, so the drive stays locked. By default a strong Lumen password is never cracked.

Example:

lua
exhausts = {
    lumen = {strong = true},
}

#recovery.maxLogs

Lines each drive's system log keeps. The oldest go first.

Example:

lua
maxLogs = 100

#recovery.repair

OSes whose installer stick can reset the local password of a drive running that OS without erasing it. Leave an OS out, or set it to false, to have its installer only erase and reinstall.

Example:

lua
repair = {facade = true, solace = true}

#Shroud (chat)

Shroud, the onion chat app. Players chat under anonymous handles, and other resources register contacts such as dealers and fixers through exports. Creates the zyke_laptop_chat_* tables on start. See Messaging and Shroud Contacts.

#chat.enabled

Enables Shroud. Off, the app is gone from every OS, nothing of it runs on the server, its tables aren't created and its exports, such as RegisterChatContact, aren't registered.

Example:

lua
enabled = true

#chat.maxLength

Characters one message may have.

Example:

lua
maxLength = 500

#chat.maxIdentities

Handles one character may create. They are counted per character, but which handles belong to whom is only stored when the creating OS leaves the character trace.

Example:

lua
maxIdentities = 3

#chat.retentionDays

Days messages and traces are kept before they are deleted. Checked on start and once a day.

Example:

lua
retentionDays = 14

#chat.rateLimits

How often a player may do something, count times per seconds: send messages, create an identity, signIn, and action for card buttons, forms and purchases.

Example:

lua
rateLimits = {
    send = {count = 8, seconds = 10},
    identity = {count = 3, seconds = 3600},
    signIn = {count = 5, seconds = 60},
    action = {count = 10, seconds = 10},
}

#chat.rooms

Public rooms every player can join. A room is named by the "chatServer:room:<id>" and "chatServer:roomDescription:<id>" locale keys, or by the name and description given here. listed = false keeps a room out of the directory. Resources add more with the RegisterChatRoom export.

Example:

lua
rooms = {
    {id = "lobby"},
    {id = "market"},
    {id = "garage", name = "The Garage", description = "Parts and rides"},
}

#chat.traces

What the server records when a player sends a message or creates a handle, by the OS their laptop runs. "character" is the character identifier, "serial" the laptop's serial, "network" the hotspot or phone it is online through and "coords" where the player stood. Players never see traces; only server resources read them, through the GetChatTraces export.

Example:

lua
traces = {
    facade = {"character", "serial", "network", "coords"},
    solace = {"character", "serial", "network", "coords"},
    lumen = {"serial", "network"},
    veil = {},
}

#Natter (messenger)

Natter, the messaging app. Accounts belong to the character's phone number, read through zyke_lib, and a laptop stays linked to its account. Creates the zyke_laptop_messenger_* tables on start. See Messaging.

#messenger.enabled

Enables Natter. Off, the app, its requests, its exports and its tables are gone.

Example:

lua
enabled = true

#messenger.maxLength

Characters one text or caption may have.

Example:

lua
maxLength = 2000

#messenger.maxDevices

Laptops one account may be linked to at once, Veil links included.

Example:

lua
maxDevices = 4

#messenger.numberFormat

Number given to characters when the server has no phone resource at all. # is a random digit.

Example:

lua
numberFormat = "555-####"

#messenger.codeSender

Short code the link code text comes from on the phone, digits only.

Example:

lua
codeSender = "6283"

#messenger.linkCodeSeconds

Seconds a link code sent to the phone stays valid.

Example:

lua
linkCodeSeconds = 300

#messenger.retentionDays

Days messages are kept. Checked on start and once a day.

Example:

lua
retentionDays = 30

#messenger.metadataRetentionDays

Days who messaged whom and when stays available to police tools through GetMessengerMetadata. The content of messages is never part of it.

Example:

lua
metadataRetentionDays = 30

#messenger.editMinutes

Minutes a message can be edited.

Example:

lua
editMinutes = 15

#messenger.deleteForEveryoneHours

Hours a message can be deleted for everyone.

Example:

lua
deleteForEveryoneHours = 48

#messenger.disappearing

Times in seconds the app offers for disappearing messages. The defaults are 24 hours, 7 days and 90 days.

Example:

lua
disappearing = {86400, 604800, 7776000}

#messenger.groups.maxMembers

Members a group may have.

Example:

lua
groups = {maxMembers = 64}

#messenger.attachments.maxFileBytes

Bytes a file sent from the drive may have.

Example:

lua
maxFileBytes = 262144

#messenger.attachments.imageHosts

Hosts images may come from. Empty, the default, allows any https host.

Example:

lua
imageHosts = {"r2.fivemanage.com"}

#messenger.rateLimits

How often a player may do something, count times per seconds: send messages, action (edits, deletes, reactions, stars, votes, settings, contacts, blocks and group changes), lookup (opening chats by number, profile lookups, search and invite previews), link (link codes and attempts at them), group (creating and joining groups) and typing.

Example:

lua
rateLimits = {
    send = {count = 10, seconds = 10},
    action = {count = 30, seconds = 10},
    lookup = {count = 15, seconds = 60},
    link = {count = 5, seconds = 300},
    group = {count = 5, seconds = 600},
    typing = {count = 20, seconds = 10},
}

#Browsing

The browser. On Facade, Lumen and Solace ordinary sites open over the plain internet, .onion sites need the Onion network on every OS, and Veil sends everything through it. A VPN and the Onion network are optional protection that changes what the server records about a visit. Creates the zyke_laptop_site_visits table on start. See Wi-Fi & Browsing.

#browsing.vpn.enabled

Enables the one-click VPN in Facade's quick settings, Lumen's system menu, Solace's Quick Controls and the browser's menu. Veil has no VPN; everything already goes through the Onion network.

Example:

lua
enabled = true

#browsing.vpn.connectDuration

Milliseconds the VPN takes to connect.

Example:

lua
connectDuration = 2500

#browsing.record

Which visits are recorded. "monitored" only records the sites in monitored, sites a resource registered with monitored = true and sites marked with the SetSiteMonitored server export. "all" records every site.

Example:

lua
record = "monitored"

#browsing.monitored

Addresses recorded with record = "monitored". Empty by default.

Example:

lua
monitored = {"lifeinvader.com", "ammu-nation.com"}

#browsing.retentionDays

Days visit traces are kept before they are deleted. Checked on start and once a day.

Example:

lua
retentionDays = 14

#browsing.rateLimit

How often a player's visits are taken in, count times per seconds. Visits over the limit are dropped.

Example:

lua
rateLimit = {count = 20, seconds = 10}

#browsing.traces

What the server records about a visit, by how the player was protected: direct, vpn or onion. The fields are the same as Shroud's traces. Veil is always onion. Only server resources read them, through the GetSiteVisitTraces export.

Example:

lua
traces = {
    direct = {"character", "serial", "network", "coords"},
    vpn = {"serial"},
    onion = {},
}

#Casino

Rollhaus, the online casino in the browser. Players deposit bank money as credits, play the games in shared/unlocked/casino/ with them and withdraw them back to the bank. Results come from the server and can be checked by the player. The CanUseCasino hook in server/unlocked/hooks.lua adds your own rules. Creates the zyke_laptop_casino_* tables on start. See Casino.

#casino.enabled

Enables the casino. Off, the site is gone from the browser, search and LS Pages, every casino request is refused, nothing of it runs on the server and none of its tables are created, for example on servers with young players or where even simulated gambling isn't allowed. The IsCasinoEnabled export still answers, with false.

Example:

lua
enabled = true

#casino.site

The site's name and address in the browser.

Example:

lua
site = {name = "Rollhaus", host = "rollhaus.gg"}

#casino.creditRate

Dollars one credit costs. Deposits round down to whole cents of credit and withdrawals pay whole dollars, so cents left over stay on the site.

Example:

lua
creditRate = 1.0

#casino.deposit

Whole dollars one deposit may be, taken from the bank account.

Example:

lua
deposit = {min = 10, max = 250000}

#casino.withdraw

Whole dollars one withdrawal may be, paid to the bank account, and cooldown, the seconds between two withdrawals.

Example:

lua
withdraw = {min = 10, max = 250000, cooldown = 0}

#casino.minAge

Years a character must be, from the date of birth the framework stores. 0, the default, turns the check off, and characters without a readable date of birth are let in.

Example:

lua
minAge = 18

#casino.bots.enabled

Generated players who fill the seats a real site would have other people in. Their money is the house's: nobody is paid when a bot wins. Off, no bots play anywhere, whatever the switches below and in the game files say.

Example:

lua
enabled = true

#casino.bots.feed

Mixes bot bets into the live bets feed so a quiet server still looks alive. They are never in anyone's history.

Example:

lua
feed = true

#casino.bots.feedInterval

Seconds between two bot bets in the feed.

Example:

lua
feedInterval = {min = 4, max = 14}

#casino.bots.coinflip

Lets the creator of a coinflip call a bot to take the other side.

Example:

lua
coinflip = true

#casino.bots.battles

Lets bots fill the empty seats of a case battle.

Example:

lua
battles = true

#casino.bots.blackjack

Bots playing basic strategy at the live blackjack tables, up to the game's botSeats.

Example:

lua
blackjack = false

#casino.bots.crash

Bots betting in each Crash round so its players list still moves.

Example:

lua
crash = true

#casino.feed.size

Rows each list of the live bets feed keeps.

Example:

lua
size = 30

#casino.feed.highRoller

Credits a bet has to stake to show under High rollers.

Example:

lua
highRoller = 1000

#casino.historyDays

Days settled bets and revealed round seeds are kept for history and bet details. The ledger of every balance change is never deleted.

Example:

lua
historyDays = 30

#casino.rateLimits

How often a player may do something, count times per seconds. bet covers every action that takes a stake, action everything else on the site, wallet deposits and withdrawals, account picking a username and changing settings, and password the passwords tried on player blackjack tables.

Example:

lua
rateLimits = {
    bet = {count = 20, seconds = 5},
    action = {count = 30, seconds = 10},
    wallet = {count = 6, seconds = 60},
    account = {count = 5, seconds = 60},
    password = {count = 5, seconds = 60},
}

#Job Board

Hireline, a job board in the browser. Businesses post listings for their job, players apply with their profile, talk to the employer in a message thread and book interview slots. Hiring does not change anyone's job: the server fires zyke_laptop:jobs:ApplicantHired for your own script to do that. Creates the zyke_laptop_jobs_* tables on start. See Job Board.

#jobs.enabled

Enables Hireline. Off, the site, every action and every table it has are gone.

Example:

lua
enabled = true

#jobs.site

The site's name and address in the browser.

Example:

lua
site = {name = "Hireline", host = "hireline.com"}

#jobs.posting.bossRanks

Lets the job's boss grades in the framework post listings and handle applicants for their current job.

Example:

lua
bossRanks = true

#jobs.posting.grades

Lowest grade level of a job that may post as well, by job name. Empty by default.

Example:

lua
grades = {police = 3, mechanic = 2}

#jobs.posting.excluded

Jobs that never post, such as the framework's default job. The CanPostJobListing(plyId, jobName, ranked) hook in server/unlocked/hooks.lua gets the last word for every other job; ranked is whether the player passes bossRanks or grades.

Example:

lua
excluded = {"unemployed"}

#jobs.listingDays

Days an open listing stays up before it closes, unless an employer renews it.

Example:

lua
listingDays = 14

#jobs.maxOpenListings

Listings one job may have open at once.

Example:

lua
maxOpenListings = 10

#jobs.maxApplicationsPerDay

Applications one character may send in 24 hours.

Example:

lua
maxApplicationsPerDay = 15

#jobs.historyDays

Days closed listings and finished applications are kept, then deleted with their messages. Checked on start and once a day.

Example:

lua
historyDays = 60

#jobs.interviewLockMinutes

Minutes before an interview from which it can no longer be moved or cancelled.

Example:

lua
interviewLockMinutes = 30

#jobs.reminderMinutes

Minutes before an interview its applicant and the job's online employers get a reminder.

Example:

lua
reminderMinutes = 15

#jobs.phoneReminders

Also texts the reminder to the applicant's phone, when the phone resource takes texts.

Example:

lua
phoneReminders = true

#jobs.limits

Most characters each text may have, and most entries of each list, on listings, applications and profiles. payMax is the highest pay a listing may offer in dollars, slotMinutes the length of one interview slot and interviewDays the upcoming interview days one listing may have.

Example:

lua
limits = {
    title = 80,
    aboutRole = 2000,
    cover = 1500,
    message = 1000,
    payMax = 100000,
    slotMinutes = {min = 10, max = 60},
    interviewDays = 8,
}

#jobs.rateLimits

How often a player may do something, count times per seconds. read covers looking around, write saving anything, apply sending applications and message sending messages.

Example:

lua
rateLimits = {
    read = {count = 60, seconds = 10},
    write = {count = 15, seconds = 20},
    apply = {count = 4, seconds = 60},
    message = {count = 10, seconds = 20},
}

#Config.Shop

shared/unlocked/shop.lua holds the laptop shop: a counter where players buy laptops, refinish and put backplates on the ones they carry, and buy parts, tools and accessories, each shown on the counter in a studio as they browse. Every offer and price is here, and the server checks each purchase against it. CanBuyLaptopShopItem in server/unlocked/hooks.lua adds your own rules. See Shop.

The location, storages, door locks and displays stand in Digital Den, the free zyke_digitalden interior at the Morningwood strip mall. Run it alongside, or move them all to a shop of your own when another map fills that unit; see Dependencies.

#enabled

Enables the shop counters. Set to false to sell only through a real person, such as a player-run Digital Den, with the server exports. The storages, door locks and display laptops stay; the displays then show no price.

Example:

lua
enabled = true

#accounts

Accounts a purchase is paid from, the first one that holds the price.

Example:

lua
accounts = {"cash", "bank"}

#locations

Shop counters. Each gets a shop keeper and a blip, browsed through an interest point or the target menu, as interaction picks. Buyers have to stand within 5 metres of coords.

FieldDescription
labelName of the shop, also the blip's name.
modelPed model of the shop keeper. Leave it out for a counter without a ped.
coordsWhere the ped stands, at eye height, and the heading it faces, a vec4.
scenarioScenario the ped plays.
blip{sprite, color, scale}. Leave it out for no blip.
studioOptional vec4: the spot on the counter the studio shows its laptop or item on, and the heading from it towards the customer. Set it when the studio can't find the counter top in front of the shop keeper; the client console says so.

Example:

lua
locations = {
    {
        label = "Digital Den",
        model = "a_m_y_business_02",
        coords = vec4(-1318.56, -401.81, 36.64, 33.95),
        scenario = "WORLD_HUMAN_STAND_IMPATIENT",
        blip = {sprite = 521, color = 3, scale = 0.7},
    },
}

#storages

Shelves the shop's staff keep stock on, each one a stash shared by everyone who may open it. They are opened the same way as the shop, within 4 metres, and stay when enabled is false. CanOpenLaptopStorage in server/unlocked/hooks.lua adds your own rule, such as being on duty.

FieldDescription
idNames the stash in your inventory as zyke_laptop_<id>, so changing it starts an empty one.
labelName shown on the shelf and the stash.
coordsSpot on the shelf that is marked.
jobsJob names that may open it. Leave it out to let anyone. The digitalden job is in extras/jobs/.
slotsSlots of the stash, 50 when left out.
weightWeight of the stash in grams, 200000 when left out.

Example:

lua
storages = {
    {id = "digitalden_workshop", label = "Workshop Shelf", coords = vec3(-1318.59, -406.73, 36.84), jobs = {"digitalden"}, slots = 50, weight = 200000},
    {id = "digitalden_stock", label = "Stock Room Shelf", coords = vec3(-1312.39, -402.99, 36.84), jobs = {"digitalden"}, slots = 100, weight = 500000},
}

#doorlock.enabled

Adds the shop's doors to ox_doorlock or rcore_doorlock when the resource or the door lock starts, unless the door lock already has a door by that name, so a door you change there keeps your change. By default Digital Den's front doors, repair room and storage open for the digitalden job only, and the front doors start unlocked so customers can walk in.

qb-doorlock can't take doors from another resource: copy extras/doorlock/qb-doorlock.lua into qb-doorlock/configs/ and set enabled = false. For cd_doorlock, import extras/doorlock/ox_doorlock.sql with its migration tool and set enabled = false. Set it to false for your own door setup too. See extras/doorlock/README.md.

Example:

lua
enabled = true

#doorlock.pin

PIN the doors open with on rcore_doorlock, which can't take a job from another resource. Change it before you start the resource with rcore_doorlock, then give the doors their job in its /doors menu.

Example:

lua
pin = "1234"

#doorlock.locks

The doors.

FieldDescription
nameUnique name the door lock knows the door by.
labelName shown in the door lock.
jobsJob names, or {name = "digitalden", minGrade = 2} for a lowest grade.
lockedHow a new door starts.
distanceMetres a player has to stand within. Optional.
doorsOne door, or two for a double door, each {model, coords, heading}.

Example:

lua
locks = {
    {
        name = "digitalden front doors",
        label = "Digital Den Front Doors",
        jobs = {"digitalden"},
        locked = false,
        distance = 2.5,
        doors = {
            {model = "v_ilev_csr_door_l", coords = vec3(-1320.2831, -398.3739, 36.8857), heading = 31.1},
            {model = "v_ilev_csr_door_r", coords = vec3(-1318.6459, -397.3864, 36.8911), heading = 31.06},
        },
    },
}

#displays

Laptops on show: the real laptop standing open on a table or shelf, with an acrylic spec sign on its right that shows the model, its colour, its parts and its price at the shop. Players walk up to a sign and look closer at it. They are props only, nobody can use one or carry it off, and they exist within renderDistance. They stay when enabled is false, then without a price; the price also only shows while the shop sells that laptop in that finish. The defaults fill Digital Den's showroom with 27 laptops. Set displays = {} for none.

FieldDescription
coordsWhere the laptop stands and the heading someone using it would face, a vec4. Keep about 0.7 metres between two laptops in a row so the sign fits.
prebuiltAn id from Config.Prebuilts.
finishOne of finishes. Leave it out for the stock graphite.
skinA backplate from skins. Leave it out for the stock lid.
replacesA map prop model, or a list of them, standing within 0.6 metres, such as the laptop an MLO has on its desk. It is hidden while the resource runs.

Example:

lua
displays = {
    {coords = vec4(-1327.918, -406.048, 36.540, 211.1), prebuilt = "basic", finish = "silver", replaces = "prop_laptop_01a"},
    {coords = vec4(-1327.236, -406.138, 36.540, 31.1), prebuilt = "standard", replaces = {"gr_prop_gr_laptop_01c", "ex_prop_ex_laptop_01a"}},
    {coords = vec4(-1325.236, -402.156, 36.090, 211.1), prebuilt = "performance", finish = "silver", skin = "grad_sunset", replaces = "gr_prop_gr_laptop_01c"},
}

#laptops

New laptops, one entry per prebuilt. Each comes in the stock graphite and in every one of finishes, and a backplate from the shop's skins below can go on in the same purchase at its own price. Needs a laptop item.

FieldDescription
enabledfalse takes the whole category out.
finishMarkupAdded to the price for a finish other than graphite. false sells graphite only.
items{prebuilt, price} per laptop. enabled = false on an entry takes it out. A prebuilt of your own needs a "shop:prebuilt:<id>" label in every locale file.

Example:

lua
laptops = {
    enabled = true,
    finishMarkup = 150,
    items = {
        {prebuilt = "basic", price = 900},
        {prebuilt = "standard", price = 1600},
        {prebuilt = "performance", price = 2800},
        {prebuilt = "flagship", price = 4500},
        {prebuilt = "diy", price = 650},
    },
}

#refinish

A new finish on a laptop the player carries, graphite included. Every finish costs price. A laptop that already has that finish is refused.

Example:

lua
refinish = {enabled = true, price = 400}

#skins

Putting a backplate, one of the skins in config.lua, on a laptop the player carries. Each one is paid in full, even one the laptop had before; a laptop that already wears that backplate is refused.

FieldDescription
enabledfalse stops selling backplates.
pricePrice of every backplate.
pricesAnother price for a whole category, such as {material = 400}. Empty by default.
boughttrue makes backplates something players buy: Settings only offers the stock lid and the backplate on that laptop, from here or the SetLaptopSkin export, which also holds with the shop disabled. false lets players pick any skin in Settings for free.

Example:

lua
skins = {enabled = true, price = 250, prices = {material = 400}, bought = true}

#parts

New parts at full condition, made by CreatePartMetadata. Each entry is {item, model, price}, where item is one of hardware.items and model an id from the parts catalogue. enabled = false on an entry or on the category takes it out. The defaults sell 6 mainboards, the 3 coolers, 7 memory sticks, 7 SSDs, 4 Wi-Fi cards and 2 batteries; see Hardware & Servicing for what each model is.

Example:

lua
parts = {
    enabled = true,
    items = {
        {item = "laptop_mainboard", model = "kc13_k7", price = 800},
        {item = "laptop_cooler", model = "dual", price = 60},
        {item = "laptop_ram", model = "d5_16", price = 140},
        {item = "laptop_ssd", model = "nv4_1tb", price = 150},
        {item = "laptop_wifi", model = "kw7", price = 90},
        {item = "laptop_battery", model = "zb61", price = 160},
    },
}

#tools

Tools bought by the amount, up to 10 at a time, new with all their uses. Each entry is {item, price}. The studio shows the laptop's own tools as their props; give an item of your own prop = "model" to show it too, and an item without one shows its inventory image. enabled = false on an entry or on the category takes it out.

Example:

lua
tools = {
    enabled = true,
    items = {
        {item = "laptop_screwdriver", price = 45},
        {item = "thermal_paste", price = 25},
        {item = "compressed_air", price = 15},
        {item = "my_antistatic_mat", price = 30, prop = "my_antistatic_mat_prop"},
    },
}

#accessories

Accessories bought by the amount, up to 10 at a time, in the same format as tools: the data stick, the HDMI cable, the power bank and the OS sticks. The recovery stick is a thief's tool, so it is listed with enabled = false.

Example:

lua
accessories = {
    enabled = true,
    items = {
        {item = "usb_drive", price = 40},
        {item = "hdmi_cable", price = 30},
        {item = "laptop_powerbank", price = 85},
        {item = "facade_installer", price = 150},
        {item = "solace_installer", price = 120},
        {item = "lumen_installer", price = 60},
        {item = "veil_usb", price = 60},
        {item = "recovery_usb", price = 2500, enabled = false},
    },
}

Prices have to be whole numbers of 0 or more; an offer with any other price is left out. The offer ids CanBuyLaptopShopItem receives are laptop:<prebuilt>:<finish or stock>, refinish:<finish or stock>, skin:<id>, part:<item>:<model>, tools:<item> and accessories:<item>.

#Prebuilts

shared/unlocked/hardware.lua lists the laptops as they leave the factory, in Config.Prebuilts. Every part gets its own serial when the laptop is made, and a board always comes with the cooler made for it. hardware.default picks the one a laptop item without hardware becomes, hardware.migrated the one laptops from before hardware get, and computer.prebuilt the desktop computer's. Shops and other resources give any of them with the CreateLaptopMetadata server export. See Hardware & Servicing.

FieldDescription
boardThe mainboard and its processor, such as "kc13_k7" for the Kestrel Talon 13 with the K7-Q360.
ramThe sticks in socket 0 and socket 1, of the memory type the board takes. One stick runs in single channel.
ssd, wifi, batteryOne model each. Leave one out for a laptop without it.

The server prints a warning on start for a prebuilt with a model the catalogue doesn't have, or with parts that don't fit together, and for hardware.default, hardware.migrated or computer.prebuilt naming a prebuilt that doesn't exist.

PrebuiltBoardMemorySSDWi-FiBattery
legacykc13_k7, Kestrel Talon 13 K7-Q3602 x 16 GB DDR4Nordwell NV4 1 TBkw6ezb61, 61 Wh
basickc12_k5, Kestrel Talon 12 K5-P2201 x 8 GB DDR4Nordwell NV3 512 GBkw6ezb55, 55 Wh
standardkc13_k7, Kestrel Talon 13 K7-Q3602 x 8 GB DDR4Nordwell NV4 512 GBkw6ezb61, 61 Wh
performanceav7_7, Arden Vela 4 Vela 7 V4802 x 16 GB DDR5Nordwell NV4 1 TBmr6ezb61, 61 Wh
flagshipava_9, Arden Vela AI 9 X5702 x 32 GB DDR5Nordwell NV4 2 TBmr7zb61, 61 Wh
diykc13_k5, Kestrel Talon 13 K5-Q340NoneNone, and no OSkw6ezb55, 55 Wh
desktopkc13_k7, Kestrel Talon 13 K7-Q3602 x 16 GB DDR4Nordwell NV4 1 TBkw6eNone, it runs on mains

Example:

lua
Config.Prebuilts = {
    -- A mid-range laptop for a police department's fleet
    police = {board = "ka1_7", ram = {"d5_16", "d5_16"}, ssd = "nv5_1tb", wifi = "kw7", battery = "zb61"},
}

#Hotspots

shared/unlocked/hotspots.lua lists the public Wi-Fi networks at places on the map, in Config.Hotspots. The laptop lists the ones in range of the player and joins them through the server; anywhere else it is online through the player's phone, see wifi.phone. Hotspots need the laptop's Wi-Fi card, whose generation and antenna leads set how far they reach. The defaults are 39 hotspots, 22 of them secured. See Wi-Fi & Browsing.

FieldDescription
idPlayers' laptops remember networks by it, so renaming one makes them forget it. Also the key of its password.
ssidThe name players see, the same in every language.
coordsWhere the router sits. Height counts too, so a rooftop can be out of reach of the street.
rangeMetres the signal reaches. The bars drop with the distance.
securedNeeds the password set in server/unlocked/hotspots.lua.

Example:

lua
Config.Hotspots = {
    {id = "lsia_terminal", ssid = "LSIA Free WiFi", coords = vec3(-1037.0, -2737.0, 20.2), range = 90.0, secured = false},
    {id = "mission_row_pd", ssid = "LSPD-MissionRow", coords = vec3(425.1, -979.5, 30.7), range = 60.0, secured = true},
}

shared/unlocked/hotspots.lua reaches every client, so never put a password there. The passwords of the secured hotspots are in server/unlocked/hotspots.lua, in HotspotPasswords by hotspot id, and only run on the server. Hand them out in roleplay: a barista writing it on a cup, a job sharing its office's network, a contact selling it.

lua
HotspotPasswords = {
    lsia_pegasus = "firstclassonly",
    mission_row_pd = "Pr0tect&Serve",
}

The server checks every join: the hotspot exists, the player is in range, the password matches for a secured hotspot, and CanJoinHotspot in server/unlocked/hooks.lua allows it. A secured hotspot without a password in HotspotPasswords can never be joined, like the FIB surveillance van and Fort Zancudo in the defaults. A password given for a hotspot that isn't secured is never asked for.

#Example Sites

shared/unlocked/examples.lua holds the example sites that come with the laptop, in Config.Examples. Each is a folder under examples/ with its code, pages and text, to use as it is or to copy as the start of your own site. Set enabled = false to leave one out. See Example Sites.

#Config.Examples.mechanic

lscustoms.com, a business site with photos and a button that sets a GPS route to the nearest shop.

KeyDescription
enabledEnables the site.
site.urlWhat players type into the address bar. It has to be unique across every site on the laptop.
site.titleThe site's title.
site.iconLucide icon name shown in search results and on the notification.
locationsThe shops listed on the site, {label, coords}. The route button picks the one closest to the player.

Example:

lua
mechanic = {
    enabled = true,
    site = {url = "lscustoms.com", title = "Los Santos Customs", icon = "wrench"},
    locations = {
        {label = "LS Customs Burton", coords = vec3(-337.38, -136.92, 38.57)},
        {label = "Beeker's Garage Paleto Bay", coords = vec3(110.99, 6626.39, 30.89)},
    },
}

#Config.Examples.police

lspd.gov, a police academy application form, and an "LSPD Recruitment" app for officers to accept or decline the applications. Stored in the zyke_laptop_police_applications table. To hire accepted applicants automatically, listen for the server event zyke_laptop:examples:police:ApplicationReviewed.

KeyDescription
enabledEnables the site and the app.
site.url, site.titleThe site's address and title.
jobJob whose members review applications.
reviewGradeLowest grade of that job that gets the recruitment app.
cooldownHoursHours a character waits after a declined application before applying again.
fieldsLength limits in characters, {min, max}, checked on the page and again on the server: age (in years), phone, availability, experience, motivation, and note, the reviewer's note, required when declining.

Example:

lua
police = {
    enabled = true,
    site = {url = "lspd.gov", title = "Los Santos Police Department"},
    job = "police",
    reviewGrade = 3,
    cooldownHours = 48,
}

#Config.Examples.arcade

Snake at snake.com and 2048 at 2048.com, with an all-time leaderboard per game in the zyke_laptop_arcade_scores table. The scores come from the player's browser, so don't hang money or items on them.

KeyDescription
enabledEnables the arcade.
debugPrints rejected scores and why to the server console.
boardSizeEntries on each game's all-time board.
submitCooldownMilliseconds a player waits between two submitted scores.
clockToleranceMilliseconds a game may claim to have lasted beyond what the server measured since it started, for latency.
gamesOne entry per game, each a site: id, url, title, page, rules handed to the page as they are, and limits.

The limits of a game reject scores that aren't possible: maxScore, minMsPerPoint with gracePoints for the fastest a score can be reached, and check, an optional function of (score, duration, stats) that returns whether the score is plausible. Snake's board and speed (cols, rows, startLength, startStepMs, minStepMs, speedUpMs) and 2048's (size, moveMs) are set at the top of the file. To add a game, add an entry, an examples/arcade/web/<id>/ folder with its page and "<id>:" keys in every examples/arcade/locales/*.lua file, at least "<id>:description" for the search results.

Example:

lua
arcade = {
    enabled = true,
    debug = false,
    boardSize = 10,
    submitCooldown = 4000,
    clockTolerance = 2000,
    games = {
        {
            id = "snake",
            url = "snake.com",
            title = "Snake",
            page = "examples/arcade/web/snake/index.html",
            rules = snake,
            limits = {
                maxScore = snake.cols * snake.rows - snake.startLength,
                gracePoints = 3,
                minMsPerPoint = snake.minStepMs * 3,
            },
        },
    },
}

#Casino Games & Cases

Each game of the casino has its own file in shared/unlocked/casino/, which adds it as Config.Casino.games.<id>. They only matter while casino.enabled is on. Every file has enabled, and enabled = false there removes only that game. Stakes and wins are in credits.

Most games share these keys:

KeyDescription
enabledEnables the game.
edgeShare of every bet the house keeps on average, 0.01 being 1%. Coinflip has rake instead, the share of the pot the house keeps.
minBetFewest credits one bet may be. Payouts round down to whole cents, so stakes below 1 credit lose a little more than the edge in most games.
maxBetMost credits one bet may be.
maxProfitMost credits one bet or round may win on top of its stake. Dice, Keno, Plinko and Wheel refuse a bet that could win more, so the player lowers the stake. Crash, Hilo, Limbo, Mines and Tower pay the cap instead, cashing out at once where the game can.
GameFileEdgeminBetmaxBetmaxProfitGame-specific keys
Blackjackblackjack.luaFrom the rules110000—tables (id, name, minBet, maxBet, seats 5 to 7), playerTables (enabled, max 20, closeSeconds 120), rules (decks 6, penetration, blackjackPays 1.5, dealerHitsSoft17, doubleAfterSplit, maxHands 4, insurance, surrender), bettingSeconds 15, decisionSeconds 12, insuranceSeconds 8, idleHands 5, botSeats 3
Case battlesbattles.luaThe cases' own———maxCases 25, maxCost 100000 per seat, maxOpen 3, expireMinutes 30, modes (1v1, 1v1v1, 1v1v1v1, 2v2, 3v3), crazy (lowest total wins), terminal (only the last case counts), botFeed false
Casescases.luaAbout 8%, from the catalogue———maxOpen 4 at once, demo free demo spins, vaultLimit 500, botFeed false
Coinflipcoinflip.luarake 0.050.110000—maxOpen 3, expireMinutes 30
Crashcrash.lua0.01110000100000minTarget 1.01, maxTarget 1000000, bettingSeconds 7, pauseSeconds 3, growth 0.00006 (2x after 11.6 seconds), bots (enabled, min 3, max 10)
Dicedice.lua0.01110000100000minChance 0.01, maxChance 98 (percent)
Hilohilo.lua0.01 per guess110000100000—
Kenokeno.luaAbout 1%, built into its payout tables110000100000—
Limbolimbo.lua0.01110000100000minTarget 1.01, maxTarget 1000000
Minesmines.lua0.01110000100000—
Plinkoplinko.luaAbout 1%, built into its payout tables110000100000—
Rouletteroulette.lua2.70%, single zero0.110000 for all of one player's bets on a spin—limits per bet type (straight 250 up to even 5000), chips, interval 25 seconds, statsSpins 100, bots (enabled, min 1, max 4)
Towertower.lua0.02110000100000—
Wheelwheel.lua0.01110000100000The layouts are built for 0.01; another edge scales every multiplier

The bots in Blackjack, Crash and Roulette also need casino.bots.enabled, and Blackjack's casino.bots.blackjack.

Example:

lua
Config.Casino.games.dice = {
    enabled = true,
    edge = 0.02,
    minBet = 1,
    maxBet = 5000,
    maxProfit = 50000,
    minChance = 0.01,
    maxChance = 98,
}

#Cases and items

The cases are Config.Casino.cases in shared/unlocked/casino/cases.lua, below the game's settings, and the items they drop are Config.Casino.items in shared/unlocked/casino/items.lua. Won items go to the player's vault and sell back for their full value in credits. The defaults are 8 cases, from Street Starter at 100 credits to High Roller at 50000, and 69 items.

  • A case is {id, label, price, image, drops}, with its price in credits. Each drop is {item, weight}. The weights are relative, and each default case totals about 100000, so a weight of 1 is a 0.001% chance.
  • An item is id = {label, category, rarity, value, image}, with its value in credits. The categories group items on the site (clothing, shoes, jewelry, watch, accessory, electronics, cash and vehicle), and the rarities are common, uncommon, rare, epic, legendary and mythic.
  • Images are paths in the casino's image folder, nui/casino/ in the built UI, such as cases/tech_drop.webp and items/cut_ruby.webp.
  • A drop naming an item that isn't in items.lua, or with a weight that isn't a whole number of at least 1, leaves its whole case out with a warning in the server console.

Keep each case's expected value, the sum of weight * value divided by the case's total weight, at about 92% of its price. That 8% is the house edge; a case worth more than it costs gives credits away.

Example:

lua
Config.Casino.items = {
    cut_ruby = {label = "Cut Ruby", category = "jewelry", rarity = "uncommon", value = 320, image = "items/cut_ruby.webp"},
}

Config.Casino.cases = {
    {id = "tech_drop", label = "Tech Drop", price = 300, image = "cases/tech_drop.webp", drops = {
        {item = "arcwave_controller", weight = 41224},
        {item = "veyra_boombox", weight = 27506},
        {item = "halden_telephoto", weight = 558},
    }},
}