Before You Begin#
Before installing Smartphone V3, make sure your server has everything it needs.
Requirement | Status | Notes |
|---|---|---|
| Required | Database access. |
| Required | Callbacks and shared helpers. |
Documentation
Requirements, download, load order, the database, inventory items, media uploads and the TURN server for video calls.
Before installing Smartphone V3, make sure your server has everything it needs.
Requirement | Status | Notes |
|---|---|---|
| Required | Database access. |
| Required | Callbacks and shared helpers. |
| Required | Ringtones, music and phone sounds. If |
| Required | The phone models in the player's hand. Included in the download. |
OneSync | Required | Must be enabled on the server. |
MariaDB 10.6+ | Required | Older versions, and local tools like XAMPP, fail to import the SQL. |
Voice | Required for calls |
|
Framework | Optional | ESX, QBCore, Qbox or vRP (experimental), detected automatically. Without one the phone runs standalone. |
Inventory | Recommended | An inventory with metadata gives every phone item its own number and data. |
FiveManage or FiveMesh | Required for media | Stores photos, videos and voice notes. See Media Uploads below. |
TURN server | Required for video | Video calls and Pictagram live streams. Cloudflare or Metered. See Video Calls below. |
After purchase, the resource is linked to your CFX Portal account automatically. Sign in to the CFX Portal with the same account used at checkout, open Granted Assets and download Smartphone V3.
After extracting the download, you should have this folder:
[smartphone]/
├── qs-smartphone/ -- the phone (config/ is what you edit)
├── smartphone-prop/ -- phone models, required
├── [addons]/ -- free DLC apps, optional
├── [sql]/database.sql -- the database
├── [installation]/ -- item lists for each inventory
├── [images]/ -- inventory icons
├── [template]/ -- starter projects for your own apps
└── [examples]/ -- phone-business-line exampleKeep the folder named
qs-smartphone. The interface, the exports and every DLC app look for the phone by that name.
Move the [smartphone] folder into your server's resources directory:
resources/
└── [qs]/
└── [smartphone]/
├── qs-smartphone/
└── smartphone-prop/Then open your server.cfg and start every dependency before the phone:
ensure oxmysql
ensure ox_lib
# Framework
ensure es_extended
# ensure qb-core
# ensure qbx_core
# Inventory
ensure qs-inventory
# Voice and sound
ensure pma-voice
ensure xsound
# Phone
ensure smartphone-prop
ensure qs-smartphone
# Optional DLC apps, always after the phone
# ensure qs-smartphone-aura
# ensure phone-radio
# ensure phone-recorder
# ensure phone-boothThe loading order matters. The phone detects your framework, inventory, garage and billing scripts only if they have already started, so a script started after the phone is not found. Never place the phone above your framework or inventory.
To set the language for every Quasar resource at once, add this line to server.cfg:
setr qs:locale "es"Import [sql]/database.sql into your server's database with HeidiSQL, phpMyAdmin or the MySQL command line. It creates every table the phone and its apps need, all prefixed with qs_phone_.
Tables | Hold |
|---|---|
metadata · numbers · backups · revoked_devices | Each phone's settings, the numbers in use, backups and stolen or wiped devices. |
contacts · call_recents · messages · mail | Contacts, call history, conversations and email. |
notes · reminders · calendar · clock · gallery · health | Personal data, alarms, saved photos and videos, and step counts. |
pictagram · tweedle · beatzy · chitchat · finder · darkchat · qchat | Accounts, posts and chats of the social apps. |
wallet · bills · crypto · market · onion | Transfers, standalone bills, coin holdings, Marketplace shops and Onion Browser orders. |
zapp · zappeats · weazel · yellowpages · store_downloads | Rides, deliveries, news articles, Findly listings and installed apps. |
The file is safe to import again after an update. It only creates the tables and columns that are missing and never deletes data.
Add the items below to your inventory. Only phone is required; the others turn on their own features. The item names must match config/main.lua, and the icons are in the [images] folder.
Item | Config | What it does |
|---|---|---|
|
| The phone. Opens the Pear skin when the skin is tied to the item. |
|
| The Nova phone. Only used when |
|
| The foldable Pear phone. |
|
| The foldable Nova phone. |
|
| A SIM card. Using it gives the phone a new number. |
|
| Charges the phone (and the earbuds case) anywhere. |
|
| Private audio: only the owner hears music, videos and calls. |
Add to qs-inventory/shared/items.lua:
['phone'] = {
['name'] = 'phone',
['label'] = 'Phone',
['weight'] = 190,
['type'] = 'item',
['image'] = 'phone.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['description'] = 'A mobile phone'
},
-- Optional: Nova phone (Config.Platform.mode = 'item')
['phone_android'] = {
['name'] = 'phone_android',
['label'] = 'Nova Phone',
['weight'] = 190,
['type'] = 'item',
['image'] = 'phone_android.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['description'] = 'A Nova mobile phone'
},
-- Optional: foldable phones
['phone_duo'] = {
['name'] = 'phone_duo',
['label'] = 'Foldable Phone',
['weight'] = 250,
['type'] = 'item',
['image'] = 'phone_duo.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['description'] = 'A foldable mobile phone'
},
['phone_fold'] = {
['name'] = 'phone_fold',
['label'] = 'Foldable Nova Phone',
['weight'] = 250,
['type'] = 'item',
['image'] = 'phone_fold.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['description'] = 'A foldable Nova mobile phone'
},
['wireless_earbuds'] = {
['name'] = 'wireless_earbuds',
['label'] = 'Wireless Earbuds',
['weight'] = 120,
['type'] = 'item',
['image'] = 'wireless_earbuds.png',
['unique'] = false,
['useable'] = true,
['shouldClose'] = true,
['description'] = 'Wireless earbuds for your phone'
},
['powerbank'] = {
['name'] = 'powerbank',
['label'] = 'Powerbank',
['weight'] = 300,
['type'] = 'item',
['image'] = 'powerbank.png',
['unique'] = false,
['useable'] = true,
['shouldClose'] = true,
['description'] = 'Portable battery charger'
},
['phone_sim'] = {
['name'] = 'phone_sim',
['label'] = 'SIM Card',
['weight'] = 45,
['type'] = 'item',
['image'] = 'phone_sim.png',
['unique'] = true,
['useable'] = true,
['shouldClose'] = true,
['description'] = 'A SIM card for your phone'
},Add to ox_inventory/data/items.lua:
['phone'] = {
label = 'Phone',
weight = 190,
stack = false,
consume = 0,
-- Optional: open the phone in the used slot (see below)
-- server = { export = 'qs-smartphone.useItem' }
},
-- Optional: Nova phone (Config.Platform.mode = 'item')
['phone_android'] = {
label = 'Nova Phone',
weight = 190,
stack = false,
consume = 0,
server = { export = 'qs-smartphone.useItem' }
},
-- Optional: foldable phones
['phone_duo'] = {
label = 'Foldable Phone',
weight = 250,
stack = false,
consume = 0,
server = { export = 'qs-smartphone.useItem' }
},
['phone_fold'] = {
label = 'Foldable Nova Phone',
weight = 250,
stack = false,
consume = 0,
server = { export = 'qs-smartphone.useItem' }
},
['wireless_earbuds'] = {
label = 'Wireless Earbuds',
weight = 120,
stack = true,
close = true,
server = { export = 'qs-smartphone.useWirelessEarbuds' }
},
['powerbank'] = {
label = 'Powerbank',
weight = 300,
stack = true,
close = true,
server = { export = 'qs-smartphone.usePowerbank' }
},
['phone_sim'] = {
label = 'SIM Card',
weight = 45,
stack = false,
consume = 0,
close = true,
server = { export = 'qs-smartphone.useSimCard' }
},Add to qb-core/shared/items.lua (or your Qbox items file):
phone = { name = 'phone', label = 'Phone', weight = 190, type = 'item', image = 'phone.png', unique = true, useable = true, shouldClose = true, description = 'A mobile phone' },
-- Optional: Nova phone (Config.Platform.mode = 'item')
phone_android = { name = 'phone_android', label = 'Nova Phone', weight = 190, type = 'item', image = 'phone_android.png', unique = true, useable = true, shouldClose = true, description = 'A Nova mobile phone' },
-- Optional: foldable phones
phone_duo = { name = 'phone_duo', label = 'Foldable Phone', weight = 250, type = 'item', image = 'phone_duo.png', unique = true, useable = true, shouldClose = true, description = 'A foldable mobile phone' },
phone_fold = { name = 'phone_fold', label = 'Foldable Nova Phone', weight = 250, type = 'item', image = 'phone_fold.png', unique = true, useable = true, shouldClose = true, description = 'A foldable Nova mobile phone' },
wireless_earbuds = { name = 'wireless_earbuds', label = 'Wireless Earbuds', weight = 120, type = 'item', image = 'wireless_earbuds.png', unique = false, useable = true, shouldClose = true, description = 'Wireless earbuds for your phone' },
powerbank = { name = 'powerbank', label = 'Powerbank', weight = 300, type = 'item', image = 'powerbank.png', unique = false, useable = true, shouldClose = true, description = 'Portable battery charger' },
phone_sim = { name = 'phone_sim', label = 'SIM Card', weight = 45, type = 'item', image = 'phone_sim.png', unique = true, useable = true, shouldClose = true, description = 'A SIM card for your phone' },Run this in your database:
INSERT INTO `items` (`name`, `label`, `weight`) VALUES
('phone', 'Phone', 190),
-- Optional: Nova phone (Config.Platform.mode = 'item')
('phone_android', 'Nova Phone', 190),
-- Optional: foldable phones
('phone_duo', 'Foldable Phone', 250),
('phone_fold', 'Foldable Nova Phone', 250),
('wireless_earbuds', 'Wireless Earbuds', 120),
('powerbank', 'Powerbank', 300),
('phone_sim', 'SIM Card', 45);Photos, videos and voice notes are stored on a media host, not on your server. Without one, the Camera, the video recorder and voice notes cannot save anything. Set it up in qs-smartphone/server/webhooks.lua.
Create an account on FiveManage, generate an API token in the dashboard and paste it here:
_G.fivemanage = {
token = 'YOUR_FIVEMANAGE_TOKEN'
}Create a service key in the FiveMesh dashboard (Account → API keys) with the cdn:write permission. When apiKey is filled in, the phone uses FiveMesh instead of FiveManage.
_G.fivemesh = {
apiKey = 'YOUR_FIVEMESH_KEY', -- server side only, never shared with players
path = 'phone', -- base folder for every upload
folders = { image = 'images', audio = 'audios', video = 'videos' },
expiresIn = 300,
maxFileSize = {
image = 12 * 1024 * 1024, -- 12 MB
audio = 25 * 1024 * 1024, -- 25 MB
video = 100 * 1024 * 1024 -- 100 MB
},
}To upload to your own server, edit the upload function in qs-smartphone/web/build/custom-upload.js. It must return the public URL of the uploaded file.
The same file holds the Discord webhooks for Pictagram, Chirpy, Beatzy, Findly and News. Leave an app's URL empty to use global instead.
Video calls and Pictagram live streams travel directly between players. Most players sit behind a router that blocks a direct connection, so a TURN server relays the video for them. Without one, video calls are disabled and players see a warning; voice calls still work.
Open Cloudflare Realtime, go to the TURN tab and press Create TURN App. It must be the TURN product, not SFU: SFU keys return a 404.
Copy the TURN Token ID and the API Token. The API Token is shown only once.
In qs-smartphone/config/main.lua:
Config.PhoneWebRTC = {
cloudflare = {
enabled = true,
turnKeyId = 'YOUR_TURN_TOKEN_ID',
apiToken = 'YOUR_API_TOKEN',
ttlSeconds = 86400,
refreshSkewSeconds = 600,
retryOnFailMs = 30000,
},
-- ...
}The server fetches the credentials on start and renews them before they expire. Check them from the server console with phone:turn:status.
Create an account on Metered and generate TURN credentials in the dashboard.
In qs-smartphone/config/main.lua, keep Cloudflare disabled and add the servers Metered gives you:
iceServers = {
{ urls = 'stun:stun.l.google.com:19302' },
{
urls = {
'turn:global.relay.metered.ca:80',
'turn:global.relay.metered.ca:80?transport=tcp',
'turn:global.relay.metered.ca:443',
'turns:global.relay.metered.ca:443?transport=tcp',
},
username = 'YOUR_METERED_USERNAME',
credential = 'YOUR_METERED_CREDENTIAL'
},
},Keep
requireTurnForVideo = true. Turning it off allows video calls without a TURN server, but most players will see a black screen. SetforceRelay = trueif direct connections are unstable for your players.
Everything you may edit is in qs-smartphone/config/. Each value is tagged in the file: [EDIT] is safe to change, [ADV] only if you know what it does, and [CORE] / [AUTO] should be left alone.
File | Contents |
|---|---|
| Language, opening the phone, Pear/Nova skins, foldables, numbers, calls, battery, signal, earbuds, SIM, map, and the settings of every built-in app. |
| The apps players download from the App Store, their size and price. |
| The shops in the Marketplace app and the jobs that staff them. |
| The products sold in the Peach Store app: powerbank, earbuds, SIM, storage. |
| Phone storage capacity and how much photos and videos take. |
| The playlists in the Music app. |
| The guides shown in the Tips app. |
| Media upload keys and Discord webhooks. |
| Framework bridges: money, jobs, items, admin check and when the phone is blocked (for example while handcuffed). |
The values worth reviewing first:
Config.Locale = GetConvar('qs:locale', 'en') -- default language
Config.Debug = false -- turn off in production
Config.Phone = {
openKey = 'F1', -- key that opens the phone
openByKeyPress = true,
requireItem = true, -- the player needs the phone item
itemName = 'phone',
blockWhenRestricted = true, -- no phone while handcuffed
}
Config.Platform = {
mode = 'item', -- 'item' = phone (Pear) / phone_android (Nova) items | 'settings' = switch in Settings
default = 'ios', -- 'ios' = Pear, 'android' = Nova
androidItemName = 'phone_android',
}
Config.MetaSystem = {
enabled = true, -- each phone item has its own number and data
phoneNumberPrefix = '555', -- always a string
phoneNumberLength = 7,
phoneNumberMask = '(XXX) XXX-XXXX',
}
Config.Mail = { emailSuffix = '@cloud.com' }Config files are read when the resource starts. Restart
qs-smartphoneafter every change. ChangingConfig.MetaSystem.enabledon a live server changes where phone data is stored, so players lose access to their old data. Choose it before opening the server.
Restart the server and check the console: qs-smartphone must start without red errors. Then join and work through the first loop:
Give yourself the phone item and press F1. The first boot walks you through language, appearance, passcode and Face Unlock.
Open Settings to see your number, then call a second player. If you configured TURN, try a video call too.
Take a photo with the Camera and open it in Gallery. If it appears, media uploads are working.
Something not working? See Common Errors.
Several phones in one inventory. The open key always opens the first phone found. To open the phone in the slot the player used, uncomment server = { export = 'qs-smartphone.useItem' } on the phone item, then comment out the npwd handler that ox_inventory ships in ox_inventory/modules/items/client.lua:
-- Item('phone', function(data, slot)
-- local success, result = pcall(function()
-- return exports.npwd:isPhoneVisible()
-- end)
--
-- if success then
-- exports.npwd:setPhoneVisible(not result)
-- end
-- end)Restart ox_inventory after both changes.