
Documentation
Integrate the script with your server and connect it to your existing systems using the available developer API and events.
Returns the client-side MotelManager instance.
local manager = exports['qs-motels-creator']:getMotelManager()Method | Returns |
|---|---|
| Motel data |
| Motel the player is inside |
| Closest motel |
| Rentals |
| Employment check |
| Key / permission check |
Triggers a raid on the nearest room, respecting every Config.raid check.
exports['qs-motels-creator']['motels:raid']()Force-evicts a tenant. Locks the room, notifies the player and clears the rental.
exports['qs-motels-creator']:kickCustomer(identifier)Parameters
Parameter | Type | Description |
|---|---|---|
| string | Player identifier ( |
A global on the server side, available from server/custom/* and any file loaded after it.
ServerMotelManager:getMotel(motelId)
ServerMotelManager:getAllMotels()
ServerMotelManager:getMotelsByOwner(identifier)
ServerMotelManager:syncMotel(motelId)
ServerMotelManager:saveAll()syncMotel broadcasts the motel to every connected client, so changes appear without a restart.
ServerMotelManager:getRental(motelId, roomIndex)
ServerMotelManager:getRentalByIdentifier(identifier) --> rental, motelId
ServerMotelManager:isRoomAvailable(motelId, roomIndex)
ServerMotelManager:getEmptyRooms(motelId)
ServerMotelManager:rentRoom(motelId, roomIndex, identifier, name, duration, price) --> ok, rental
ServerMotelManager:leaveRoom(motelId, roomIndex)
ServerMotelManager:kickTenant(motelId, roomIndex)rentRoom returns ok, rental. The price is always recomputed server-side, so the value you pass is validated, not trusted.
ServerMotelManager:shareKeyWithPermissions(motelId, roomId, ownerIdentifier, guestIdentifier, guestName, permissions)
ServerMotelManager:revokeKeyShare(shareId)
ServerMotelManager:getKeyShares(motelId, roomId)
ServerMotelManager:hasKey(identifier, motelId, roomId)
ServerMotelManager:hasGuestPermission(guestIdentifier, motelId, roomId, permission)Valid permissions: door · stash · wardrobe · extend · pay · request · create key
ServerMotelManager:getWorkers(motelId)
ServerMotelManager:addWorker(motelId, identifier, name, salary, permissions, isOwner)
ServerMotelManager:removeWorker(motelId, identifier)
ServerMotelManager:isOwner(motelId, identifier)
ServerMotelManager:isOwnerOrWorker(motelId, identifier)
ServerMotelManager:hasPermission(motelId, identifier, permission)ServerMotelManager:getBalance(motelId)
ServerMotelManager:deposit(motelId, amount, description, identifier, name)
ServerMotelManager:withdraw(motelId, amount, description, identifier, name)
ServerMotelManager:createBill(identifier, motelId, amount, description)
ServerMotelManager:hasBills(identifier)
ServerMotelManager:payBill(identifier, billId)Every deposit and withdrawal is written to the transaction ledger, so the finance dashboard stays accurate.
shared/pricing.lua is loaded on both sides and is the single source of truth for rent maths.
GetRentUnitMs(motel) -- ms per pricing unit
ComputeRentPrice(motel, durationMs) -- authoritative price
IsValidRentDuration(motel, durationMs)
ValidateRentPrice(motel, clientPrice, durationMs)Always use ComputeRentPrice rather than calculating a price yourself — the server validates against it.
A set of callbacks so a phone app can list motels, quote prices and rent rooms server-authoritatively.
lib.callback.await('motels:phone:getBootstrap', false)
lib.callback.await('motels:phone:getMotelRooms', false, motelId)
lib.callback.await('motels:phone:quoteRent', false, motelId, durationMs)
lib.callback.await('motels:phone:rentRoom', false, motelId, roomIndex, durationMs)
lib.callback.await('motels:phone:getStaffRequests', false)
lib.callback.await('motels:phone:resolveRequest', false, requestId, status)rentRoom returns ok, rental on success, or false, reason on failure.
Reason | Meaning |
|---|---|
| The motel id does not exist |
| The room is already rented |
| Duration outside the motel's allowed range |
| Room locked behind the |
| Blocked by |
|
|
| Insufficient funds |
| Player already rents at this motel |
The price is always recomputed server-side — a client-supplied price is never trusted.
Listen to these from your own resource.
Event | Args |
|---|---|
|
|
|
|
|
|
|
|
|
|
| Rent warning payload |
| (triggers the raid routine) |
|
|
Client → server events exist for every UI action, but they are not a trusted API — they re-verify identifiers, ownership, permissions and prices on their own. Use the exports and
ServerMotelManagerinstead.
Duplicate custom/esx/ to custom/<name>/ and add the value to the frameworks table in config/main.lua. The loader uses lib.load, so the folder name must match the config value.
The loaded tables are exposed globally as sfr (server) and cfr (client), and both interfaces are typed in types.lua — implement every field listed there.
Create server/custom/inventory/adapters/<name>.lua:
---@param stashId string -- "motelId_roomIndex_stashIndex", or "motel_stash" when personal
---@param ctx table -- { personal, tenantIdentifier, tenantSrc, motelId, roomIndex }
local function wipe(stashId, ctx)
if ctx.personal and ctx.tenantIdentifier then
exports.my_inventory:ClearStash(stashId, ctx.tenantIdentifier)
return
end
exports.my_inventory:ClearStash(stashId)
end
MotelStashWipe.register('myinv', { wipe = wipe })The registration name must match the value your inventory maps to in the inventories table in config/main.lua. Adapters already ship for qs, ox, qb, origen, codem and standalone.
server/custom/main.lua
Function | Purpose |
|---|---|
| Builds and hands out the key item |
| Route motel messages to your own chat / phone |
| Register stashes with your inventory at boot |
| Re-route reconnecting players into their room |
| Owner / employee key bypass rules |
client/custom/main.lua
Function | Purpose |
|---|---|
| Swap for ox_lib, qs-interface, or your own notify |
| TextUI provider |
| Progress provider |
| Hide your HUD inside rooms |
| 3D audio provider |
| Clothing integration |
| Raid routine, also exported as |
Weather and time freezing inside rooms is handled at the top of client/custom/main.lua (qb-weathersync, cd_easytime, vSync, av_weather) — extend it for your own sync resource.
local rental, motelId = ServerMotelManager:getRentalByIdentifier(identifier)
if rental then
print(('Player rents room %s at motel %s'):format(rental.room_id, motelId))
endServerMotelManager:withdraw(motelId, 5000, 'Maintenance fee', identifier, playerName)if exports['qs-motels-creator']:getMotelManager():hasKeyFor(motelId, roomIndex) then
-- Your custom logic here
end