en-core is the heart of EndCore. It keeps track of who is online, which character they are playing, how much money they carry, their job and group, their XP, and the survival stats that make the wasteland dangerous. Every other en-* resource builds on it.
It also ships the EndCore library (@en-core/lib/init.lua). Other resources load the library for callbacks, commands, notifications, the inventory and target interfaces and more. The library has its own section, starting at Library overview.
What en-core owns
| Area | What it does | Read more |
|---|---|---|
| Characters | Creates, loads, saves and deletes characters and keeps their data valid | Player data, Player API |
| Survival | A server tick for hunger, thirst, radiation, infection, immunity and temperature | Survival system |
| Money | Cash and bank balances, with cash as a real inventory item | Money and cash |
| Jobs | One active job per character, job history and duty | Jobs |
| Groups | Player-founded groups of survivors (EndCore has no gangs) | Groups |
| XP and levels | Total XP per character and a 50-level curve | XP and levels |
| Content registry | Stores world content that admins place in game | Content registry |
| World and death | Turns off ambient population and tracks death and revive state | World and death |
| Connections | Ban hook, whitelist, closed server and duplicate-license checks | This page |
| Compatibility | Answers qb-core, qbx_core and es_extended exports | Compatibility bridges |
Some things are deliberately left to other resources:
- Inventory and items: en-inventory.
- The death screen and respawning: en-respawn.
- Character selection and creation screens: en-multicharacter.
- The HUD: en-hud.
- Paychecks: en-core stores job pay values but has no paycheck loop.
At a glance
| Item | Value |
|---|---|
| Resource name | en-core |
| Version | 1.2.0 |
| Depends on | FXServer build 7290 or newer, OneSync, oxmysql, en-ui |
| Soft dependency | en-inventory |
| Provides | qb-core, qbx_core, es_extended |
| Database tables | players, encore_groups, encore_group_members, encore_content |
| Config files | config/server.lua, config/shared.lua, config/client.lua, shared/jobs.lua, shared/groups.lua, shared/levels.lua |
| Convars | encore:disablebridge |
Dependencies
| Dependency | Why it is needed |
|---|---|
/server:7290 | The minimum FXServer artifact |
/onesync | OneSync must be enabled |
oxmysql | Persistence. The server scripts load @oxmysql/lib/MySQL.lua. |
en-ui | Renders encore.notify, encore.progress, dialogs and prompts |
EndCore does not use ox_lib, ox_inventory or ox_target. It ships its own library and its own inventory and target resources.
Soft dependency on en-inventory
en-core talks to items only through the library's inventory interface, which en-inventory implements. en-core still starts without it, with these differences:
- Starter items are held back. The
receivedStarterItemsflag stays unset, so the kit is given on a later login once an inventory is running. - Cash falls back to a database balance instead of an item. See Money and cash.
- Inventory helpers on the player object (
GetItemByName,GetItemCount,AddItem,RemoveItem) returnnil,0orfalse.
The provides list
The manifest declares:
provides { 'qb-core', 'qbx_core', 'es_extended' }This only satisfies dependency checks in third-party manifests, so a script that declares dependency 'qb-core' will start. Routing the actual exports is done by the bridge files. See Compatibility bridges.
Start order
en-core must start after oxmysql and en-ui, and before every other EndCore resource.
set mysql_connection_string "<your-connection-string>"
sv_licenseKey "<your-license-key>"
sv_enforceGameBuild 3095
# Default Cfx resources
ensure chat
ensure mapmanager
ensure spawnmanager
ensure sessionmanager
ensure hardcap
# Must start before en-core
ensure oxmysql
# EndCore: en-ui first, then en-core, then the rest of the category
ensure en-ui
ensure en-core
ensure [encore]If the database setup fails on boot, the console prints Database setup failed: ... with a hint to check mysql_connection_string. The core will not function until that is fixed.
Load order inside the resource
You rarely need this, but it helps when you read the code or add files.
- Shared:
lib/init.lua. - Client:
modules/playerdata.lua(defines the client globalENC), thenclient/main.lua,client/functions.lua,client/survival.lua,client/world.lua, then the QB, QBX and ESX client bridges. - Server:
@oxmysql/lib/MySQL.lua, thenserver/main.lua, which must load first because it owns the require order and export registration. Thenserver/events.lua,server/commands.lua,server/world.lua,server/content.lua, then the QB, QBX and ESX server bridges. - Loaded with
requireonly:server/player.lua,server/survival.lua,server/groups.lua,server/cash.lua,server/xp.lua,server/storage/players.lua,modules/logger.lua,modules/utils.lua,config/*andshared/*.
Files sent to clients: lib/init.lua, lib/shared/*.lua, lib/client/*.lua, types.lua, config/shared.lua, config/client.lua, shared/jobs.lua, shared/groups.lua, shared/levels.lua, shared/vehicles.lua, shared/weapons.lua and data/nationalities.lua.
config/server.lua, the logger, the utils module and everything under server/ are never sent to clients. Keep webhook URLs and anything private in config/server.lua.
Configuration files
All config files are plain Lua tables returned from the file.
| File | Loaded on | Contains |
|---|---|---|
config/server.lua | Server | Connection rules, character slots, money rules, identifier formats, default metadata, survival tuning, logging, ban hook |
config/shared.lua | Server and client | Default spawn, starter items, autosave interval, money types and starting balances |
config/client.lua | Server and client | Run and swim speed, infinite stamina, ambient population switches |
shared/jobs.lua | Server and client | Job definitions |
shared/groups.lua | Server and client | Group size, cost, invite timeout, name and tag rules, ranks |
shared/levels.lua | Server and client | The XP curve |
The settings are documented on the page for each system. For a whole-server walkthrough, see Configuration.
Connections and access
When a player connects, en-core checks, in this order:
- The player has a
license:identifier. If not, they are rejected with "No Rockstar license found...". bans.checkForBan(license)fromconfig/server.lua, run safely inpcall. If it returnstrue, reason, the player is rejected with that reason, or with "You are banned from this server." when no reason is given.closedServer: if it istrueand the player lacks theencore.bypassclosedACE, they see "The server is currently closed to the public."whitelist: if it istrueand the player lacks theencore.whitelistACE, they see "You are not whitelisted on this server."
Two more checks run outside the connection screen:
- When a player joins and
server.checkDuplicateLicenseistrue, a second session with the same license is dropped with "You are already connected to this server." - When a player drops, their last known position is saved and they are removed from the online registry.
Key in config/server.lua | Default | What it does |
|---|---|---|
closedServer | false | Blocks connections without encore.bypassclosed |
whitelist | false | Requires encore.whitelist to connect |
server.checkDuplicateLicense | true | Drops a second session with the same license |
bans.checkForBan(license) | returns false, nil | Hook for your own ban system. Return true, reason to reject. |
| ACE | Effect |
|---|---|
encore.whitelist | Allowed to connect while the whitelist is on |
encore.bypassclosed | Allowed to connect while the server is closed |
command.<name> | Allowed to run a restricted command. All en-core admin commands are restricted to group.admin. |
add_ace group.admin command allow
add_ace group.admin encore.whitelist allow
add_ace group.admin encore.bypassclosed allow
add_principal identifier.fivem:<your-fivem-id> group.adminA ban hook that reads your own table:
-- config/server.lua
bans = {
checkForBan = function(license)
local row = MySQL.single.await('SELECT reason FROM my_bans WHERE license = ?', { license })
if row then
return true, row.reason
end
return false, nil
end,
},Logging
en-core prints to the console and can post to Discord webhooks, one per channel. Set them in config/server.lua under logging.webhook. An empty string means no webhook for that channel.
The channels are default, playermoney, playerinventory, death, joinleave, ooc, report and bans. en-core itself posts every money change to playermoney and every player death to death. Anything sent to a channel without a webhook goes to default.
logging.role[channel] holds a Discord role ID to mention in that channel's posts.
Other resources can log through the same channels with the Log export. It falls back to the default webhook when the channel has none.
exports['en-core']:Log('death', ('%s was killed by a horde'):format(GetPlayerName(source)))Saving
- Every online player is saved every
updateIntervalfromconfig/shared.lua(5 minutes by default), in one batched query. - Everyone is also saved when en-core stops and when txAdmin announces a server shutdown.
- A player's position is saved when they disconnect.
See Database for the tables.