This is vRP 2, the second major version of vRP. It aims to have less overhead and a more straightforward and structured approach using OOP. This will probably be the last major version, vRP based resources can be updated for vRP 2 using the extension system.
Download page:
v2.0 vRP_fivem.zip
v1.0 vRP_1.0_vrpex.zip
Features
basic admin tools (kick,ban,whitelist), groups/permissions, languages, identification system (persistent user id for database storage), user/character/server/global custom data key/value
player state (survival vitals, weapons, player appearance, position)
player identity/phone/aptitudes (education/exp), emotes, business system / money (wallet/bank), homes
ℹ️ 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. Extension 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/v...ic-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/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 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.
Data 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. 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, so extensions should properly handle character load/unload events and check if the character is ready. 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 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. Asynchronous vRP 2 extensively uses asynchronous tasks in a transparent way using coroutines, some functions may not return immediately (or never).
Loading script 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 vrp.lua The content of vrp.lua is now executed in the vRP context and can now use the API.
ℹ️ vRP.loadScript() has the same behavior as module(), but executed in the vRP context. 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)
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) 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: 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)
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() 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
Added: May 1 2020, 07:45 AM
Modified: Never edited!
Thanks List: [+]
Add Feedback
You cannot post yet! Get involved on GVME by posting your thoughts. Give authors your feedback to help them develop.