XP Ranking System for FiveM
ESX
and QBCore
Select an option:
xperience
as a standalone resource then import xperience_standalone.sql
onlyESX
with Config.UseESX
set to true
then import xperience_esx.sql
only. This adds the xp
and rank
columns to the users
table
esx_xp
, then don't import xperience_esx.sql
, instead see Transitioning from esx_xpQBCore
with Config.UseQBCore
set to true
then there's no need to import any sql
files as the xp and rank are saved to the player's metadata - see QBCore Integrationthen:
xperience
directory into you resources
directoryensure xperience
to your server.cfg
fileBy default this resource uses oxmysql
, but if you don't want to use / install it then you can use mysql-async
by following these instructions:
'@mysql-async/lib/MySQL.lua',
line in fxmanifest.lua
and comment out the '@oxmysql/lib/MySQL.lua'
lineIf you previously used esx_xp
and are still using es_extended
then do the following to make your current stored xp / rank data compatible with xperience
rp_xp
column in the users
table to xp
rp_rank
column in the users
table to rank
Config.UseESX
to true
Give XP to player
exports.xperience:AddXP(xp --[[ integer ]])
Take XP from player
exports.xperience:RemoveXP(xp --[[ integer ]])
Set player's XP
exports.xperience:SetXP(xp --[[ integer ]])
Set player's rank
exports.xperience:SetRank(rank --[[ integer ]])
Get player's XP
exports.xperience:GetXP()
Get player's rank
exports.xperience:GetRank()
Get XP required to rank up
exports.xperience:GetXPToNextRank()
Get XP required to reach defined rank
exports.xperience:GetXPToRank(rank --[[ integer ]])
Listen for rank up event on the client
AddEventHandler("xperience:client:rankUp", function(newRank, previousRank, player)
-- do something when player ranks up
end)
Listen for rank down event on the client
AddEventHandler("xperience:client:rankDown", function(newRank, previousRank, player)
-- do something when player ranks down
end)
Get player's XP
exports.xperience:GetPlayerXP(playerId --[[ integer ]])
Get player's rank
exports.xperience:GetPlayerRank(playerId --[[ integer ]])
Get player's required XP to rank up
exports.xperience:GetPlayerXPToNextRank(playerId --[[ integer ]])
Get player's required XP to reach defined rank
exports.xperience:GetPlayerXPToRank(playerId --[[ integer ]], rank --[[ integer ]])
TriggerClientEvent('xperience:client:addXP', playerId --[[ integer ]], xp --[[ integer ]])
TriggerClientEvent('xperience:client:removeXP', playerId --[[ integer ]], xp --[[ integer ]])
TriggerClientEvent('xperience:client:setXP', playerId --[[ integer ]], xp --[[ integer ]])
TriggerClientEvent('xperience:client:setRank', playerId --[[ integer ]], rank --[[ integer ]])
RegisterNetEvent('xperience:server:rankUp', function(newRank, previousRank)
-- do something when player ranks up
end)
RegisterNetEvent('xperience:server:rankDown', function(newRank, previousRank)
-- do something when player ranks down
end)
You can define callbacks on each rank by using the Action
function.
The function will be called both when the player reaches the rank and drops to the rank.
You can check whether the player reached or dropped to the new rank by utilising the rankUp
parameter.
Config.Ranks = {
[1] = { XP = 0 },
[2] = {
XP = 800, -- The XP required to reach this rank
Action = function(rankUp, prevRank, player)
-- rankUp: boolean - whether the player reached or dropped to this rank
-- prevRank: number - the player's previous rank
-- player: integer - The current player
end
},
[3] = { XP = 2100 },
[4] = { XP = 3800 },
...
}
If Config.UseQBCore
is set to true
then the player's xp and rank are stored in their metadata. The metadata is saved whenever a player's xp / rank changes.
local PlayerData = QBCore.Functions.GetPlayerData()
local xp = PlayerData.metadata.xp
local rank = PlayerData.metadata.rank
local Player = QBCore.Functions.GetPlayer(src)
local xp = Player.PlayerData.metadata.xp
local rank = Player.PlayerData.metadata.rank
local xPlayer = ESX.GetPlayerById(src)
local xp = xPlayer.get('xp')
local rank = xPlayer.get('rank')
-- Set the theme
/setXPTheme [theme]
These require ace permissions: e.g. add_ace group.admin command.addXP allow
-- Award XP to player
/addXP [playerId] [xp]
-- Deduct XP from player
/removeXP [playerId] [xp]
-- Set a player's XP
/setXP [playerId] [xp]
-- Set a player's rank
/setRank [playerId] [rank]
The theme can be set by the player using the /setXPTheme [theme]
command. The theme
argument must exist in the Config.Themes
table in config.lua
for it to work:
Config.Theme = 'native' -- Set the default theme (must exist in the Config.Themes table)
Config.Themes = {
native = {
segments = 10, -- Sets the number of segments the XP bar has. Native = 10, Max = 20
width = 532 -- Sets the width of the XP bar in px
},
hitman = {
segments = 80,
width = 800
},
hexagon = {
segments = 16,
width = 400
},
}
Let's say you want to add a theme called myTheme
:
Config.Themes
table using the name of the theme as the index:Config.Themes = {
...
myTheme = {
segments = 20,
width = 650
}
}
Create the theme's .css
file in ui/css
directory with the theme-
prefix:
ui/css/theme-myTheme.css
Set Config.Theme
to read your new theme:
Config.Theme = 'myTheme'
<div class="xperience">
<div class="xperience-inner">
<div class="xperience-rank">
<div>XXXX</div> <!-- CURRENT RANK -->
</div>
<div class="xperience-progress"> <!-- MAIN PROGRESS BAR -->
<div class="xperience-segment"> <!-- BAR SEGMENT (IF YOU'VE SET THE THEME'S SEGMENTS TO 10 THEN THERE'LL BE 10 OF THESE) -->
<div class="xperience-indicator--bar"></div> <!-- SEGMENT INDICATOR (ONLY USED WHEN XP IS UPDATING)-->
<div class="xperience-progress--bar"></div> <!-- SEGMENT PROGRESS -->
</div>
...
</div>
<div class="xperience-rank">
<div>XXXX</div> <!-- NEXT RANK -->
</div>
</div>
<div class="xperience-data">
<span>XXXX</span> <!-- CURRENT XP -->
<span>XXXX</span> <!-- XP REQUIRED FOR NEXT RANK -->
</div>
</div>
Example of awarding players 100XP for every 30mins of playtime
-- Server side
CreateThread(function()
local interval = 30 -- interval in minutes
local xp = 100 -- XP amount to award every interval
while true do
for i, src in pairs(GetPlayers()) do
TriggerClientEvent('xperience:client:addXP', src, xp)
end
Wait(interval * 60 * 1000)
end
end)
Example of giving a player 100 XP for shooting another player
AddEventHandler('gameEventTriggered', function(event, data)
if event == "CEventNetworkEntityDamage" then
local victim = tonumber(data[1])
local attacker = tonumber(data[2])
local weaponHash = tonumber(data[5])
local meleeDamage = tonumber(data[10]) ~= 0 and true or false
-- Don't register melee damage
if not meleeDamage then
-- Check victim and attacker are both players
if (IsEntityAPed(victim) and IsPedAPlayer(victim)) and (IsEntityAPed(attacker) and IsPedAPlayer(attacker)) then
if attacker == PlayerPedId() then -- We are the attacker
exports.xperience:AddXP(100) -- Give player 100 xp for getting a hit
end
end
end
end
end)
You can either utilise Rank Events or Rank Actions.
Example of giving a minigun with 500
bullets to a player for reaching rank 10
:
AddEventHandler("xperience:client:rankUp", function(newRank, previousRank, player)
if newRank == 10 then
local weapon = `WEAPON_MINIGUN`
if not HasPedGotWeapon(player, weapon, false) then
-- Player doesn't have weapon so give it them loaded with 500 bullets
GiveWeaponToPed(player, weapon, 500, false, false)
else
-- Player has the weapon so give them 500 bullets for it
AddAmmoToPed(player, weapon, 500)
end
end
end)
Config.Ranks = {
[1] = { XP = 0 },
[2] = { XP = 800 },
[3] = { XP = 2100 },
[4] = { XP = 3800 },
[5] = { XP = 6100 },
[6] = { XP = 9500 },
[7] = { XP = 12500 },
[8] = { XP = 16000 },
[9] = { XP = 19800 },
[10] = {
XP = 24000,
Action = function(rankUp, prevRank, player)
if rankUp then -- only run when player moved up to this rank
local weapon = `WEAPON_MINIGUN`
if not HasPedGotWeapon(player, weapon, false) then
-- Player doesn't have weapon so give it them loaded with 500 bullets
GiveWeaponToPed(player, weapon, 500, false, false)
else
-- Player has the weapon so give them 500 bullets for it
AddAmmoToPed(player, weapon, 500)
end
end
end
},
[11] = { XP = 28500 },
...
}
xperience - XP Ranking System for FiveM
Copyright (C) 2021 Karl Saunders
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>