.1.1. The Problem
Given a graph 
This “definition” remains vague enough to encompass the most known variants of the VRP. Indeed, not only does the VRP
exist in different flavors (capacitated, multi-depots, with time-windows, with pick-up and delivery, …) but several
slightly different definitions exist in the literature.
In this manual, we will use the definition given by Gilbert Laporte in [Laporte1992]. In this article, a VRP
is designed in such a way that
The last point is important. Indeed, without side constraints and if the graph obeys the triangle inequality
(i.e. 





The most common side constraints include:
And the list goes on.
For our basic version of the VRP, all vehicles must be used. This version of the VRP
is better known as the mTSP[2]. Some problems can be coined as mTSP and we refer again the reader to [Bektas2006]
to find some examples.
Below you’ll find a picture of a solution of a VRP with 32 cities and 5 vehicles (A-n32-k5) in the
sub-section Visualization with ePix.
.1.5. To hold and check a (C)VRP solution: the CVRPSolution class
To represent a (C)VRP solution, we have defined the CVRPSolution class. Two constructors are available:
Two methods verify the feasibility of the solution:
The CVRPSolution class provides iterators to run through the solution. For instance, the
ComputeObjectiveValue() method — that computes the objective value of the solution — is written as follows:
Because this method is constant and doesn’t change the solution, it uses constant iterators. The CVRPSolution
class also provides the following non constant iterators:
Audio
-- TUNNEL CLIENT API-- 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)vRP.playAudioSource(url, volume, x, y, z, max_dist)-- 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)vRP.setAudioSource(name, url, volume, x, y, z, max_dist)-- remove named audio sourcevRP.removeAudioSource(name)Database
SQL queries are managed by DB drivers, you can use the default vRP driver vrp_mysql or use a custom one (vrp_mysql has crappy code, see alternatives).
DB drivers will register themselves (as resources) with a specific name to use in cfg/base.lua. Since there is no guarantee about when the driver will be registered, all queries will be cached until that moment.
-- API (PROXY)-- register a DB driver--- name: unique name for the driver--- on_init(cfg): called when the driver is initialized (connection), should return true on success---- cfg: db config--- on_prepare(name, query): should prepare the query (@param notation)--- on_query(name, params, mode): should execute the prepared query---- params: map of parameters---- mode:----- "query": should return rows (list of map of parameter => value), affected----- "execute": should return affected----- "scalar": should return a scalarvRP.registerDBDriver(name, on_init, on_prepare, on_query)-- prepare a query--- name: unique name for the query--- query: SQL string with @params notationvRP.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 field => value), affected---- "execute": should return affected---- "scalar": should return a scalarvRP.query(name, params, mode)-- shortcut for vRP.query with "execute"vRP.execute(name, params)-- shortcut for vRP.query with "scalar"vRP.scalar(name, params)Group/permission
Group and permissions are a way to limit features to specific players.
Each group have a set of permissions defined in cfg/groups.lua.
Permissions can be used with most of the vRP modules, giving the ability to create specific garages, item transformers, etc.
Api
-- PROXY API-- return group titlevRP.getGroupTitle(group)-- add a group to a connected uservRP.addUserGroup(user_id,group)-- remove a group from a connected uservRP.removeUserGroup(user_id,group)-- check if the user has a specific groupvRP.hasGroup(user_id,group)-- register a special permission function-- name: name of the permission -> "!name.[...]"-- callback(user_id, parts)--- parts: parts (strings) of the permissions, ex "!name.param1.param2" -> ["name", "param1", "param2"]--- should return true or false/nilvRP.registerPermissionFunction(name, callback)-- check if the user has a specific permissionvRP.hasPermission(user_id, perm)-- check if the user has a specific list of permissions (all of them)vRP.hasPermissions(user_id, perms)-- get user group by group type-- return group name or an empty stringvRP.getUserGroupByType(user_id,gtype)-- return list of connected users by groupvRP.getUsersByGroup(group)-- return list of connected users by permissionvRP.getUsersByPermission(perm)Inventory
The inventory is autosaved and, as the wallet, gets empty upon death.
Items
Items are simple identifiers associated with a quantity in an inventory. But they can also be parametrics.
Parametrics items are identified like other items in the inventory but also have arguments as: weapon|pistol instead of just an ID. Parametric items don’t contain any data, they are generic item definitions that will be specialized by the arguments.
-- PROXY API-- define an inventory item (call this at server start) (parametric or plain text data)-- idname: unique item name-- name: display name or genfunction-- description: item description (html) or genfunction-- choices: menudata choices (see gui api) only as genfunction or nil-- weight: weight or genfunction---- genfunction are functions returning a correct value as: function(args) return value end-- where args is a list of {base_idname,arg,arg,arg,...}vRP.defInventoryItem(idname,name,description,choices,weight)-- return name, description, weightvRP.getItemDefinition(idname)
vRP.getItemName(idname)
vRP.getItemDescription(idname)
vRP.getItemChoices(idname)
vRP.getItemWeight(idname)-- add item to a connected user inventoryvRP.giveInventoryItem(user_id,idname,amount,notify)-- try to get item from a connected user inventory-- return true if the item has been found and the quantity removedvRP.tryGetInventoryItem(user_id,idname,amount,notify)-- get item amount from a connected user inventoryvRP.getInventoryItemAmount(user_id,idname)-- get connected user inventory-- return map of full idname => amount or nilvRP.getInventory(user_id)-- clear connected user inventoryvRP.clearInventory(user_id)-- compute weight of a list of items (in inventory/chest format)vRP.computeItemsWeight(items)-- return user inventory total weightvRP.getInventoryWeight(user_id)-- return user inventory max weightvRP.getInventoryMaxWeight(user_id)-- open a chest by name-- cb_close(): called when the chest is closedvRP.openChest(source, name, max_weight, cb_close)-- TUNNEL SERVER API-- TUNNEL CLIENT APIOnce defined, items can be used by any resources (ex: they can be added to shops).
local Proxy =module("vrp", "lib/Proxy")local Tunnel =require("vrp", "lib/Tunnel")
vRP = Proxy.getInterface("vRP")
vRPclient = Tunnel.getInterface("vRP","vrp_waterbottle")-- create Water bottle itemlocal wb_choices = {} -- (see gui API for menudata choices structure)wb_choices["Drink"] = {function(player,choice) -- add drink actionlocal user_id = vRP.getUserId(player) -- get user_idif user_id thenif vRP.tryGetInventoryItem(user_id,"water_bottle",1) then-- try to remove one bottle vRP.varyThirst(user_id,-35) -- decrease thirst vRPclient.notify(player,"~b~ Drinking.") -- notify vRP.closeMenu(player) -- the water bottle is consumed by the action, close the menuendendend,"Do it."}-- add item definitionvRP.defInventoryItem("water_bottle","Water bottle","Drink this my friend.",function() return wb_choices end,0.5)-- (at any time later) give 2 water bottles to a connected uservRP.giveInventoryItem(user_id,"water_bottle",2)Item transformer
The item transformer is a very generic way to create harvest and processing areas.
you can use the action of the item transformer when entering the area
the item transformer has a number of work units, regenerated at a specific rate
the item transformer takes reagents (money, items or none) to produce products (money or items) and it consumes a work unit
This way, processing and harvesting are limited by the work units.
-- add an item transformer-- name: transformer id name-- itemtr: item transformer definition table--- name--- permissions (optional)--- max_units--- units_per_minute--- x,y,z,radius,height (area properties)--- r,g,b (color)--- recipes, map of action =>---- description---- in_money---- out_money---- reagents: items as idname => amount---- products: items as idname => amount---- aptitudes: list as "group.aptitude" => exp amount generated--- onstart(player,recipe): optional callback--- onstep(player,recipe): optional callback--- onstop(player,recipe): optional callbackvRP.setItemTransformer(name,itemtr)-- remove an item transformervRP.removeItemTransformer(name)local itemtr = { name="Water bottles tree", -- menu name r=0,g=125,b=255, -- color max_units=10, units_per_minute=5, x=1858,y=3687.5,z=34.26, -- pos radius=5, height=1.5, -- area recipes = { ["Harvest"] = { -- action name description="Harvest some water bottles.", -- action description in_money=0, -- money taken per unit out_money=0, -- money earned per unit reagents={}, -- items taken per unit products={ -- items given per unit ["water_bottle"] =1 } } }
}
vRP.setItemTransformer("my_unique_transformer",itemtr)Items
Items are simple identifiers associated with a quantity in an inventory. But they can also be parametrics.
Parametrics items are identified like other items in the inventory but also have arguments as: weapon|pistol instead of just an ID. Parametric items don’t contain any data, they are generic item definitions that will be specialized by the arguments.
-- PROXY API-- define an inventory item (call this at server start) (parametric or plain text data)-- idname: unique item name-- name: display name or genfunction-- description: item description (html) or genfunction-- choices: menudata choices (see gui api) only as genfunction or nil-- weight: weight or genfunction---- genfunction are functions returning a correct value as: function(args) return value end-- where args is a list of {base_idname,arg,arg,arg,...}vRP.defInventoryItem(idname,name,description,choices,weight)-- return name, description, weightvRP.getItemDefinition(idname)
vRP.getItemName(idname)
vRP.getItemDescription(idname)
vRP.getItemChoices(idname)
vRP.getItemWeight(idname)-- add item to a connected user inventoryvRP.giveInventoryItem(user_id,idname,amount,notify)-- try to get item from a connected user inventory-- return true if the item has been found and the quantity removedvRP.tryGetInventoryItem(user_id,idname,amount,notify)-- get item amount from a connected user inventoryvRP.getInventoryItemAmount(user_id,idname)-- get connected user inventory-- return map of full idname => amount or nilvRP.getInventory(user_id)-- clear connected user inventoryvRP.clearInventory(user_id)-- compute weight of a list of items (in inventory/chest format)vRP.computeItemsWeight(items)-- return user inventory total weightvRP.getInventoryWeight(user_id)-- return user inventory max weightvRP.getInventoryMaxWeight(user_id)-- open a chest by name-- cb_close(): called when the chest is closedvRP.openChest(source, name, max_weight, cb_close)-- TUNNEL SERVER API-- TUNNEL CLIENT APIOnce defined, items can be used by any resources (ex: they can be added to shops).
local Proxy =module("vrp", "lib/Proxy")local Tunnel =require("vrp", "lib/Tunnel")
vRP = Proxy.getInterface("vRP")
vRPclient = Tunnel.getInterface("vRP","vrp_waterbottle")-- create Water bottle itemlocal wb_choices = {} -- (see gui API for menudata choices structure)wb_choices["Drink"] = {function(player,choice) -- add drink actionlocal user_id = vRP.getUserId(player) -- get user_idif user_id thenif vRP.tryGetInventoryItem(user_id,"water_bottle",1) then-- try to remove one bottle vRP.varyThirst(user_id,-35) -- decrease thirst vRPclient.notify(player,"~b~ Drinking.") -- notify vRP.closeMenu(player) -- the water bottle is consumed by the action, close the menuendendend,"Do it."}-- add item definitionvRP.defInventoryItem("water_bottle","Water bottle","Drink this my friend.",function() return wb_choices end,0.5)-- (at any time later) give 2 water bottles to a connected uservRP.giveInventoryItem(user_id,"water_bottle",2)Player state
-- PROXY API-- TUNNEL SERVER API-- TUNNEL CLIENT API-- get player weapons data-- return table with weapons data, use print(json.encode(result)) to understand the structurevRP.getWeapons()-- give weapons-- weapons: same structure as returned by getWeapons()-- (optional) clear_before: if true, will remove all the weapons before adding the new onesvRP.giveWeapons(weapons,clear_before)-- get player apparence customization data-- return table with customization data, use print(json.encode(result)) to understand the structure-- .model or .modelhash define the player model, the indexes define each component as [drawable_id,texture_id,palette_id] array-- props are referenced using the prefix "p" for the key (p0,p1,p2,p...), -1 = no propvRP.getCustomization()-- set player apparence-- customization_data: same structure as returned by getCustomization()vRP.setCustomization(customization_data)-- set player armour (0-100)vRP.setArmour(amount)Proxy
The proxy lib is used to call other resources functions through a proxy event.
The notation is Interface.function(…).
Survival
Running, walking, being hurt/injured, and just living add hunger and thirst. When the hunger and the thirst are at their maximum level (100%), next hunger/thirst overflow will damage the character by the same amount (ex: when thirsty, don’t run, take a car).
The survival module implement also a coma system. If the health of the player is below the coma threshold, the player is in coma for a specific duration before dying. The health (thus coma) is recorded in the player state.
If a player disconnect and reconnect while in coma, he will fall in coma again and die in a few seconds.
Tunnel
The idea behind tunnels is to easily access any declared server function from any client resource, and to access any declared client function from any server resource.
-- build the client-side interfaceclientdef = {} -- you can add function to clientdef later in other client scriptsTunnel.bindInterface("myrsc",clientdef)functionclientdef.teleport(x,y,z) SetEntityCoords(GetPlayerPed(-1), x, y, z, 1,0,0,0)end-- sometimes, you would want to return the tunnel call with asynchronous data-- ex:functionclientdef.setModel(hash) local r =async() Citizen.CreateThread(function() -- do the asynchronous model loading Citizen.Wait(1000) r(true) -- return trueend) return r:wait() -- wait for the async returned valueend-- get the server-side accessserveraccess = Tunnel.getInterface("myrsc")-- call test on server and print the returned value (in an async context)local r = serveraccess.test("my client message")print(r) -- trueNow if we want to use the same teleport function in another resource:
This way resources can easily use other resources client/server API.
The notation is Interface.function(dest, …).
