How Integrations Work#
Ping Pong talks to the rest of your server through a few small, open files: custom/<framework>/server.lua for money, names and identifiers, custom/<framework>/client.lua for the player's data, and client/custom/ for notifications, the 3D prompt, sounds and ox_target. All of them are outside the escrow and are yours to change.
Layer | Supported | Without it |
|---|---|---|
Framework |
| Runs standalone: bets move no money, names come from FiveM and players are kept by license. |
Target |
| The [E] - Ping Pong prompt. |
Notifications |
| ox_lib notifications. |
3D prompt |
| Built-in 3D text on a dark box. |
Every optional integration degrades silently. A missing resource makes its part fall back to the built-in behaviour, with no error and nothing to configure.
Frameworks#
The framework is picked from whichever of these is already started when Ping Pong starts:
Resource | Mode |
|---|---|
|
|
|
|
|
|
None of them |
|
To force a mode instead of detecting it, replace the detection line in config/main.lua:
Config.Framework = 'qb' -- 'esx', 'qb' or 'standalone'Each mode has a folder in custom/ — esx, qb and standalone — with a server.lua and a client.lua. The server file answers five questions:
Function | Decides | Shipped behaviour |
|---|---|---|
| Whether a player can cover a stake. | Cash on ESX, QBCore and Qbox. Unlimited standalone. |
| Takes a stake. | From cash, logged as |
| Pays a win or a pot. | To cash, logged as |
| The name on the scoreboard, the player list, invitations and the leaderboard. | The character's name on ESX and QBCore; the FiveM name standalone. |
| Who a leaderboard row belongs to. | The ESX identifier, the QBCore |
Betting With the Bank or Your Own Currency#
Stakes are cash by default. To use the bank — or something else entirely, like casino chips or a VIP coin — change the three money functions in your framework's server file. For example, ESX with the bank account:
-- custom/esx/server.lua
function framework:getMoney(source)
local xPlayer = self:getPlayerFromId(source)
if not xPlayer then return 0 end
return xPlayer.getAccount('bank').money
end
function framework:removeMoney(source, amount)
local xPlayer = self:getPlayerFromId(source)
if not xPlayer then return false end
xPlayer.removeAccountMoney('bank', amount)
return true
end
function framework:addMoney(source, amount)
local xPlayer = self:getPlayerFromId(source)
if not xPlayer then return false end
xPlayer.addAccountMoney('bank', amount)
return true
endOr QBCore and Qbox paying in casino chips held in ox_inventory:
-- custom/qb/server.lua
function framework:getMoney(source)
return exports.ox_inventory:GetItemCount(source, 'casino_chips') or 0
end
function framework:removeMoney(source, amount)
return exports.ox_inventory:RemoveItem(source, 'casino_chips', amount)
end
function framework:addMoney(source, amount)
return exports.ox_inventory:AddItem(source, 'casino_chips', amount)
endChange all three together.
getMoneyis asked before every stake,removeMoneytakes it, andaddMoneypays wins and pots. If one of them still uses cash, stakes leave one account and wins land in another. They only ever run on the server.
ox_target#
With Config.UseTarget = true, the [E] prompt is switched off and every table model in Config.TableSkins — with and without its net — gets three options on the target eye, within Config.InteractDistance: Play vs Player, Play vs Bot and Leaderboard. They are added in client/custom/target.lua once the first table has loaded, and ox_target must start before Ping Pong. Only ox_target is supported.
Notifications, the Prompt and Sounds#
Three functions in client/custom/main.lua decide how Ping Pong looks and sounds next to the rest of your server:
Function | What it does | Default |
|---|---|---|
| Every message a player sees. |
|
| The [E] - Ping Pong prompt over the nearest table. |
|
| The | GTA V's own sounds. |
To use another notification system, change the body of Notification:
-- client/custom/main.lua
function Notification(msg, type)
exports['okokNotify']:Alert('Ping Pong', msg, 3000, type or 'info')
endThe shipped qs-interface branch handles info, success and error. Add a warning case to it if you want Your opponent has left the match. to show there as well.
Table and Racket Models#
Config.TableSkins lists the table models Ping Pong knows, each with and without its net, and gives each one a label. The creator places Config.Creator.model — prop_table_tennis, the blue table. To place another one, set it there and keep it in Config.TableSkins so it gets its label and its ox_target options.
Config.TableSkins = {
{ net = 'prop_table_tennis', nonet = 'prop_table_tennis_nonet', label = 'Blue' },
{ net = 'prop_table_tennis_b', nonet = 'prop_table_tennis_b_nonet', label = 'Green' },
-- ...
}
Config.Creator = {
permission = 'command.pingpongcreator',
model = `prop_table_tennis_b`, -- the green table
rotateSpeed = 80.0,
heightStep = 0.05,
}The players' racket is the first entry of Config.RacketSkins; the bot holds prop_table_ten_bat. Rackets held by a character sit on the bone and offsets in Config.Racket, and the ball is prop_ping_pong (Config.Ball.model).
Languages#
41 translations ship in locales/: Arabic (ar), Bulgarian (bg), Bengali (bn), Catalan (ca), Czech (cs), Danish (da), German (de), Greek (el), English (en), Spanish (es), Estonian (et), Persian (fa), Finnish (fi), French (fr), Hebrew (he), Hindi (hi), Croatian (hr), Hungarian (hu), Indonesian (id), Italian (it), Japanese (ja), Korean (ko), Lithuanian (lt), Latvian (lv), Malay (ms), Dutch (nl), Norwegian (no), Polish (pl), Portuguese (pt), Romanian (ro), Russian (ru), Slovak (sk), Slovenian (sl), Swedish (sv), Swahili (sw), Thai (th), Turkish (tr), Ukrainian (uk), Urdu (ur), Vietnamese (vi) and Chinese (zh). Pick one in config/main.lua:
Config.Locale = 'es'One file translates the notifications and the whole interface, the creator included. A key missing from a translation is filled in from English, with a Missing locale key warning in the console; an unknown code falls back to English entirely. To add a language, copy en.json, translate the values — never the keys — save it under the new code and set Config.Locale to it.



