Concepts

OOP

vRP 2 Object-Oriented Programming uses the Luaoop library.

By using OOP, instead of loading resources communicating with vRP through Proxy, extensions can directly be loaded into the vRP resource context. Each extension keeps its data and is able to access other extensions data and methods, making extending vRP easier, more powerful (we can access stuff even if not exposed) and with less overhead.

Asynchronous

vRP 2 extensively uses asynchronous tasks in a transparent way using coroutines, some functions may not return immediately (or never).

DB driver

A DB driver is a class handling MySQL queries. When the vRP MySQL API is used, the DB driver methods are called to process the queries. If the DB driver is not loaded yet, queries will be cached until loaded.

Proxy and Tunnel

The proxy library is used to call other resources functions through a proxy event.

The idea behind tunnel is to easily access any exposed server function from any client resource, and to access any exposed client function from any server resource.

Good practice is to get the interface once and set it as a global. If we want to get multiple times the same interface from the same resource, we need to specify a unique identifier (the name of the resource + a unique id for each one).
Tunnel and Proxy are blocking calls in the current coroutine until the values are returned, to bypass this behaviour, especially for the Tunnel to optimize speed (ping latency of each call), use _ as a prefix for the function name (Proxy/Tunnel interfaces should not have functions starting with _). This will discard the returned values, but if we still need them, we can make normal calls in a new Citizen thread with Citizen.CreateThreadNow or async to have non-blocking code.
Also remember that Citizen event handlers (used by Proxy and Tunnel) may not work while loading the resource; to use the Proxy at loading time, we will need to delay it with Citizen.CreateThread or a SetTimeout.

Data

Multi-server and character

vRP 2 has multi-server and multi-character support. Each server has a string identifier.

Players can use another character after spawned, thus extensions should properly handle character load/unload events and check if the character is ready.

Key-value stores

vRP provides a key-value API to store custom binary data in the database. A string key is associated to a binary value (ex: a Lua string). The key is a VARCHAR(100) and the value a BLOB (probably 65535 bytes).

Types
global

GData

per server

SData

per user

UData

per character

CData

vRP core tables, as core files, should not be modified for extensions compatibility. New tables and queries should be created if the key-value system is not powerful enough.

Core

vRP core modules data may be stored in specific MySQL tables or using the key-value stores. For the latter, the keys will probably be prefixed by vRP and the binary data will use the MessagePack format.

Script loading

To use vRP 2, a script must be loaded in the vRP resource context of a specific side.

-- include `@vrp/lib/utils.lua` in `__resource.lua` (for the targeted side)

local Proxy = module("vrp", "lib/Proxy")
local vRP = Proxy.getInterface("vRP")

vRP.loadScript("my_resource", "vrp") -- load "my_resource/vrp.lua"

The content of vrp.lua is now executed in the vRP context and can now use the API.

vRP.loadScript() is a proxy to call module() in the vRP context.

Extension

An extension is a class extending vRP.

Two versions of the same extension (same name) can be loaded: for the server-side and the client-side. They will be able to interact with each other through the tunnel/remote interfaces.

local MyExt = class("MyExt", vRP.Extension)

Loaded extensions are accessibles through the vRP instance:

vRP.EXT.MyExt:test()
You can see how an extension is made by looking at the code of vRP modules or https://github.com/vRP-framework/vRP-basic-mission.

User

Extensions can extend User properties/methods with a User class (constructor is called).

To not conflict with other extensions, make sure the added properties and methods have a very specific name or prefix.
MyExt.User = class("User")

Event

Extensions can listen to global events by defining methods in the event table.

MyExt.event = {}

function MyExt.event:playerSpawn(user, first_spawn)
end
Events marked with (sync) in the documentation may be called using vRP:triggerEventSync which will wait for the listeners to complete, meaning that listeners must return (mostly in a short time frame) in order to let the execution continue normally.

Proxy and Tunnel

Extensions can listen to proxy/tunnel calls by defining methods in the proxy or tunnel table.

MyExt.proxy = {}
function MyExt.proxy:getInfo()
end

-- client-side
MyExt.tunnel = {}
function MyExt.tunnel:test()
end

The proxy interface generated will be accessible from other resources like this:

local my_ext = Proxy.getInterface("vRP.EXT.MyExt")
local info = my_ext.getInfo()
Extensions don’t need and should not use proxy between them.

The tunnel is accessible (from the client-side or server-side extension) through the remote table.

-- server-side
function MyExt.event:playerSpawn(user, first_spawn)
  self.remote._test(user.source)
end

-- client-side
function MyExt.event:playerDeath()
  self.remote._test()
end

Profiler

vRP embeds ELProfiler to profile Lua code, vRP and resources based on it. When a resource loads @vrp/lib/utils.lua (which is the case for resources based on vRP), it will setup itself to be recorded by the profiler.

To use the profiler, the module must be enabled in cfg/modules.lua. This will keep track of created coroutines to profile them. The overhead is probably small, thus it can be enabled on a live server.

Two options are available (with permissions) in the main and admin menus to respectively profile the client-side or server-side.

Profiling has an overhead, but mostly because of the Lua debug hook. Being a statistical/sampling profiler, profiling a long period of time is fine (low memory usage).

General API

utils

lib/utils.lua defines some useful globals.

-- side detection
SERVER -- boolean
CLIENT -- boolean

-- load a lua resource file as module (for a specific side)
-- rsc: resource name
-- path: lua file path without extension
module(rsc, path)

class -- Luaoop class

-- create an async returner or a thread (Citizen.CreateThreadNow)
-- func: if passed, will create a thread, otherwise will return an async returner
async(func)

-- convert Lua string to hexadecimal
tohex(str)

