Origin Multicharacter
A cinematic character selection, creation and spawn selector for Qbox, QBCore, ESX and standalone servers. One resource replaces qb-multicharacter + qb-spawn, esx_multicharacter, and Qbox's built-in character menu.
Features
Character selection
- All of a player's characters stand side by side in the real GTA world, GTA Online style (lineup), with an empty spot for a new one. Small interiors can show one at a time (single)
- Someone always stands there: the empty spot gets a stand-in ped. It's solid for a brand-new player's first character and faded next to existing characters (Config.Selection.placeholderPed)
- The camera frames the lineup by itself: it works out distance and field of view for any number of characters and any screen shape. It moves closer or turns around when a wall, glass or a building is in the way
- Characters only stand on real ground: on narrow or uneven spots the lineup squeezes closer together, and if it still doesn't fit, the scene shows one character at a time instead
- Clean, minimal look: each character's name, surname and job sit underneath them, and there's one small action bar at the bottom
- Click a character (or its name), use ←/→, and press Enter to play. The camera glides over, the selected character gets a brighter light and waves
- Drag to rotate, scroll to zoom
- Players can cycle scenes, and the choice is remembered per player
- Each player sits in a private routing bucket while choosing
- The action bar shows the fields you choose: cash, bank, job, gang, birth date, nationality, phone, playtime, last played
- Never a black screen: on joining, the server's loading screen stays up until the scene is ready. If anything fails (a Lua error, a conflicting resource, Qbox's own menu still on, ESX's Config.Multichar off), an error screen says what's wrong and offers Retry and Disconnect. A watchdog catches any step that stops making progress for 45 seconds
Slots
- Default slot count, plus per-identifier overrides, ACE permissions, Discord roles (via a bot), database grants (/mc_setslots) and a hook for your own logic (e.g. Tebex)
- Locked teaser cards with a link to your store
- If a player loses slots, their extra characters stay visible but locked; nothing is deleted
Creation
- First and last name, date of birth, gender (the 3D preview updates live), searchable nationality list or free text, a height slider (cm and ft/in), optional backstory, and an optional "I accept the rules" box
- An ID card preview fills in as the player types
- Validation runs live in the UI, then again on the server, which has the final say. It covers length, allowed characters (accents optional), substring blacklist, reserved words, real calendar dates and age limits. Global unique names are optional
- Opens the player's clothing menu afterwards: illenium-appearance, fivem-appearance, qb-clothing, esx_skin, or your own
- Starter items: uses the framework's defaults (QBCore Shared.StarterItems, Qbox starterItems) or your own list. Supports ox_inventory, qb-inventory, ps-inventory, lj-inventory and ESX. ID cards get name and date-of-birth metadata
Deletion
- Soft delete (default): the character can be recovered with /mc_restore for N days, then is purged automatically. Hard delete is also available
- Confirmation by hold-to-delete, by typing the character's name, or both. Has a cooldown and an optional ACE permission
Spawn selector
- A drone camera orbits each location. Nearby spots fly over the map; distant ones cut with a quick fade
- Simple fade into the world (default) or the GTA "switch" sky transition
- New characters start the game right where they stood in the character scene, with no spawn picker (Config.Spawn.newCharacter.spawnInScene). A scene that isn't safe to start in can set spawnHere = false (new characters use Config.Spawn.newCharacter.default, LSIA arrivals) or a vector4 to start them somewhere else, e.g. outside the door. None of the included scenes sets it
- Last location (forced automatically if the character was dead, jailed or inside a property), public spawns, job/gang/ACE-restricted spawns, new-only/existing-only spawns
- qb-apartments: starter apartment selection for new characters (read from qb-apartments' own config) and a "My apartment" spawn
- qb-houses: every owned house as a spawn
- Any other housing script plugs in through one hook
- Categories, search, thumbnails, and an optional "back to characters" button
Interface - Clean minimal design: thin light type floating over the game with no boxes. Themeable: accent colours, radius, any Google Font, logo, left or right layout - Optional extras, all off or easy to switch off: intro splash, server news cards, ID card preview, social/store links, live player count, background music, native GTA UI sounds - Scales correctly from 720p to 4K and ultrawide - Opens in a normal browser with mock data, so you can theme it without a server (see Browser preview)
Server owner tools
- Discord webhook logs for create, delete, restore, select, spawn, relog and slot changes
- /relog with a cooldown, an optional ACE permission and a hook
- Admin commands, exports and events for other resources
- Its own tables are created automatically; no SQL import needed
- English, German, French and Spanish; easy to add more
Requirements
- oxmysql
- OneSync (standard on modern servers)
- One of:
qbx_core,qb-core,es_extended(Legacy), or nothing (standalone) - Optional: a clothing resource (illenium-appearance recommended)
Installation
- Drop
origin_multicharacterinto your resources folder. - Remove or stop the resource this replaces (see your framework below).
- Start it after your framework and oxmysql in
server.cfg:
ensure oxmysql
ensure qb-core # or qbx_core / es_extended
ensure illenium-appearance
ensure origin_multicharacter
- Join the server. Tables are created automatically on first start.
QBCore
- Remove
qb-multicharacterandqb-spawn. - Works with
qb-apartments(starter apartments) andqb-housesout of the box. - Anything that triggers
qb-multicharacter:client:chooseCharstill works.
Qbox
- Required: in
qbx_core/config/client.luasetcharacters.useExternalCharacters = true, then restart the server. While it'sfalse, Qbox opens its own menu at the same time and the two fight over the camera. That's the usual cause of a black screen, so this resource detects it and shows an error telling you what to change. - Remove
qbx_spawnif you use it. - Apartments (
qbx_properties): by default (Config.Spawn.properties['qbx_properties'].starter = 'auto'), every character without an apartment gets its starter apartment automatically. The resource writes the same database rows qbx_properties' apartment board would, using apartment optionstarterApartmentfrom qbx_properties'config/shared.lua. This works whatever qbx_core'scharacters.startingApartmentsays, and with it on, Qbox's board (the map with apartment markers) never shows. New characters still spawn where they stand. Owned apartments show up in the spawn picker, and players who logged out inside their apartment are put back inside, the same way qbx_spawn does it. qbx_spawn isn't needed.starter = 'board'leaves it to Qbox's board.starter = falseonly stops this resource from creating apartments: with qbx_core'scharacters.startingApartment = true(Qbox's default), qbx_properties' board still shows for every character without one. To have no starter apartments at all, setstartingApartment = falsein qbx_core. - Starter items come from qbx_core's
starterItems(config/shared.lua, orconfig/server.luaon qbx_core older than 1.15) whenConfig.StarterItems.useFrameworkDefaults = true.
ESX Legacy
- Remove
esx_multicharacter. You don't needesx_identity's registration either, because creation happens here. - In es_extended's config set
Config.Multichar = true. Newer versions compute it from whetheresx_multicharacteris running, so set it totrueexplicitly. - Set
Config.ESX.dateFormatto match the format your other scripts expect inusers.dateofbirth. - Characters use ESX's standard
charN:licenseidentifiers, so existing characters show up as they are.
Standalone
- Nothing else is needed. Characters are stored in
origin_characters. - Other resources read the active character with
exports.origin_multicharacter:GetCharacter(source)or the replicated state bagPlayer(source).state.character.
The console warns you on start if it finds a conflicting resource or a wrong framework setting.
Configuration
| File | What it controls |
|---|---|
config/config.lua |
Slots, creation rules, deletion, selection, relog, spawning, appearance, starter items, logs, interface/theme |
config/scenes.lua |
Where characters are shown |
config/spawns.lua |
Spawn locations |
editable/server.lua |
Server hooks: gate actions, extra slots, extra spawns, reactions |
editable/client.lua |
Client hooks: notifications, HUD/weather-sync toggles, custom clothing menu |
locales/*.lua |
All text, both in-game and in the UI |
Scenes
Every scene is built around one anchor, vector4(x, y, z, heading). Characters stand on it facing heading, and the camera places itself automatically. In a lineup, characters stand side by side on the ground, closer together on uneven ground. The camera backs off far enough to fit them all, and if a wall or railing is in the way it moves closer, widens, or films from the other side.
Four scenes are included, all one character at a time. New characters start right where they stand:
| id | Where | Mode |
|---|---|---|
yellowjackbar (default) |
Inside the Yellow Jack, by the BEER sign, standing idle | single |
forum |
Forum Drive, Strawberry, leaning on a graffiti garage door | single |
janitor |
The Janitor's apartment, standing idle | single |
clubhouse |
The Lost MC clubhouse bar, sitting on the booth (rpemotes' sitchair) | single |
Other modes: lineup (everyone side by side, GTA Online style) and seats (everyone seated on chairs the scene places, seen from a fixed camera). The comments at the top of config/scenes.lua list every option.
To build your own scene:
1. Set Config.Debug = true, stand where the characters should stand, and face the way they should face, towards the camera.
2. Run /mc_coords and paste the printed vector4 as anchor.
3. Optionally add time, weather, timecycle, spacing, camera overrides, or entities (vehicles or props placed relative to the anchor).
Slots
Config.Slots = {
default = 3, max = 8,
identifiers = { ['license:...'] = 6 },
ace = { { permission = 'origin.slots.vip', slots = 5 } },
discord = { enabled = true, guildId = '...', roles = { ['roleId'] = 4 } },
}
- ACE:
add_ace group.vip origin.slots.vip allow - Discord:
set origin_mc_discord_token "BOT_TOKEN"inserver.cfg. The bot needs to be in your guild with the Server Members intent. - Database:
/mc_setslots <server id | identifier> <slots> - Custom:
Hooks.GetSlots(src, owner, current)ineditable/server.lua
Spawn restrictions
{ id = 'mrpd', label = 'Mission Row PD', category = 'job', icon = 'shield',
coords = vector4(428.23, -984.28, 29.76, 3.5),
jobs = { police = 0 }, onDuty = false, gangs = nil, ace = nil, newOnly = false, existingOnly = false, snap = false }
Other housing scripts
Return spawns from Hooks.GetExtraSpawns in editable/server.lua. event is fired on the client after the teleport:
function Hooks.GetExtraSpawns(src, character, isNew)
local list = {}
for _, house in ipairs(MySQL.query.await('SELECT id, label, x, y, z FROM my_houses WHERE owner = ?', { character.citizenid })) do
list[#list + 1] = {
id = 'myhousing:' .. house.id, label = house.label, category = 'property', icon = 'home',
coords = vector4(house.x, house.y, house.z, 0.0),
event = 'myhousing:client:enter', args = { house.id },
}
end
return list
end
Theme
Config.UI.theme = { accent = '#5aa9ff', accent2 = '#a78bfa', radius = 8, glass = 0.88, font = 'Barlow', displayFont = 'Barlow Condensed' }
Config.UI.layout = 'left' -- or 'right'
accent2 is the second stop of every gradient, so pick a colour that pairs with accent. Any Google Font name works.
Commands
| Command | Who | What |
|---|---|---|
/relog |
players | Back to character selection (name, cooldown and ACE are configurable) |
/mc_setslots <id\|identifier> <n> |
ACE command.mc_setslots |
Grant slots (stored in the database) |
/mc_restore <key> |
ACE command.mc_restore |
Restore a soft-deleted character (citizenid, charN:license, or sa:id) |
/mc_open <id> |
ACE command.mc_open |
Send a player back to character selection |
/mc_purge |
ACE command.mc_purge |
Remove expired soft-deleted characters now |
/mc_coords |
everyone, only with Config.Debug |
Print your position as a vector4 |
add_ace group.admin command.mc_setslots allow
add_ace group.admin command.mc_restore allow
add_ace group.admin command.mc_open allow
add_ace group.admin command.mc_purge allow
Developer API
Server exports
exports.origin_multicharacter:GetSlots(src) -- number
exports.origin_multicharacter:SetSlots(srcOrIdentifier, n)
exports.origin_multicharacter:Relog(src, bypassChecks) -- bypassChecks defaults to true
exports.origin_multicharacter:IsInSelection(src) -- boolean
exports.origin_multicharacter:GetPlaytime(src) -- seconds on the current character
exports.origin_multicharacter:GetIdentityExtra(charKey) -- { height, backstory, nationality }
exports.origin_multicharacter:RegisterSpawn({ id, label, coords, category, icon, jobs, ... })
exports.origin_multicharacter:RemoveSpawn(id)
-- standalone only
exports.origin_multicharacter:GetCharacter(src)
exports.origin_multicharacter:GetCharacterId(src)
Client exports
exports.origin_multicharacter:IsInSelection()
exports.origin_multicharacter:Relog()
Server events (AddEventHandler)
- origin_multicharacter:server:characterCreated (src, character)
- origin_multicharacter:server:characterSelected (src, character)
- origin_multicharacter:server:characterDeleted (src, character, mode)
- origin_multicharacter:server:playerSpawned (src, character, isNew)
- Standalone: origin_multicharacter:server:characterLoaded / :characterUnloaded
Client events
- origin_multicharacter:client:spawned (character, spawnId, isNew)
Event names are prefixed with the resource name, so renaming the folder renames them.
Browser preview
Serve the resource folder over HTTP and open the page:
cd origin_multicharacter
npx serve . # or: python -m http.server
# open http://localhost:3000/web/index.html
Query options: ?view=create, ?view=spawn, ?chars=0 (a brand-new player), ?intro=1, ?news=1, ?idcard=1, ?layout=right, ?accent=%23f97316.
A browser can't render GTA, so the preview puts plain outlines where the characters stand. In game, the real world and your real characters take their place.
Security
- The server is the only authority. The client sends a character key or spawn id, and the server checks it against the list it built for that player. Unknown keys, other players' characters, locked characters and spawn ids that weren't offered are all refused.
- Requests are serialized per player, so double-clicks and spam can't run overlapping logins or deletes. Creation, deletion and relog have cooldowns.
- Private data (identifiers, database keys) never leaves the server.
- Names and backstories are sanitized on the server; the UI escapes everything it renders.
Troubleshooting
- An error screen appears instead of the selection: it names the step that failed. "Check for conflicts" means another character resource is running, or Qbox's own menu is still on. "Load characters from the server" means the server side didn't answer: look at the server console. Anything else: press F8 and look for red
origin_multicharacterlines. - Qbox shows its own menu as well, or the screen stays black: set
characters.useExternalCharacters = trueinqbx_core/config/client.luaand restart the server (a resource restart isn't enough). - Characters stand in a bad spot or the camera clips: switch scenes with the scenery button, or set
Config.Selection.sceneto another id. For your own spot, see Scenes. - ESX character doesn't load, or "Config.Multichar = false" on screen: set
Config.Multichar = truein es_extended's config. Write it literally: newer versions compute it from whether esx_multicharacter is running, which turns it off once you remove that resource. - "This resource started before your framework": in
server.cfg,ensureyour framework (qbx_core,qb-coreores_extended) beforeorigin_multicharacter. - Which version is installed? The server console prints
origin_multicharacter <version>on start, andfxmanifest.luahas the same version. - Weather/time flicker in scenes: pause your weather sync in
Editable.OnOpen(qb-weathersync and cd_easytime are handled already). - Stuck on the clothing camera with no menu: press F8. Errors like
Cannot read properties of undefined (reading 'masks')from illenium-appearance mean it got an incomplete menu config; 1.1.2 always sends the full one. Other errors from your clothing resource: tryConfig.Appearance.providerset to the resource name, or'none'. - Clothing menu opens twice with qb-apartments: some qb-apartments/qb-interior setups open the clothing menu themselves for new characters. Set
Config.Appearance.newCharacter = false.