EndCore Framework

Core overview

What the en-core resource owns, what it needs to run, how it loads and starts, and how the rest of EndCore builds on it.

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

AreaWhat it doesRead more
CharactersCreates, loads, saves and deletes characters and keeps their data validPlayer data, Player API
SurvivalA server tick for hunger, thirst, radiation, infection, immunity and temperatureSurvival system
MoneyCash and bank balances, with cash as a real inventory itemMoney and cash
JobsOne active job per character, job history and dutyJobs
GroupsPlayer-founded groups of survivors (EndCore has no gangs)Groups
XP and levelsTotal XP per character and a 50-level curveXP and levels
Content registryStores world content that admins place in gameContent registry
World and deathTurns off ambient population and tracks death and revive stateWorld and death
ConnectionsBan hook, whitelist, closed server and duplicate-license checksThis page
CompatibilityAnswers qb-core, qbx_core and es_extended exportsCompatibility bridges

Some things are deliberately left to other resources:

At a glance

ItemValue
Resource nameen-core
Version1.2.0
Depends onFXServer build 7290 or newer, OneSync, oxmysql, en-ui
Soft dependencyen-inventory
Providesqb-core, qbx_core, es_extended
Database tablesplayers, encore_groups, encore_group_members, encore_content
Config filesconfig/server.lua, config/shared.lua, config/client.lua, shared/jobs.lua, shared/groups.lua, shared/levels.lua
Convarsencore:disablebridge

Dependencies

DependencyWhy it is needed
/server:7290The minimum FXServer artifact
/onesyncOneSync must be enabled
oxmysqlPersistence. The server scripts load @oxmysql/lib/MySQL.lua.
en-uiRenders 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 receivedStarterItems flag 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) return nil, 0 or false.

The provides list

The manifest declares:

lua
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.

cfg
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]
Warning

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 global ENC), then client/main.lua, client/functions.lua, client/survival.lua, client/world.lua, then the QB, QBX and ESX client bridges.
  • Server: @oxmysql/lib/MySQL.lua, then server/main.lua, which must load first because it owns the require order and export registration. Then server/events.lua, server/commands.lua, server/world.lua, server/content.lua, then the QB, QBX and ESX server bridges.
  • Loaded with require only: 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/* and shared/*.

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.

Tip

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.

FileLoaded onContains
config/server.luaServerConnection rules, character slots, money rules, identifier formats, default metadata, survival tuning, logging, ban hook
config/shared.luaServer and clientDefault spawn, starter items, autosave interval, money types and starting balances
config/client.luaServer and clientRun and swim speed, infinite stamina, ambient population switches
shared/jobs.luaServer and clientJob definitions
shared/groups.luaServer and clientGroup size, cost, invite timeout, name and tag rules, ranks
shared/levels.luaServer and clientThe 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:

  1. The player has a license: identifier. If not, they are rejected with "No Rockstar license found...".
  2. bans.checkForBan(license) from config/server.lua, run safely in pcall. If it returns true, reason, the player is rejected with that reason, or with "You are banned from this server." when no reason is given.
  3. closedServer: if it is true and the player lacks the encore.bypassclosed ACE, they see "The server is currently closed to the public."
  4. whitelist: if it is true and the player lacks the encore.whitelist ACE, they see "You are not whitelisted on this server."

Two more checks run outside the connection screen:

  • When a player joins and server.checkDuplicateLicense is true, 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.luaDefaultWhat it does
closedServerfalseBlocks connections without encore.bypassclosed
whitelistfalseRequires encore.whitelist to connect
server.checkDuplicateLicensetrueDrops a second session with the same license
bans.checkForBan(license)returns false, nilHook for your own ban system. Return true, reason to reject.
ACEEffect
encore.whitelistAllowed to connect while the whitelist is on
encore.bypassclosedAllowed 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.
cfg
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.admin

A ban hook that reads your own table:

lua
-- 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.

lua
exports['en-core']:Log('death', ('%s was killed by a horde'):format(GetPlayerName(source)))

Saving

  • Every online player is saved every updateInterval from config/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.