-- basic deep clone function (doesn't handle circular references)
clone(t)

parseInt(v)

-- will remove chars not allowed/disabled by strchars
-- allow_policy: if true, will allow all strchars, if false, will allow everything except the strchars
sanitizeString(str, strchars, allow_policy)

splitString(str, sep)

Proxy and Tunnel

Proxy

-- add event handler to call interface functions
-- name: interface name
-- itable: table containing functions
Proxy.addInterface(name, itable)

-- get a proxy interface
-- name: interface name
-- identifier: (optional) unique string to identify this proxy interface access; if nil, will be the name of the resource
Proxy.getInterface(name, identifier)

Tunnel

-- set the base delay between Triggers for a destination
-- dest: player source
-- delay: milliseconds (0 for instant trigger)
Tunnel.setDestDelay(dest, delay)

-- bind an interface (listen to net requests)
-- name: interface name
-- interface: table containing functions
Tunnel.bindInterface(name,interface)

-- get a tunnel interface to send requests
-- name: interface name
-- identifier: (optional) unique string to identify this tunnel interface access; if nil, will be the name of the resource
Tunnel.getInterface(name,identifier)

Interface

  • interface defined function names should not start with an underscore (_)

  • the tunnel server-side call requires the player source as first parameter

  • the tunnel server-side called function can use the global source (correct until a TriggerEvent/yield/etc) as the remote player source

  • using an underscore to call a remote function interface ignores (no wait) the returned values

-- PROXY any side, TUNNEL client-side

-- call and wait for returned values
-- ...: arguments
-- return values
interface.func(...)

-- call without waiting
-- ...: arguments
interface._func(...)

-- TUNNEL server-side

-- call and wait for returned values
-- ...: arguments
-- return values
interface.func(player, ...) -- or _func to ignore returned values

DBDriver

-- called when the driver is initialized (connection), should return true on success
-- db_cfg: cfg/base.lua .db config
DBDriver:onInit(db_cfg)

-- should prepare the query (@param notation)
DBDriver:onPrepare(name, query)

-- should execute the prepared query
-- params: map of parameters
-- mode:
--- "query": should return rows, affected
--- "execute": should return affected
--- "scalar": should return a scalar
DBDriver:onQuery(name, params, mode)

Extension

self.remote -- tunnel interface to other network side

-- level: (optional) level, 0 by default
Extension:log(msg, level)

Extension:error(msg)

User

User inherits from all extensions sub-class User (if registered before the first user instantiation).

self.source
self.name -- FiveM name (may be steam name)
self.id
self.cid -- character id
self.endpoint -- FiveM endpoint
self.data -- user data
self.cdata -- character data
self.loading_character -- flag
self.use_character_action -- action delay
self.spawns -- spawn count

-- return true if the user character is ready (loaded, not loading)
User:isReady()

User:save()

-- return characters id list
User:getCharacters()

-- return created character id or nil if failed
User:createCharacter()

-- use character
-- return true or false, err_code
-- err_code:
--- 1: delay error, too soon
--- 2: already loading
--- 3: invalid character
User:useCharacter(id)

-- delete character
-- return true or false on failure
User:deleteCharacter(id)

vRP

Shared

self.EXT -- map of name => ext
self.modules -- cfg/modules

vRP.Extension

-- register an extension
-- extension: Extension class
vRP:registerExtension(extension)

-- trigger event (with async call for each listener)
vRP:triggerEvent(name, ...)

-- trigger event and wait for all listeners to complete
vRP:triggerEventSync(name, ...)

-- msg: log message
-- suffix: (optional) category, string
-- level: (optional) level, 0 by default
vRP:log(msg, suffix, level)

-- msg: error message
-- suffix: optional category, string
vRP:error(msg, suffix)
Events
extensionLoad(extension)

called when an extension is loaded, passing the extension instance (can be used to initialize with another extension when loaded before the latter)

Server

self.cfg -- cfg/base config
self.lang -- loaded lang (https://github.com/ImagicTheCat/Luang)
self.users -- map of id => User
self.pending_users -- pending user source update (first spawn), map of ids key => user
self.users_by_source -- map of source => user
self.users_by_cid -- map of character id => user

-- db/SQL API
self.db_drivers
self.db_driver
self.db_initialized

vRP.DBDriver

-- return identification string for a specific source
vRP.getSourceIdKey(source)

vRP.getPlayerEndpoint(player)

vRP.getPlayerName(player)

-- register a DB driver
-- db_driver: DBDriver class
vRP:registerDBDriver(db_driver)

-- prepare a query
--- name: unique name for the query
--- query: SQL string with @params notation
vRP:prepare(name, query)

-- execute a query
--- name: unique name of the query
--- params: map of parameters
--- mode: default is "query"
---- "query": should return rows (list of map of parameter => value), affected
---- "execute": should return affected
---- "scalar": should return a scalar
vRP:query(name, params, mode)

-- shortcut for vRP.query with "execute"
vRP:execute(name, params)

-- shortcut for vRP.query with "scalar"
vRP:scalar(name, params)

-- user data
-- value: binary string
vRP:setUData(user_id,key,value)

vRP:getUData(user_id,key)

-- character data
-- value: binary string
vRP:setCData(character_id,key,value)

vRP:getCData(character_id,key)

-- server data
-- value: binary string
vRP:setSData(key,value,id)

vRP:getSData(key,id)

-- global data
-- value: binary string
vRP:setGData(key,value)

vRP:getGData(key)

-- reason: (optional)
vRP:kick(user, reason)

vRP:save()
Events
(sync) characterLoad(user)

called right after the character loading

(sync) characterUnload(user)

called before character unloading

playerJoin(user)

called when a player joins (valid user)

playerRejoin(user)

called when a player re-joins (ex: after a crash)

playerDelay(user, state)

called when the player tunnel delay changes, state is true if delay is enabled

playerSpawn(user, first_spawn)

called when the player spawns

playerDeath(user)

called when the player dies

(sync) playerLeave(user)

called before user removal

save

called when vRP performs a save (can be used to sync the save of external extension data)

Client

self.cfg -- cfg/client config
Events
playerSpawn()

called when the player spawns

playerDeath()

called when the player dies

Modules

Admin

The admin module adds an administration (kick,ban…​) menu and functions.

Extension

Client
Admin:toggleNoclip()
Admin:teleportToMarker()

-- TUNNEL

Admin.tunnel.toggleNoclip = Admin.toggleNoclip
Admin.tunnel.teleportToMarker = Admin.teleportToMarker

Menu

admin

Administration menu.

admin.users

Administration sub-menu: user list.

admin.users.user

Administration sub-menu: user.

data
id

user id (offline or online)

Aptitude

This module adds aptitudes and experience (skill/education).

Extension

User
self.cdata.aptitudes

-- return user aptitudes table
User:getAptitudes()

-- vary experience of an aptitude
User:varyExp(group, aptitude, amount)

-- level up an aptitude
User:levelUp(group, aptitude)

-- level down an aptitude
User:levelDown(group, aptitude)

User:getExp(group, aptitude)

-- set aptitude experience
User:setExp(group, aptitude, amount)
Server
self.cfg
self.exp_step
self.groups

-- define aptitude group
Aptitude:defineGroup(group, title)

-- define aptitude
-- max_exp: -1 => infinite
Aptitude:defineAptitude(group, aptitude, title, init_exp, max_exp)

-- get aptitude definition
Aptitude:getAptitude(group, aptitude)

-- get aptitude group title
-- return string
Aptitude:getGroupTitle(group)

-- convert experience to level
-- return float
Aptitude:expToLevel(exp)

-- convert level to experience
-- return integer
Aptitude:levelToExp(lvl)

Permission

aptitude

Aptitude level comparison.

!aptitude.<group>.<aptitude>.<op>

group, aptitude

aptitude

op

>x, <x, x (equal) level

Example
!aptitude.physical.strength.>3

strength level superior to 3

Menu

aptitudes

Aptitudes display menu.

Transformer processor

Produce experience in transformers.

aptitudes (products only)

map of group.aptitude ⇒ amount of experience

Example
...
aptitudes = {
  ["physical.strength"] = 1
}

ATM

This module spawns ATMs to interact with the bank.

Extension

Server
self.cfg

Menu

ATM

ATM menu.

Audio

This module handles audio features.

VoIP
  • a voip_server (nodejs) must be launched for each vRP server (by executing main.js)

  • nodejs ws and wrtc packages are required

  • the TCP websocket and UDP webrtc range of ports must be opened by firewalls

  • see voip_server/config.js and cfg/audio.lua

The clients will automatically connect/reconnect themselves to the VoIP server (can be started after, shutdown, restarted).

Extension

Server
self.cfg
self.channels -- map of id => {index, config} (available after the first player connection)

-- register VoIP channel
-- all channels should be registered before any player joins the server
--
-- id: channel name/id (string)
-- config:
--- effects: map of name => true/options
---- spatialization => { max_dist: ..., rolloff: ..., dist_model: ..., ref_dist: ...} (per peer effect)
---- biquad => { frequency: ..., Q: ..., type: ..., detune: ..., gain: ...} see WebAudioAPI BiquadFilter
----- freq = 1700, Q = 3, type = "bandpass" (idea for radio effect)
---- gain => { gain: ... }
Audio:registerVoiceChannel(id, config)
Client
self.voice_channels = {} -- map of channel => map of player => state (0-1)

-- play audio source (once)
--- url: valid audio HTML url (ex: .ogg/.wav/direct ogg-stream url)
--- volume: 0-1
--- x,y,z: position (omit for unspatialized)
--- max_dist  (omit for unspatialized)
--- player: (optional) player source id, if passed the spatialized source will be relative to the player (parented)
Audio:playAudioSource(url, volume, x, y, z, max_dist, player)

-- set named audio source (looping)
--- name: source name
--- url: valid audio HTML url (ex: .ogg/.wav/direct ogg-stream url)
--- volume: 0-1
--- x,y,z: position (omit for unspatialized)
--- max_dist  (omit for unspatialized)
--- player: (optional) player source id, if passed the spatialized source will be relative to the player (parented)
Audio:setAudioSource(name, url, volume, x, y, z, max_dist, player)

-- remove named audio source
Audio:removeAudioSource(name)

-- VoIP

-- create connection to another player for a specific channel
Audio:connectVoice(channel, player)

-- delete connection to another player for a specific channel
-- player: nil to disconnect from all players
Audio:disconnectVoice(channel, player)

-- enable/disable speaking for a specific channel
--- active: true/false
Audio:setVoiceState(channel, active)

Audio:isSpeaking()

-- TUNNEL

Audio.tunnel.playAudioSource = Audio.playAudioSource
Audio.tunnel.setAudioSource = Audio.setAudioSource
Audio.tunnel.removeAudioSource = Audio.removeAudioSource
Audio.tunnel.connectVoice = Audio.connectVoice
Audio.tunnel.disconnectVoice = Audio.disconnectVoice
Audio.tunnel.setVoiceState = Audio.setVoiceState
Events
speakingChange(speaking)

called when speaking state changes

voiceChannelPlayerSpeakingChange(channel, player, speaking)

called when the speaking state of a remote player for a specific channel changes (VoIP)

voiceChannelTransmittingChange(channel, transmitting)

called when the transmitting state of the local player for a specific channel changes (VoIP)

Base

vRP base module.

Extension

Server
Client
self.id -- user id
self.cid -- character id
self.players -- map of player id, keeps track of connected players (server id)
self.ragdoll -- flag

-- trigger vRP respawn
Base:triggerRespawn()

-- heading: (optional) entity heading
Base:teleport(x,y,z,heading)

-- teleport vehicle when inside one (placed on ground)
-- heading: (optional) entity heading
Base:vehicleTeleport(x,y,z,heading)

-- return x,y,z
Base:getPosition()

-- return false if in exterior, true if inside a building
Base:isInside()

-- return ped speed (based on velocity)
Base:getSpeed()

-- return dx,dy,dz
Base:getCamDirection()

-- return map of player id => distance
Base:getNearestPlayers(radius)

-- return player id or nil
Base:getNearestPlayer(radius)

-- GTA 5 text notification
Base:notify(msg)

-- GTA 5 picture notification
Base:notifyPicture(icon, type, sender, title, text)

-- SCREEN

-- play a screen effect
-- name, see https://wiki.fivem.net/wiki/Screen_Effects
-- duration: in seconds, if -1, will play until stopScreenEffect is called
Base:playScreenEffect(name, duration)

-- stop a screen effect
-- name, see https://wiki.fivem.net/wiki/Screen_Effects
Base:stopScreenEffect(name)

-- ANIM

-- animations dict and names: http://docs.ragepluginhook.net/html/62951c37-a440-478c-b389-c471230ddfc5.htm

-- play animation (new version)
-- upper: true, only upper body, false, full animation
-- seq: list of animations as {dict,anim_name,loops} (loops is the number of loops, default 1) or a task def (properties: task, play_exit)
-- looping: if true, will infinitely loop the first element of the sequence until stopAnim is called
Base:playAnim(upper, seq, looping)

-- stop animation (new version)
-- upper: true, stop the upper animation, false, stop full animations
Base:stopAnim(upper)

-- RAGDOLL

-- set player ragdoll flag (true or false)
Base:setRagdoll(flag)

-- SOUND
-- some lists:
-- pastebin.com/A8Ny8AHZ
-- https://wiki.gtanet.work/index.php?title=FrontEndSoundlist

-- play sound at a specific position
Base:playSpatializedSound(dict,name,x,y,z,range)

-- play sound
Base:playSound(dict,name)

-- TUNNEL

Base.tunnel.triggerRespawn = Base.triggerRespawn
Base.tunnel.teleport = Base.teleport
Base.tunnel.vehicleTeleport = Base.vehicleTeleport
Base.tunnel.getPosition = Base.getPosition
Base.tunnel.isInside = Base.isInside
Base.tunnel.getSpeed = Base.getSpeed
Base.tunnel.getNearestPlayers = Base.getNearestPlayers
Base.tunnel.getNearestPlayer = Base.getNearestPlayer
Base.tunnel.notify = Base.notify
Base.tunnel.notifyPicture = Base.notifyPicture
Base.tunnel.playScreenEffect = Base.playScreenEffect
Base.tunnel.stopScreenEffect = Base.stopScreenEffect
Base.tunnel.playAnim = Base.playAnim
Base.tunnel.stopAnim = Base.stopAnim
Base.tunnel.setRagdoll = Base.setRagdoll
Base.tunnel.playSpatializedSound = Base.playSpatializedSound
Base.tunnel.playSound = Base.playSound
Events
playerTeleport()

called when the player is teleported

Menu

characters

Characters menu.

Permission

inside

!inside

Check if inside a building (interior). Will do a tunnel call.

Business

This module adds a simple business system (dirty money, capital…​).

Extension

Server
self.cfg

-- return character business data or nil
Business:getBusiness(character_id)

-- close the business of a character
Business:closeBusiness(character_id)

Item

dirty_money

Can be converted to money using business money laundering.

Menu

commerce_chamber

Commerce chamber menu.

commerce_chamber.directory

Business directory.

data
page

page index, start at 0

Cloak

This module manages character cloaks (uniforms, etc).

Extension

User
self.cdata.pre_cloak

-- cloak: skin customization
User:setCloak(cloak)

User:removeCloak()

User:hasCloak()
Server
self.cfg

Menu

cloakroom

Cloakroom menu.

data
type

cloakroom type

cloaks

cloakroom type table

Edible

This module defines a generic edible item.

Extension

Server
self.cfg
self.types
self.effects
self.edibles

-- id: identifier (string)
-- action_name: (string)
-- on_consume(user, edible)
Edible:defineType(id, action_name, on_consume)

-- id: identifier (string)
-- on_effect(user, value)
Edible:defineEffect(id, on_effect)

-- id: identifier (string)
-- type: edible type
-- effects: map of effect => value
-- name: (string)
-- description: (html)
-- weight
Edible:defineEdible(id, type, effects, name, description, weight)

Item

edible

edible|<id>

id

edible id

Example
edible|tacos

a tacos

Emotes

This module adds an emotes menu.

Extension

User
self.emotes_action -- emotes action delay
Server
self.cfg

-- add a new emote
-- see cfg/emotes.lua
Emotes:add(config)

Menu

emotes

Emotes menu.

Garage

This module manages garages and character vehicles.

Each owned vehicle has a saved state which can be extended with new properties.

Vehicles in FiveM/GTA V are entities which can unload/load/despawn themselves on multiple clients, this module try to handle it this way.
  • it tracks the owned vehicles with a decorator and will try to re-own the vehicles when near them

  • if multiple versions of the same owned vehicle exist at the same place, it will try to delete the others

  • vehicles states are saved and it will try to re-spawn the vehicles if near their last saved position

  • if a vehicle is stuck or completely lost somewhere, players can force its re-spawn at the garage by paying a fee

Extension

User
self.cdata.vehicles
self.cdata.rent_vehicles
self.vehicle_states

-- get owned vehicles
-- return map of model
User:getVehicles()

-- get vehicle model state table (may be async)
User:getVehicleState(model)
Server
self.cfg
self.models -- map of all garage defined models

-- get vehicle trunk chest id by character id and model
Garage.getVehicleChestId(cid, model)
Client
self.vehicles -- map of vehicle model => veh id (owned vehicles)
self.hash_models -- map of hash => model

-- veh: vehicle game id
-- return owner character id and model or nil if not managed by vRP
Garage:getVehicleInfo(veh)

-- spawn vehicle (will despawn first)
-- will be placed on ground properly
-- one vehicle per model allowed at the same time
--
-- state: (optional) vehicle state (client)
-- position: (optional) {x,y,z}, if not passed the vehicle will be spawned on the player (and will be put inside the vehicle)
-- rotation: (optional) quaternion {x,y,z,w}, if passed with the position, will be applied to the vehicle entity
Garage:spawnVehicle(model, state, position, rotation)

-- return true if despawned
Garage:despawnVehicle(model)

Garage:despawnVehicles()

-- get all game vehicles
-- return list of veh
Garage:getAllVehicles()

-- return map of veh => distance
Garage:getNearestVehicles(radius)

-- return veh
Garage:getNearestVehicle(radius)

Garage:trySpawnOutVehicles()

-- try re-own vehicles
Garage:tryOwnVehicles()

-- cleanup invalid owned vehicles
Garage:cleanupVehicles()

Garage:fixNearestVehicle(radius)

Garage:replaceNearestVehicle(radius)

-- return model or nil
Garage:getNearestOwnedVehicle(radius)

-- return ok,x,y,z
Garage:getAnyOwnedVehiclePosition()

-- return x,y,z or nil
Garage:getOwnedVehiclePosition(model)

Garage:putInOwnedVehicle(model)

-- eject the ped from the vehicle
Garage:ejectVehicle()

Garage:isInVehicle()

-- return model or nil if not in owned vehicle
Garage:getInOwnedVehicleModel()

-- VEHICLE STATE

Garage:getVehicleCustomization(veh)

-- partial update per property
Garage:setVehicleCustomization(veh, custom)

Garage:getVehicleState(veh)

-- partial update per property
Garage:setVehicleState(veh, state)

-- VEHICLE COMMANDS

Garage:vc_openDoor(model, door_index)

Garage:vc_closeDoor(model, door_index)

Garage:vc_detachTrailer(model)

Garage:vc_detachTowTruck(model)

Garage:vc_detachCargobob(model)

Garage:vc_toggleEngine(model)

-- return true if locked, false if unlocked
Garage:vc_toggleLock(model)

-- TUNNEL

Garage.tunnel.spawnVehicle = Garage.spawnVehicle
Garage.tunnel.despawnVehicle = Garage.despawnVehicle
Garage.tunnel.despawnVehicles = Garage.despawnVehicles
Garage.tunnel.fixNearestVehicle = Garage.fixNearestVehicle
Garage.tunnel.replaceNearestVehicle = Garage.replaceNearestVehicle
Garage.tunnel.getNearestOwnedVehicle = Garage.getNearestOwnedVehicle
Garage.tunnel.getAnyOwnedVehiclePosition = Garage.getAnyOwnedVehiclePosition
Garage.tunnel.getOwnedVehiclePosition = Garage.getOwnedVehiclePosition
Garage.tunnel.putInOwnedVehicle = Garage.putInOwnedVehicle
Garage.tunnel.getInOwnedVehicleModel = Garage.getInOwnedVehicleModel
Garage.tunnel.trySpawnOutVehicles = Garage.trySpawnOutVehicles
Garage.tunnel.cleanupVehicles = Garage.cleanupVehicles
Garage.tunnel.tryOwnVehicles = Garage.tryOwnVehicles
Garage.tunnel.ejectVehicle = Garage.ejectVehicle
Garage.tunnel.isInVehicle = Garage.isInVehicle
Garage.tunnel.vc_openDoor = Garage.vc_openDoor
Garage.tunnel.vc_closeDoor = Garage.vc_closeDoor
Garage.tunnel.vc_detachTrailer = Garage.vc_detachTrailer
Garage.tunnel.vc_detachTowTruck = Garage.vc_detachTowTruck
Garage.tunnel.vc_detachCargobob = Garage.vc_detachCargobob
Garage.tunnel.vc_toggleEngine = Garage.vc_toggleEngine
Garage.tunnel.vc_toggleLock = Garage.vc_toggleLock
Events
garageVehicleSpawn(model)

called when a vehicle is spawned

garageVehicleDespawn(model)

called when a vehicle is despawned

Item

repairkit

Used to repair vehicles, but can be used for other stuff.

Menu

vehicle

Owned vehicle menu.

data
model

vehicle model

garage

Garage menu.

data
type

garage type

vehicles

garage type table

garage.owned

Garage sub-menu. Same data as garage.

garage.buy

Garage sub-menu. Same data as garage.

garage.sell

Garage sub-menu. Same data as garage.

garage.rent

Garage sub-menu. Same data as garage.

Permission

in_vehicle

!in_vehicle

Will do a tunnel call.

in_owned_vehicle

!in_owned_vehicle[.<model>]

Will do a tunnel call.

model

(optional) vehicle model

Examples
!in_owned_vehicle

check if inside an owned vehicle

!in_owned_vehicle.taxi

check if inside owned taxi model

Vehicle state

customization

customization properties

condition

damages properties

position, rotation

position/rotation for persistence

locked

doors state

Group

This module adds groups and permissions.

Extension

User
self.cdata.groups

-- return map of groups
User:getGroups()

User:hasGroup(name)

User:addGroup(name)

User:removeGroup(name)

-- get user group by type
-- return group name or nil
User:getGroupByType(gtype)

-- check if the user has a specific permission
User:hasPermission(perm)

-- check if the user has a specific list of permissions (all of them)
User:hasPermissions(perms)
Server
self.cfg

-- return users list
Group:getUsersByGroup(name)

-- return users list
Group:getUsersByPermission(perm)

-- return title or nil
Group:getGroupTitle(group_name)

-- register a special permission function
-- name: name of the permission -> "!name.[...]"
-- callback(user, params)
--- params: params (strings) of the permissions, ex "!name.param1.param2" -> ["name", "param1", "param2"]
--- should return true or false/nil
Group:registerPermissionFunction(name, callback)
Events
playerJoinGroup(user, name, gtype)

called when a player joins a group

playerLeaveGroup(user, name, gtype)

called when a player leaves a group

Menu

group_selector

Group selector menu.

data
name

selector name

groups

selector table

Permission

not

Negate another permission function.

!not.<…​>

Example
!not.group.admin

check if not an admin

group

Group check.

!group.<name>

name

group name

Example
!group.admin

check if an admin

GUI

This module adds GUI features.

If GUI customization is needed, it’s possible to use GUI:setDiv to add additional CSS (the override order should depends on GUI:setDiv call order).

It’s even possible to load a custom client-side design.css file like this:
-- client-side
function MyExt.event:NUIready()
  vRP.EXT.GUI:addDiv("my_ext_css", LoadResourceFile("my_ext", "design.css"), "")
end

Extension

Menu
self.user
self.name
self.data
self.title
self.options
self.css
self.closed

-- dispatcher events:
--- close(menu)
--- remove(menu)
--- select(menu, id)
Menu:listen(name, callback)

-- add option
-- title: option title (html)
-- action(menu, value, mod, index): (optional) select callback
--- value: option value
--- mod: action modulation
---- -1: left
---- 0: valid
---- 1: right
-- description: (optional) option description (html)
--- callback(menu, value): should return a string or nil
-- value: (optional) option value, can be anything, option index by default
-- index: (optional) by default the option is added at the end, but an index can be used to insert the option
Menu:addOption(title, action, description, value, index)

-- update menu option
-- title: (optional) as Menu:addOption
-- description: (optional) as Menu:addOption
-- will trigger client update if current menu
Menu:updateOption(id, title, description)
User
self.menu_stack -- stack of menus
self.request_ids
self.requests

-- return current menu or nil
User:getMenu()

-- open menu (build and open menu)
-- data: (optional) menu build data
-- return menu
User:openMenu(name, data)

-- close menu
-- menu: (optional) menu to close, if nil, will close the current menu
User:closeMenu(menu)

-- close and rebuild current menu (no remove)
-- menu is rebuilt, listeners are kept
User:actualizeMenu()

-- close all menus
User:closeMenus()

-- prompt textual (and multiline) information from player
-- return entered text
User:prompt(title, default_text)

-- REQUEST

-- ask something to a player with a limited amount of time to answer (yes|no request)
-- time: request duration in seconds
-- return true (yes) or false (no)
User:request(text, time)
Server
self.cfg
self.menu_builders -- map of name => callbacks list

-- register a menu builder function
-- name: menu type name
-- builder(menu): callback to modify the menu
GUI:registerMenuBuilder(name, builder)

-- TUNNEL

-- open the general player menu
GUI.tunnel:openMainMenu()

-- can be used to implement a custom menu

GUI.tunnel:closeMenu()
GUI.tunnel:triggerMenuOption(id, mod)
GUI.tunnel:triggerMenuSelect(id)
Client
self.paused
self.menu_data

-- CONTROLS/GUI

GUI:isPaused()

-- get native GUI coordinates based on UI description
-- x_align, y_align: integers, see https://runtime.fivem.net/doc/natives/#_0xB8A850F20A067EB6
-- x, y: floats, UI defined coordinates
GUI:getNativeCoords(x_align, y_align, x, y)

-- get minimap rect in pixels
-- return x, y, w, h
GUI:getMinimapRect()

-- hide/show GUI
GUI:setVisible(flag)

-- MENU

GUI:isMenuOpen()

-- ANNOUNCE

-- add an announce to the queue
-- background: image url (800x150)
-- content: announce html content
GUI:announce(background,content)

-- PROGRESS BAR

-- create/update a progress bar
-- anchor:
--- "minimap"
--- "center"
--- "botright"
-- r,g,b: RGB 256 color
-- value: 0-1
GUI:setProgressBar(name,anchor,text,r,g,b,value)

-- set progress bar value 0-1
GUI:setProgressBarValue(name,value)

-- set progress bar text
GUI:setProgressBarText(name,text)

-- remove a progress bar
GUI:removeProgressBar(name)

-- DIV

-- set a div
-- css: plain global css, the div class is "div_name"
-- content: html content of the div
GUI:setDiv(name,css,content)

-- set the div css
GUI:setDivCss(name,css)

-- set the div content
GUI:setDivContent(name,content)

-- execute js for the div
-- js: code, "this" is the div
GUI:divExecuteJS(name,js)

-- remove the div
GUI:removeDiv(name)

-- TUNNEL

GUI.tunnel.announce = GUI.announce
GUI.tunnel.setProgressBar = GUI.setProgressBar
GUI.tunnel.setProgressBarValue = GUI.setProgressBarValue
GUI.tunnel.setProgressBarText = GUI.setProgressBarText
GUI.tunnel.removeProgressBar = GUI.removeProgressBar
GUI.tunnel.setDiv = GUI.setDiv
GUI.tunnel.setDivCss = GUI.setDivCss
GUI.tunnel.setDivContent = GUI.setDivContent
GUI.tunnel.divExecuteJS = GUI.divExecuteJS
GUI.tunnel.removeDiv = GUI.removeDiv
Events
NUIready()

called when the vRP NUI is ready

pauseChange(paused)

called when the game pause state changes

menuOpen(menudata)

called when the menu is opened (can be used to implement a custom menu)

menuClose()

called when the menu is closed (can be used to implement a custom menu)

menuSetSelectEvent(select_event)

called when the select event flag is set (if true, the menu must trigger the server-side option select event) (can be used to implement a custom menu)

menuOptionUpdate(index, title, description)

called when a menu option is updated (can be used to implement a custom menu)

Menu

main

Main menu.

HiddenTransformer

This module adds hidden/randomly placed transformers and an informer to find them.

Extension

Server
self.cfg

Menu

hidden_transformer_informer

Informer menu.

Home

This module adds a home system.

Extension

Component
self.slot
self.id
self.index
self.cfg
self.x
self.y
self.z

-- called when the component is loaded for a specific slot
Component:load()

-- called when the component is unloaded from a specific slot
Component:unload()

-- called when a player enters the slot
Component:enter(user)

-- called when a player leaves the slot
Component:leave(user)
Slot
self.type
self.id
self.owner_id -- character id
self.home
self.number
self.users -- map of users
self.components -- map of index => component

Slot:isEmpty()
User
self.home_slot
self.address

-- access a home by address
-- return true on success
User:accessHome(home, number)

User:leaveHome()

-- check if inside a home
User:inHome()
Server
self.cfg
self.components
self.slots -- map of type => map of slot id => slot instance

-- address access (online and offline characters)
-- return address or nil
Home:getAddress(cid)

-- return character id or nil
Home:getByAddress(home,number)

-- find a free address number to buy
-- return number or nil if no numbers availables
Home:findFreeNumber(home,max)

-- register home component
-- id: unique component identifier (string)
-- component: Home.Component derived class
Home:registerComponent(component)

-- SLOTS

-- get slot instance
-- return slot or nil
Home:getSlot(stype, sid)

-- get slot instance by address
-- return slot or nil
Home:getSlotByAddress(home, number)

-- return sid or nil
Home:findFreeSlot(stype)
Events
characterAddressUpdate(user)

called when the address is updated (load, buy, sell…​)

Menu

home

Home menu (outside).

data
name

home name

home:component:entry

Home entry menu (inside).

data
slot

component slot

Home component

entry

Home entry component.

Permission

home

Home/address check.

!home[.<name>[.<number>]]

name

(optional) home name, all . characters should be removed

number

(optional) home number

Example
!home

has a home

!home.HLM Vinewood

lives at HLM Vinewood

!home.HLM Vinewood.5

lives at 5, HLM Vinewood

home_components

This module adds some home components.

Extension

Server
self.cfg

Menu

home:component:wardrobe

Wardrobe menu.

data
sets

map of name ⇒ customization

home:component:gametable

Gametable menu.

home:component:radio

Radio menu.

data
component

radio component

Home component

chest

A home chest to store items. The chest is bound to the character, not to the home or the component.

config
weight

(optional) chest max weight, 200 by default

wardrobe

Wardrobe to save/restore clothes.

gametable

Gametable to play simple games with other people (bet…​).

transformer

Transformer component.

config
cfg

transformer config

map_entity

(optional) map entity at the component position

radio

Component to play music in the home.

config
stations

map of name ⇒ audio source URL

Identity

This module adds an identity system.

Extension

User
self.identity
Server
self.cfg

-- (ex: DDDLLL, D => digit, L => letter)
Identity.generateStringNumber(format)

-- identity access (online and offline characters)
-- return identity or nil
Identity:getIdentity(cid)

-- return character_id or nil
Identity:getByRegistration(registration)

-- return character_id or nil
Identity:getByPhone(phone)

-- return a unique registration number
Identity:generateRegistrationNumber()

-- return a unique phone number
Identity:generatePhoneNumber()
Events
characterIdentityUpdate(user)

called when the identity is updated (load, change…​)

Client
self.registration

Menu

identity

Identity menu, offline and online characters.

data
cid

character id of the identity

cityhall

Inventory

This module adds an inventory/item system.

Extension

User
self.cdata.inventory

-- return map of fullid => amount
User:getInventory()

-- try to give an item
-- dry: if passed/true, will not affect
-- return true on success or false
User:tryGiveItem(fullid,amount,dry,no_notify)

-- try to take an item from inventory
-- dry: if passed/true, will not affect
-- return true on success or false
User:tryTakeItem(fullid,amount,dry,no_notify)

-- get item amount in the inventory
User:getItemAmount(fullid)

-- return inventory total weight
User:getInventoryWeight()

-- return maximum weight of inventory
User:getInventoryMaxWeight()

User:clearInventory()

-- open a chest by identifier (GData)
-- cb_close(id): called when the chest is closed (optional)
-- cb_in(chest_id, fullid, amount): called when an item is added (optional)
-- cb_out(chest_id, fullid, amount): called when an item is taken (optional)
-- return chest menu or nil
User:openChest(id, max_weight, cb_close, cb_in, cb_out)
Server
self.cfg
self.items -- item definitions
self.computed_items -- computed item definitions
self.chests -- loaded chests

-- define an inventory item (parametric or plain text data)
-- id: unique item identifier (string, no "." or "|")
-- name: display name, value or genfunction(args)
-- description: value or genfunction(args) (html)
-- menu_builder: (optional) genfunction(args, menu)
-- weight: (optional) value or genfunction(args)
--
-- genfunction are functions returning a correct value as: function(args, ...)
-- where args is a list of {base_idname,args...}
Inventory:defineItem(id,name,description,menu_builder,weight)

-- compute item definition (cached)
-- return computed item or nil
-- computed item {}
--- name
--- description
--- weight
--- menu_builder: can be nil
--- args: parametric args
Inventory:computeItem(fullid)

-- compute weight of a list of items (in inventory/chest format)
Inventory:computeItemsWeight(items)

-- load global chest
-- id: identifier (string)
-- return chest (as inventory, map of fullid => amount)
Inventory:loadChest(id)

-- unload global chest
-- id: identifier (string)
Inventory:unloadChest(id)

Menu

inventory

Player inventory menu.

inventory.item

Player inventory item sub-menu.

data
fullid

item fullid

chest

Chest menu.

data
id

chest id

chest

loaded chest

max_weight

chest max weight

cb_close

cb_close

cb_in

cb_in

cb_out

cb_out

chest.put

Chest put sub-menu. Same data as chest.

chest.take

Chest take sub-menu. Same data as chest.

Permission

item

Item amount comparison.

!item.<fullid>.<op>

fullid

item fullid

op

>x, <x, x (equal) amount

Examples
!item.edible|tacos.>0

one or more tacos

!item.dirty_money.0

no dirty money

Transformer processor

Consume and produce items in transformers.

items

map of item fullid ⇒ amount

Example
...
items = {
  ["edible|peach"] = 1
}

Login

This is the base login module with whitelist and ban features.

Extension

Server
self.cfg

-- return (end_timestamp, reason) if banned or nil
Login:checkBanned(user_id)

-- end_timestamp: POSIX timestamp (0: not banned, 2^32-1: banned "forever")
-- reason: (optional)
Login:setBanned(user_id, end_timestamp, reason)

Login:isWhitelisted(user_id)

Login:setWhitelisted(user_id, whitelisted)

-- duration: in seconds (-1: ban "forever")
-- reason: (optional)
Login:ban(user, duration, reason)

Map

This module adds map features.

Extension

Map.Entity

Client-side map entity.

self.id
self.cfg

-- called when the entity is loaded
Entity:load()

-- called when the entity is unloaded
Entity:unload()

-- called to check if the entity is active
-- px, py, pz: player position
-- should return true if active
Entity:active(px, py, pz)

-- called at each render frame if the entity is active
-- time: seconds since last frame
Entity:frame(time)

Entity.command table can be created to define entity commands, the same way as for the Extension.proxy table.

User
self.map_areas

-- create/update a player area
-- cb_enter(user, name): (optional) called when entering the area
-- cb_leave(user, name): (optional) called when leaving the area
User:setArea(name,x,y,z,radius,height,cb_enter,cb_leave)

User:removeArea(name)

User:inArea(name)
Server
self.cfg
Client
self.entities -- map of id (number or name) => entity
self.def_entities -- defined entities
self.frame_entities -- active entities for the next frames
self.areas

-- ent: Map.Entity class
Map:registerEntity(ent)

-- add entity
-- ent: registered entity name
-- cfg: entity config
-- return id (number) or nil on failure
Map:addEntity(ent, cfg)

-- id: number or string
Map:removeEntity(id)

-- id: number (update added entity) or string (create/update named entity)
-- ent: registered entity name
-- cfg: entity config
Map:setEntity(id, ent, cfg)

-- entity command
-- id: number or string
-- command: command name
-- ...: arguments
Map:commandEntity(id, command, ...)

-- GPS

-- set the GPS destination marker coordinates
Map:setGPS(x,y)

-- DOOR

-- set the closest door state
-- doordef: .model or .modelhash
-- locked: boolean
-- doorswing: -1 to 1
Map:setStateOfClosestDoor(doordef, locked, doorswing)

-- TUNNEL

Map.tunnel.addEntity = Map.addEntity
Map.tunnel.setEntity = Map.setEntity
Map.tunnel.removeEntity = Map.removeEntity
Map.tunnel.commandEntity = Map.commandEntity
Map.tunnel.setGPS = Map.setGPS
Map.tunnel.setStateOfClosestDoor = Map.setStateOfClosestDoor

Map entity

Map.PosEntity

Specialized entity with a position and default active behavior by distance.

config
pos

{x,y,z} (must be floats)

active_distance

(optional) maximum distance for the entity to be active, default is 250

PoI

Point of Interest generic map entity (blip/marker). Inherits from Map.PosEntity.

config
blip_id, blip_color

(optional) blip id/color (ex: https://wiki.gtanet.work/index.php?title=Blips)

blip_scale

(optional) float

blip_flashes

(optional) mode number 1 or 2 (flashes or flashes alternate)

title

(optional) title (used for the blip)

marker_id

(optional) marker id (ex: https://wiki.gtanet.work/index.php?title=Marker)

scale

(optional) {sx,sy,sz} marker scale

color

(optional) {r,g,b,a} marker color

height

(optional) marker height

rotate_speed

(optional) number of z-axis rotations per second

commands
setBlipRoute()

set player blip route to PoI blip

PlayerMark

Mark on player ped.

config
player

player server id

blip_id, blip_color

(optional) blip id/color (ex: https://wiki.gtanet.work/index.php?title=Blips)

blip_scale

(optional) float

blip_flashes

(optional) mode number 1 or 2 (flashes or flashes alternate)

title

(optional) title (used for the blip)

Mission

This module adds a simple mission system.

Extension

User
self.mission
self.mission_step

-- start a mission for a player
--- mission:
---- name: mission name
---- steps: ordered list of
----- text: (html)
----- position: {x,y,z}
----- radius: (optional) area radius (affect default PoI)
----- height: (optional) area height (affect default PoI)
----- onenter: see Map.User:setArea
----- onleave: (optional) see Map.User:setArea
----- map_entity: (optional) a simple PoI by default
User:startMission(mission)

-- end the current player mission step
User:nextMissionStep()

-- stop the player mission
User:stopMission()

-- check if the player has a mission
User:hasMission()
Server
self.cfg
Events
playerMissionStart(user)

called when the player start a mission (the mission data is attached to the player at the beginning of the call)

playerMissionStep(user)

called when the player start a new mission step

playerMissionStop(user)

called when the player stop the mission (the mission data is still attached to the player at the beginning of the call)

Money

This module adds money (wallet and bank).

Extension

User
self.cdata.wallet
self.cdata.bank


-- get wallet amount
User:getWallet()

-- get bank amount
User:getBank()

-- set wallet amount
User:setWallet(amount)

-- set bank amount
User:setBank(amount)

-- give money to bank
User:giveBank(amount)

-- give money to wallet
User:giveWallet(amount)

-- try a payment (with wallet)
-- dry: if passed/true, will not affect
-- return true if debited or false
User:tryPayment(amount, dry)

-- try a withdraw (from bank)
-- dry: if passed/true, will not affect
-- return true if withdrawn or false
User:tryWithdraw(amount, dry)

-- try a deposit
-- dry: if passed/true, will not affect
-- return true if deposited or false
User:tryDeposit(amount, dry)

-- try full payment (wallet + bank to complete payment)
-- dry: if passed/true, will not affect
-- return true if debited or false
User:tryFullPayment(amount, dry)
Server
self.cfg
Events
playerMoneyUpdate(user)

called when the player money is updated (load, change…​)

Item

money

Money packed as item.

money_binder

Used to pack an amount of wallet money into money item.

Transformer processor

Consume and produce money in transformers.

money

amount

Example
...
money = 200

PedBlacklist

This module adds a ped blacklist (remove peds from the game).

Extension

Server
self.cfg
Client
self.ped_models -- map of model hash
self.interval

Phone

This module adds a phone.

Extension

User
self.phone_sms
self.cdata.phone_directory
self.phone_call -- source of correspondent if in phone call

-- send an sms to a phone number
-- return true on success
User:sendSMS(phone, msg)

-- add sms to phone
User:addSMS(phone, msg)

-- get directory name by number for a specific user
User:getPhoneDirectoryName(phone)

User:phoneHangUp()

-- call a phone number
-- return true if the communication is established
User:phoneCall(phone)

-- send smspos to a phone number
-- return true on success
User:sendSMSPos(phone, x,y,z)
Server
self.cfg

-- Send a service alert to all service listeners
--- sender: user or nil (optional, if not nil, it is a call request alert)
--- service_name: service name
--- x,y,z: coordinates
--- msg: alert message
Phone:sendServiceAlert(sender,service_name,x,y,z,msg)
Client

Menu

phone

Phone menu.

phone.directory

Phone directory sub-menu.

phone.directory.entry

Phone directory entry sub-menu.

data
phone

phone number

phone.sms

Phone sms sub-menu.

phone.service

Phone service sub-menu.

phone.announce

Phone announce sub-menu.

PlayerState

This module handles the player/character state (customization, weapons, health, armour…​).

Extension

User
self.cdata.state
Server
self.cfg
Events
playerStateLoaded(user)

called when the player state loading is finished

playerStateUpdate(user, state)

called when the player state is updated (state read-only, partial update, per property)

Client
self.state_ready

-- WEAPONS

-- get player weapons
-- return map of name => {.ammo}
PlayerState:getWeapons()

-- replace weapons (combination of getWeapons and giveWeapons)
-- weapons: map of name => {.ammo}
--- ammo: (optional)
-- return previous weapons
PlayerState:replaceWeapons(weapons)

-- weapons: map of name => {.ammo}
--- ammo: (optional)
PlayerState:giveWeapons(weapons, clear_before)

-- set player armour (0-100)
PlayerState:setArmour(amount)

PlayerState:getArmour()

-- amount: 100-200 ?
PlayerState:setHealth(amount)

PlayerState:getHealth()

-- PLAYER CUSTOMIZATION

-- get number of drawables for a specific part
PlayerState:getDrawables(part)

-- get number of textures for a specific part and drawable
PlayerState:getDrawableTextures(part,drawable)

-- get player skin customization
-- return custom parts
PlayerState:getCustomization()

-- set partial customization (only what is set is changed)
-- custom: indexed customization parts ("foo:arg1:arg2...")
--- "modelhash": number, model hash
--- or "model": string, model name
--- "drawable:<index>": {drawable,texture,palette} ped components
--- "prop:<index>": {prop_index, prop_texture}
--- "hair_color": {primary, secondary}
--- "overlay:<index>": {overlay_index, primary color, secondary color, opacity}
PlayerState:setCustomization(custom)

-- TUNNEL

PlayerState.tunnel.getWeapons = PlayerState.getWeapons
PlayerState.tunnel.replaceWeapons = PlayerState.replaceWeapons
PlayerState.tunnel.giveWeapons = PlayerState.giveWeapons
PlayerState.tunnel.setArmour = PlayerState.setArmour
PlayerState.tunnel.getArmour = PlayerState.getArmour
PlayerState.tunnel.setHealth = PlayerState.setHealth
PlayerState.tunnel.getHealth = PlayerState.getHealth
PlayerState.tunnel.getDrawables = PlayerState.getDrawables
PlayerState.tunnel.getDrawableTextures = PlayerState.getDrawableTextures
PlayerState.tunnel.getCustomization = PlayerState.getCustomization
PlayerState.tunnel.setCustomization = PlayerState.setCustomization
Events
(sync) playerModelSave

called before the player model is changed (can be used to save some native player data)

(sync) playerModelRestore

called after the player model is changed (can be used to restore some native player data)

Item

wbody

Weapon body.

wbody|<weapon>

weapon

native weapon name

Example
wbody|WEAPON_PISTOL

pistol body

wammo

Weapon ammunition.

wammo|<weapon>[|<amount>]

weapon

native weapon name

amount

(optional) ammo amount (ammo box)

Example
wammo|WEAPON_PISTOL

pistol ammo

wammo|WEAPON_PISTOL|50

pistol ammo box x50

Police

This module adds some police features.

Extension

User
self.police_records -- list of strings

-- insert a police record (will do a save)
--- record: text for one line (html)
User:insertPoliceRecord(record)

User:savePoliceRecords()
Server
self.cfg
self.wantedlvl_users -- map of user => wanted level

Police:getUserWantedLevel(user)
Client
self.handcuffed -- flag
self.cop -- flag
self.wanted_level
self.current_jail
self.follow_player

-- set player as cop (true or false)
Police:setCop(flag)

-- HANDCUFF

Police:toggleHandcuff()

Police:setHandcuffed(flag)

Police:isHandcuffed()

Police:putInNearestVehicleAsPassenger(radius)

-- FOLLOW

-- follow another player
-- player: nil to disable
Police:followPlayer(player)

-- return player or nil if not following anyone
Police:getFollowedPlayer()

-- JAIL

-- jail the player in a no-top no-bottom cylinder
Police:jail(x,y,z,radius)

-- unjail the player
Police:unjail()

Police:isJailed()

-- WANTED

Police:applyWantedLevel(new_wanted)

-- TUNNEL

Police.tunnel.setCop = Police.setCop
Police.tunnel.toggleHandcuff = Police.toggleHandcuff
Police.tunnel.setHandcuffed = Police.setHandcuffed
Police.tunnel.isHandcuffed = Police.isHandcuffed
Police.tunnel.putInNearestVehicleAsPassenger = Police.putInNearestVehicleAsPassenger
Police.tunnel.followPlayer = Police.followPlayer
Police.tunnel.getFollowedPlayer = Police.getFollowedPlayer
Police.tunnel.jail = Police.jail
Police.tunnel.unjail = Police.unjail
Police.tunnel.isJailed = Police.isJailed
Police.tunnel.applyWantedLevel = Police.applyWantedLevel

Menu

police

Police menu.

police.fine

Police fine sub-menu.

data
tuser

target user

money

target maximum available money

police.check

Police check report menu.

data
tuser

target user

police_pc

Police PC menu.

police_pc.records

Police PC records sub-menu. Manage police records of a character.

data
tuser

target user

Item

bulletproof_vest

Bulletproof vest, set maximum armour when used.

Radio

This module adds groups radio (voice and GPS signal).

Extension

User
User:connectRadio()

User:disconnectRadio()
Server
self.cfg
Client
self.talking

self.players -- radio players, map of player server id => {.group, .group_title, .title}
Events
radioSpeakingChange(speaking)

called when radio speaking state changes

Shop

This module adds item shops.

Extension

Server
self.cfg

Menu

shop

Shop menu.

data
type

shop type

items

shop type table

SkinShop

This module adds skin shops.

Extension

Server
self.cfg

Menu

skinshop

Skinshop menu.

data
parts

skinshop config parts

skinshop.part

Shinshop part sub-menu.

data
title

part title

part

part name

custom

current skinshop customization state

Survival

This module adds survival features.

Base vitals
  • food

  • water

Extension

User
self.cdata.vitals -- map of name => value

-- return vital value (0-1) or nil
User:getVital(name)

-- set vital
-- value: 0-1
User:setVital(name, value)

User:varyVital(name, value)
Server
self.cfg
self.vitals -- registered vitals, map of name => {default_value}
Events
playerVitalChange(user, name)

called when a player vital value changes

playerVitalOverflow(user, name, overflow)

called when a player vital overflows (overflow can be negative or positive)

Client
self.in_coma -- flag
self.coma_left -- seconds

Survival:varyHealth(variation)

Survival:setFriendlyFire(flag)

Survival:setPolice(flag)

-- COMA SYSTEM

Survival:isInComa()

-- kill the player if in coma
Survival:killComa()

-- TUNNEL

Survival.tunnel.isInComa = Survival.isInComa
Survival.tunnel.killComa = Survival.killComa
Survival.tunnel.setFriendlyFire = Survival.setFriendlyFire
Survival.tunnel.setPolice = Survival.setPolice
Survival.tunnel.varyHealth = Survival.varyHealth

Item

medkit

Item used to reanimate people in coma.

Transformer

This module adds transformers.

  • it’s a generic system to transform (generate, process, convert) items/money/etc to items/money/etc in a specific area

  • each transformer can take things to generate other things, using a unit of work

  • units are regenerated periodically at a specific rate

  • reagents ⇒ products (ex: reagents can be nothing to create an harvest transformer)

Extension

Transformer
self.id
self.cfg
self.units
self.users -- map of user => recipe name

Transformer:unbindUser(user)

Transformer:bindUser(user, recipe_name)

Transformer:unbindAll()
Server
self.cfg
self.transformers -- map of id => transformer
self.processors -- registered processors, map of id => {on_display, on_check, on_process}

-- register a transformer processor
-- on_display(user, reagents, products): should return r_info, p_info (two html strings to display info about the reagents and products)
-- on_check(user, reagents, products): should return true if the processing can occur
-- on_process(user, reagents, products): should process the transformation
-- for the three callbacks:
--- reagents: reagents data, can be nil
--- products: products data, can be nil
Transformer:registerProcessor(id, on_display, on_check, on_process)

-- add a transformer
-- id: transformer identitifer (string)
-- cfg: transformer config
--- title
--- color {r,g,b} (255)
--- max_units
--- units_per_minute
--- pos {x,y,z}
--- radius,height (area properties)
--- permissions: (optional)
--- recipes: map of recipe name => recipe {}
---- description (html)
---- reagents: map of processor id => data, see modules transformer processors
---- products: map of processor id => data, see modules transformer processors
---- permissions: (optional) recipe permissions
---- onstart(transformer, user, recipe_name): (optional) called when the recipe starts
---- onstep(transformer, user, recipe_name): (optional) called at each recipe step
---- onstop(transformer, user, recipe_name): (optional) called when the recipe stops
Transformer:set(id, cfg)

-- remove a transformer
Transformer:remove(id)

Menu

transformer

Transformer menu.

data
transformer

transformer instance

VehBlacklist

This module adds a vehicle blacklist (remove vehicles from the game).

Extension

Server
self.cfg
Client
self.veh_models -- map of model hash
self.interval

Warp

This module spawns warp points for player/vehicle teleportation.

Extension

Server
self.cfg

Map entity

Warp

Default animated entity for the warp module. Inherits from Map.PosEntity.

config
color

(optional) {r,g,b,a} marker color