Skip to main content
TRACK B·FRAMEWORK INTEGRATION·Verified June 2026 · Lua 5.4 · ox_lib 3.x
Learning with an AI assistant?
Copies this lesson plus 2026 ground rules (no lua54 'yes', Cfx.re Portal, correct callback signatures) as a ready-to-paste mentor prompt.

Multi-character and identity systems

Identity is the spine of a roleplay server: who is this player, which character are they on, and what belongs to that character. Every other script, money, inventory, jobs, keys off the character identifier, so if the identity layer is wrong, everything downstream is wrong. This lesson builds a tiny identity store you can run, then takes it apart so you understand the model instead of trusting a framework blindly.

You'll build
A server resource named qu_identity that maps one license to two characters and proves money belongs to the character, not the account.
Time
~25 minutes.
You need
A framework server, MariaDB wired to oxmysql, and the database lessons.
You'll learn
FiveM identifiers and why license: is the anchor -> the two-table account/character model -> how a framework loads a character on selection -> why keying money on the wrong column shares it across characters.
BEFORE YOU START

Build it

This is a server-only resource. There is no client file and no F8 output: identity lives entirely on the server, against the database, which is the whole point. You will run one command in the server console and read the proof there.

Make the resource folder

The server has one folder for this lesson.

Inside your server's resources folder, create this folder:

code
resources/qu_identity

Create the files

Every file named in the manifest exists.

Create this exact file layout:

code
resources/qu_identity/
fxmanifest.lua
server.lua

Write fxmanifest.lua and the tables

FiveM loads oxmysql first, and the database has the account/character shape.

Open fxmanifest.lua and paste this:

code
fx_version 'cerulean'
game 'gta5'

server_script '@oxmysql/lib/MySQL.lua'
server_script 'server.lua'

dependencies {
'oxmysql'
}

Then run this SQL against your MariaDB before starting the resource. These two tables are the entire identity model in miniature:

code
CREATE TABLE IF NOT EXISTS qu_accounts (
license VARCHAR(64) NOT NULL,
PRIMARY KEY (license)
);

CREATE TABLE IF NOT EXISTS qu_characters (
citizenid  VARCHAR(16) NOT NULL,
license    VARCHAR(64) NOT NULL,
first_name VARCHAR(40) NOT NULL,
money      INT NOT NULL DEFAULT 0,
PRIMARY KEY (citizenid),
UNIQUE KEY uniq_license_name (license, first_name),
FOREIGN KEY (license) REFERENCES qu_accounts (license)
);

Write the lesson code

The license-to-character mapping and the per-character money rule are runnable.

Open server.lua and paste this:

code
local function makeCitizenId()
return ('QU%07d'):format(math.random(0, 9999999))
end

local function ensureCharacter(license, firstName)
local existing = MySQL.scalar.await(
    'SELECT citizenid FROM qu_characters WHERE license = ? AND first_name = ?',
    { license, firstName }
)
if existing then return existing end

local citizenid = makeCitizenId()
MySQL.insert.await('INSERT INTO qu_characters (citizenid, license, first_name) VALUES (?, ?, ?)',
    { citizenid, license, firstName })
return citizenid
end

RegisterCommand('identitytest', function()
local license = 'license:qu000000000000000000000000000000demo'

MySQL.insert.await('INSERT IGNORE INTO qu_accounts (license) VALUES (?)', { license })

local charA = ensureCharacter(license, 'Avery')
local charB = ensureCharacter(license, 'Blake')

MySQL.update.await('UPDATE qu_characters SET money = ? WHERE citizenid = ?', { 5000, charA })

local moneyA = MySQL.scalar.await('SELECT money FROM qu_characters WHERE citizenid = ?', { charA })
local moneyB = MySQL.scalar.await('SELECT money FROM qu_characters WHERE citizenid = ?', { charB })

print(('[qu_identity] one license: %s'):format(license))
print(('[qu_identity] character A %s money %d'):format(charA, moneyA))
print(('[qu_identity] character B %s money %d'):format(charB, moneyB))
end, true)

Start and test it

Two characters share one license, but only one has the money.

Open server.cfg and add this line:

code
ensure qu_identity

Save, then run this in the server console (txAdmin Live Console):

code
restart qu_identity

Now run the test command in the same console:

code
identitytest

The two QU ids are random, so yours will differ. What must match is the shape: both characters hang off the same license line, character A holds 5000, and character B holds 0. You just stored money against a character, and the other character on the same account never saw it.

Keep reading the full lesson

Sign in to start, then unlock every step of this lesson and the full FiveM School with a membership.

Still ahead in this lesson
  • How it works
  • If something went wrong
  • What you can do now
  • Try it yourself

The remainder of Multi-character and identity systems is available to FiveM School members.