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 (you can access stuff even if not exposed) and with less overhead. |
An extension is a class extending vRP. Two versions of an extension (same name) can be loaded server-side and client-side.
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/ImagicTheCat/vRP-basic-mission. |
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")
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.
|
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/should not use proxy between them. |
The tunnel is accessible (from the client-side or server-side extension) like this:
-- 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
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.
vRP provides basic methods 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).
global |
GData |
per server |
SData |
per user |
UData |
per character |
CData |
🔥
|
vRP core tables, as core files, should not be modified for the same reasons. New tables and queries should be created if the data system is not powerful enough. |
The proxy library is used to call other resources functions through a proxy event.
The idea behind tunnel is to easily access any declared server function from any client resource, and to access any declared client function from any server resource.
💡
|
Good practice is to get the interface once and set it as a global, if you want to get multiple times the same interface from the same resource, you 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 prefix for the function name (Proxy/Tunnel interfaces should not have functions starting with ). This will discard the returned values, but if you still need them, you 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, you will need to delay it with Citizen.CreateThread or a SetTimeout .
|
To use vRP 2, a script must be loaded in the vRP resource context.
-- include `@vrp/lib/utils.lua` in `__resource.lua` of the resource
local Proxy = module("vrp", "lib/Proxy")
local vRP = Proxy.getInterface("vRP")
vRP.loadScript("my_resource", "server_vrp") -- load server_vrp.lua
The content of server_vrp.lua
is now executed in the vRP context and can now use the API.
lib/utils.lua
defines some useful globals.
-- side detection
SERVER -- boolean
CLIENT -- boolean
-- load a lua resource file as module
-- 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)
parseDouble(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)
-- 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)
-- 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 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
-- 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)
self.remote -- tunnel interface to other network side
-- level: (optional) level, 0 by default
Extension:log(msg, level)
Extension:error(msg)
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)
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)
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) |
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)
vRP:isBanned(user_id)
vRP:setBanned(user_id,banned)
vRP:isWhitelisted(user_id)
vRP:setWhitelisted(user_id,whitelisted)
-- 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)
vRP:ban(user,reason)
vRP:kick(user,reason)
vRP:save()
(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, |
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) |
See modules/.