Example Sites
The example sites that ship with the laptop, and how to build your own from one.
The laptop ships with three example sites in its examples/ folder. They start with the laptop and each is a starting point to copy for your own site or app.
| Example | Address | What it shows |
|---|---|---|
| Mechanic | lscustoms.com | A business site with photos and a button that sets a GPS route to the nearest shop. |
| Police | lspd.gov | A police academy application form, and an LSPD Recruitment app for officers to accept or decline applications. |
| Arcade | snake.com, 2048.com | Snake and 2048, each with an all-time leaderboard. |
#Turning Them On or Off
Each example has an enabled switch and its settings in shared/unlocked/examples.lua, under Config.Examples.
| Key | Default | Description |
|---|---|---|
mechanic.enabled | true | Registers lscustoms.com. |
police.enabled | true | Registers lspd.gov and the recruitment app, and creates its table. |
arcade.enabled | true | Registers snake.com and 2048.com, and creates the scores table. |
An example that is turned off registers nothing and creates no table.
#Mechanic
mechanic.site holds the address, title and the Lucide icon shown in search results. mechanic.locations lists the shops on the site, each {label, coords}. The route button sets a GPS route to the shop closest to the player.
This example runs on the client only, with no server script or table.
#Police
The site is a public application form. A character can have one pending application at a time, and after a declined one waits cooldownHours (48 by default) before applying again.
The LSPD Recruitment app lists the applications by status and lets a reviewer accept or decline them. A note is required when declining. Reviewers can't review their own application. An applicant who is online gets a laptop notification when their application is reviewed.
| Key | Default | Description |
|---|---|---|
police.site | lspd.gov | Address and title of the site. |
police.job | "police" | Job whose members review applications. |
police.reviewGrade | 3 | Lowest grade of that job that gets the recruitment app. |
police.cooldownHours | 48 | Hours after a declined application before the character can apply again. |
police.fields | Length limits of each form field and the reviewer's note, checked on the page and again on the server. age is the allowed age range. |
The app is only registered on the clients of officers of reviewGrade and up, and follows job changes. The server checks the job and grade again on every reviewer request. With the app store on, officers download the app from the store on Facade, Lumen and Solace. Add example_police_app to Config.Settings.appStore.preinstalled to have it installed from the start.
Applications are kept in zyke_laptop_police_applications, created on start. status is pending, accepted or declined.
#Application Reviewed
Fires on the server after a reviewer accepts or declines an application. The application is never hired automatically; listen here to set the job yourself.
The application carries
id,identifier(the applicant's character),name,status,age,phone,availability,experience,motivation,note,reviewerName,createdAtandreviewedAt(seconds since the epoch).Example:
lua---@param application table AddEventHandler("zyke_laptop:examples:police:ApplicationReviewed", function(application) if (application.status ~= "accepted") then return end local plyId = Z.getPlayerId(application.identifier) if (not plyId) then return end -- Offline, hire them on their next login -- Set the job through your framework or job script here, for example to police grade 0 end)
#Arcade
Every game is a site listed in arcade.games, with its id, url, title, page, the rules handed to the page, and limits the server checks scores against.
| Key | Default | Description |
|---|---|---|
arcade.boardSize | 10 | Entries on each game's all-time board. |
arcade.submitCooldown | 4000 | Milliseconds between two submitted scores. |
arcade.clockTolerance | 2000 | Milliseconds a game may claim to have lasted beyond what the server measured, for latency. |
arcade.debug | false | Prints rejected scores and why to the server console. |
The server only saves scores that are possible in the time the game took: under maxScore, not faster than minMsPerPoint allows, and passing the game's own check function when it has one. Each character keeps their best score per game in zyke_laptop_arcade_scores, created on start.
Arcade scores come from the player's browser. Don't hang money or items on them.
To add a game, add an entry to arcade.games, 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.
#How They Are Built
Each folder holds what a resource of its own would:
- A client script that registers the site with
RegisterSite, andRegisterAppfor the police app. See Apps & Sites. - Server scripts that check every request.
- Its text in
locales/. - The pages in
web/, as plain HTML, CSS and JavaScript with no build step.
The pages read their text through an NUI callback and post to the client script with Laptop.post from the JavaScript SDK. The client script passes requests on to the server where needed. Every callback name starts with the example's name, such as mechanic:GetSite, so they never clash with the laptop's own.
The server takes nothing from a page on trust. Every server callback checks IsAtLaptop, the police reviewer callbacks check the job and grade again, and the arcade checks every score.
#Making Your Own Site From One
Move the folder into a resource of your own, then:
- Load its scripts in your
fxmanifest.lua, withlocales/*.luaandweb/**/*underfiles. - Copy its block from
shared/unlocked/examples.luainto a config of your own. - Read the text with zyke_lib's
TandTranslationsinstead ofLoadLaptopExampleLocale. - Change the
uipaths to the folder's place in your resource, give the site a uniqueidandurl, and rename the callbacks, events and table. - Register the site again on the client event
zyke_laptop:Ready, which fires when the laptop restarts, as well as when your resource starts.
Pages are served from the resource, so restart it after editing them. Set the example's enabled to false in the laptop so the original doesn't run next to your copy.
#Working on a Page in a Browser
Pages can be worked on in a normal browser, outside the game.
- Serve your resources folder over HTTP, for example by running
python3 -m http.serverin it. - Open a page, such as
zyke_laptop/examples/mechanic/web/index.html.
Outside the game, each page answers its own requests from its mock.js (standalone.js for the arcade), with the English text from its locales/en.lua.
- The police pages take
?state=to pick what the server would answer:pending,accepted,declined,cooldown,serverErrorsornotAtLaptopon the site, andempty,noAccessorconflictin the app.?lang=depicks another language. - The arcade brings its own stand-in for the SDK, and the police pages find the laptop's copy themselves.
- The mechanic page loads the SDK from
https://cfx-nui-zyke_laptop/nui/sdk/laptop.js, which only resolves in game. Point that script tag at../../../nui/sdk/laptop.jswhile working in a browser, and put it back before restarting.