EndCore Framework

Library overview

What the EndCore library gives your resource, how to import it, how its resource-aware require works, and the rules every module follows.

The EndCore library is the toolkit that ships inside en-core. Every en-* resource is built on it, and your own resources should be too. It gives you server and client callbacks, commands, rebindable keybinds, notifications and progress bars, asset loading, world helpers, and one interface each for inventory, targeting, minigames, the radial menu, skills and parties.

Because resources talk to those systems through the library instead of calling each other directly, you can run a server without one of them, start resources in any order, or swap in a replacement. When a backing resource is missing, the call returns a safe "nothing happened" value instead of throwing.

Importing the library

Add @en-core/lib/init.lua as the first shared script in your fxmanifest.lua:

lua
fx_version 'cerulean'
game 'gta5'
lua54 'yes'

shared_scripts {
    '@en-core/lib/init.lua',   -- must come before any script that uses `encore`
    'config/shared.lua',
}

client_scripts { 'client/main.lua' }
server_scripts { 'server/main.lua' }

dependencies {
    'en-core',
    'en-ui',      -- needed if you call encore.notify / progress / inputDialog / prompts
}

This is the same pattern every shipped en-* resource uses. en-core serves the library files to clients itself, so you don't list them in your own files {}.

What you get

Loading init.lua defines one global table, encore:

FieldValue
encore.__libLoadedtrue. The file returns early if the library is already loaded, so it only loads once per resource
encore.context'server' or 'client'
encore.resourceThe name of the resource that imported the library

The modules load in this order on both sides: util, callback, command, ui, inventory, party, skills, content. The client also loads streaming, world, keybind, target, minigame and radial.

ModuleSideReference
Logging, strings, maths, tables, waitFor, export bridgingbothUtilities
encore.callbackbothCallbacks
encore.addCommandbothRegistering commands
encore.addKeybindclientKeybinds
encore.notify, progress, inputDialog, prompts, encore.dialogboth / clientUI services
encore.requestModel and friends, nearby entities, vehicles, textclientStreaming and world helpers
encore.contentbothContent API
encore.inventorybothInventory interface
encore.targetclientTargeting interface
encore.minigame, encore.radialclientMinigames and radial menu
encore.skills, encore.partybothSkills and party interfaces

The library runs in your resource

init.lua runs inside the Lua state of the resource that imports it, not inside en-core. So:

  • Callbacks, commands, keybinds, target options and radial items you register belong to your resource, and are cleaned up when it stops.
  • GetCurrentResourceName() and encore.resource return your resource's name.
  • Settings such as encore.callback.timeout apply only to your resource.

Resource-aware require

init.lua replaces the global require with a loader that reads Lua files out of resources.

CallLoads
require 'config.server'<this resource>/config/server.lua
require 'modules.utils'<this resource>/modules/utils.lua, falling back to modules/utils/init.lua
require '@en-core.shared.jobs'en-core/shared/jobs.lua (the @resource. prefix reads from another resource)

How it behaves:

  • Dots become slashes. The loader tries path.lua first, then path/init.lua.
  • Each module runs once per resource and the result is cached under the exact name string. A module that returns nothing is cached as true.
  • A missing file raises module "<name>" not found (looked for <resource>/<path>.lua).
  • A circular require raises circular require: "<name>" is already being loaded.
  • Compile and runtime errors are re-raised, and the cache entry is cleared so the next call can try again.
  • On the server, files are read straight off disk, so server-only modules need no manifest entry.
  • On the client, the file must be listed in your resource's files {} so the client has downloaded it.
Warning

Don't also list a required file under client_scripts, server_scripts or shared_scripts. It would run twice: once as a script and once through require.

lua
-- fxmanifest.lua
files { 'config/client.lua' }

-- server/main.lua
local Config = require 'config.server'          -- read from disk, no manifest entry needed
local jobs   = require '@en-core.shared.jobs'   -- a file from another resource

-- client/main.lua
local ClientConfig = require 'config.client'    -- must be in files {}

Nothing throws at runtime

The library follows one design rule: a runtime failure never takes down the thread that called it. Callers check results instead.

  • A callback that times out returns nothing.
  • A model that doesn't exist or won't load returns nil.
  • A missing backing resource logs a warning once and returns a fallback value.
  • An export that errors is caught, logged once per export name, and turned into the fallback value.

Programmer mistakes still raise an error so you find them straight away: require with a bad or missing module name, encore.addKeybind without a name, and encore.radial.addItem without a string id.

Interfaces instead of dependencies

EndCore resources never call inventory, targeting, minigame, radial, skills or party resources directly. They call encore.*, and the library forwards the call to a fixed resource name:

InterfaceLibrary moduleResource it callsWithout it
Inventoryencore.inventoryen-inventoryItem operations report failure (false, 0, nil)
Targetingencore.targeten-targetNothing is shown; registrations are remembered
Minigamesencore.minigameen-minigamesA cancellable progress bar stands in
Radial menuencore.radialen-radialmenuNothing is shown; items are remembered
Skillsencore.skillsen-skillsLevel 1, bonus 0, no perks, no XP recorded
Partyencore.partyen-partyEvery player is a party of one

Every forwarded call first checks GetResourceState(<name>) == 'started'. The export lookup and the call both run inside pcall. If you want to replace one of these resources, see the contracts on Inventory interface and Targeting interface.

UI calls work the same way against en-ui. See UI services.

Security basics

  • On the server, source is the only value you can trust. Anything a client sends you through a callback, net event, NUI callback or serverEvent target option can be forged. Check its type, range, ownership and distance before acting on it.
  • Content placed in game, item labels, character names and chat are typed by people. Validate them in Lua and display them in NUI as text only. See Design system.
  • Restrict admin commands with restricted on the server. Client commands can never be restricted.