Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SCFD Resource Template

A FiveM / Cfx.re Lua resource template used as a starting point for Scuffed Labs resources. It includes a startup validator, configuration loading, localization, shared logging, client/server utilities, and a modular bridge layer for third-party integrations.

This README is intentionally written for both human developers and LLMs. It documents the template structure, the expected loading order, the bridge contract, and the conventions that should be followed when generating or modifying code in this resource.

What this template provides

  • fxmanifest.lua with common FiveM metadata, dependencies, escrow ignore rules, and bridge module declarations.
  • init.lua startup validation for dependency checks, file checks, resource metadata checks, and optional version checking.
  • data/configuration.lua for runtime configuration.
  • locales/*.json for ox_lib localization.
  • shared/main.lua for shared logger helpers.
  • bridge/loader.lua for lazy-loading adapter modules.
  • client/utils.lua and server/utils.lua for reusable utility helpers.
  • client/main.lua, server/main.lua, and shared/main.lua as the main entry points for resource logic.
  • _INSTALL/ for installation snippets such as inventory items.

Directory layout

.
├── _INSTALL/              # Install snippets, SQL, item definitions, migration notes, etc.
├── bridge/                # Bridge loader, hooks, and third-party integration modules
│   ├── loader.lua         # Bridge resolver and lazy module loader
│   ├── hooks/             # Optional integration extension points
│   └── modules/           # Adapter implementations grouped by module/adapter/context
├── client/                # Client entry points, modules, and utilities
├── data/                  # Configuration and static data files
├── locales/               # Localization JSON files loaded by ox_lib
├── server/                # Server entry points, modules, and utilities
├── shared/                # Shared code loaded on both client and server
├── fxmanifest.lua         # Resource manifest and dependency declarations
├── init.lua               # Startup checks and global initialization
└── README.md              # Developer and LLM documentation

Loading order

The manifest currently loads shared scripts first:

shared_scripts {
    "@ox_lib/init.lua",
    "shared/logger.lua",
    "init.lua",
    "bridge/loader.lua",
}

Expected order:

  1. @ox_lib/init.lua provides lib, cache, locale, and ox_lib helpers.
  2. shared/logger.lua creates the shared Logger table.
  3. init.lua validates the resource, loads Config, runs version checks, and marks the resource as loaded.
  4. bridge/loader.lua creates Bridge and loads required/optional bridge modules.
  5. Client and server scripts then load their context-specific utilities and main files.

fxmanifest bridge declarations

Each resource declares which bridge modules it needs directly in fxmanifest.lua.

required_modules {
    "framework",
    "notify"
}

optional_modules {
    "inventory",
    "target",
    "fuel",
    "dispatch"
}

Use required_modules for integrations that must work for the resource to function correctly. If a required module cannot resolve or load, startup should fail loudly.

Use optional_modules for integrations that add compatibility or quality-of-life behavior but are not required for the resource to operate. Optional modules should soft-fail and return a noop object when unavailable.

Important syntax note

Use FiveM metadata function syntax, not assignment syntax:

required_modules {
    "framework",
    "notify"
}

Do not use:

required_modules = {
    "framework",
    "notify"
}

The loader reads manifest metadata using GetNumResourceMetadata and GetResourceMetadata, which expects metadata entries created by manifest function calls.

Bridge module system

The bridge allows resource code to call one consistent API while supporting multiple third-party resources behind the scenes.

Example usage:

Bridge.notify.send(source, {
    title = locale("common.title"),
    description = locale("common.success"),
    type = "success"
})

Bridge.inventory.addItem(source, "water", 1)

The bridge loader resolves three separate concerns:

  1. What the resource needs: declared in fxmanifest.lua through required_modules and optional_modules.
  2. Which adapter should be used: selected by Config or auto-detected from started resources.
  3. Where the adapter file lives: built from bridge/modules/<module>/<adapter>/<context>.lua.

Bridge module definitions

moduleDefs should describe how a module is configured and loaded. It should not decide whether the current resource requires that module.

Good:

framework = {
    configKey = "framework",
    client = false,
    fallback = "standalone"
}

Avoid putting requirement state here:

framework = {
    configKey = "framework",
    required = true, -- avoid this
    client = false,
    fallback = "standalone"
}

Requirement state belongs in fxmanifest.lua so every resource can declare only the integrations it actually uses.

Adapter resolution

Adapter selection is controlled by data/configuration.lua.

Example:

config.framework = "auto"
config.inventory = "auto"
config.interaction = "auto"
config.notification = "auto"
config.progressbar = "auto"
config.contextMenu = "auto"

Common values:

  • "auto": detect the best available adapter from started resources.
  • "none": disable the integration.
  • "custom": load the custom adapter path.
  • adapter name such as "ox", "qbx", "qb", "esx", "lation", etc.

When auto is selected, autoDetectMap checks known resource names with GetResourceState and maps them to adapter names.

Adapter file convention

Bridge modules should follow this path convention:

bridge/modules/<module>/<adapter>/<context>.lua

Examples:

bridge/modules/framework/qbx/server.lua
bridge/modules/framework/qbx/client.lua
bridge/modules/inventory/ox/server.lua
bridge/modules/notify/ox/client.lua

Each adapter should return a table of functions unless it is intentionally a single callable function.

Preferred:

local Notify = {}

function Notify.send(source, data)
    -- implementation
end

return Notify

For client-only integrations, set client = true in moduleDefs. The loader should skip those modules on the server context.

Noop modules

When an optional module is unavailable, the bridge returns a noop object. This lets optional integrations fail safely without forcing every call site to check for existence.

Example:

Bridge.dispatch.createAlert(data)

If dispatch is optional and unavailable, the noop object prevents the call from crashing.

The noop should remain table/callable based instead of a plain function() end because many modules are used as method tables:

Bridge.dispatch.createAlert(...)
Bridge.target.addLocalEntity(...)

A plain function would only support:

Bridge.dispatch(...)

and would break method-style calls.

Startup validation

init.lua currently performs startup checks such as:

  • ox_lib exists.
  • ox_lib is at least the expected version.
  • Config loads successfully.
  • resource name and manifest metadata match expected values.
  • required files exist.
  • version check runs on the server when enabled.

This protects paid or distributed resources from partial installs, renamed folders, missing files, and outdated dependencies.

When creating a new resource from this template, update:

local resourceCfg = {
    expectedName = "your_resource_name",
    expectedAuthor = "Scuffed Labs",
    expectedDescription = " by Scuffed Labs",
    fileCheck = {
        "locales/en.json",
        "LICENSE.md",
        "data/configuration.lua"
    },
}

Also update the manifest metadata:

name "your_resource_name"
description "Your resource description by Scuffed Labs"
version "v1.0.0"

Configuration conventions

Use data/configuration.lua for user-editable settings. Keep it simple and stable.

Recommended sections:

config.debug = false
config.enableVersionCheck = true

config.framework = "auto"
config.inventory = "auto"
config.interaction = "auto"
config.notification = "auto"
config.progressbar = "auto"
config.contextMenu = "auto"

config.commands = {}
config.permissions = {}
config.locations = {}
config.rewards = {}

Avoid putting complex runtime logic in configuration files. Configuration should describe behavior, not execute behavior.

Localization conventions

Localization files live in locales/*.json and are loaded by ox_lib.

Example locales/en.json:

{
    "common.title": "Scuffed Labs",
    "common.success": "Success",
    "common.error": "Something went wrong"
}

Use locale keys instead of hardcoded strings in notifications, menus, context menus, and prompts:

locale("common.title")

Recommended key naming:

common.*
error.*
success.*
menu.*
interaction.*
command.*

Hooks

Hooks should be used for integration-specific extension points that do not belong in core resource logic.

Example uses:

  • Add custom dispatch behavior.
  • Add framework-specific player metadata behavior.
  • Add inventory-specific item metadata formatting.
  • Allow server owners to customize reward logic without editing core files.

Suggested hook shape:

local Hooks = {}

function Hooks.onRewardGiven(source, reward)
    return reward
end

function Hooks.canInteract(source, data)
    return true
end

return Hooks

Core code can call hooks safely:

if Hooks.canInteract and not Hooks.canInteract(source, data) then
    return
end

Utility conventions

Utilities should be reusable helpers that are not specific to a single feature.

Good utility examples:

  • Utils.debugPrint
  • Utils.formatNumber
  • Utils.trimString
  • Utils.notifySuccess
  • Utils.spawnVehicle
  • Utils.createBlip

Avoid placing one-off business logic in Utils. Feature-specific logic should live in client/modules/* or server/modules/*.

Recommended features to add to this template

1. Bootstrap-only manifest loading

Load fewer files directly in fxmanifest.lua and let guarded boot files load internal modules with lib.load. This reduces repeated LoadedResource guards and makes startup order easier to reason about.

2. Shared constants file

Add:

shared/const.lua

Use it for immutable values such as event names, state bag keys, command names, default distances, and permission names.

3. Event name helper

Prevent event-name collisions with a small helper:

local resource = GetCurrentResourceName()

Events = {
    server = function(name)
        return ("%s:server:%s"):format(resource, name)
    end,
    client = function(name)
        return ("%s:client:%s"):format(resource, name)
    end
}

Then use:

RegisterNetEvent(Events.client("sync"), function(data)
    -- ...
end)

4. Callback wrapper

Standardize ox_lib callbacks:

Callbacks = {}

function Callbacks.register(name, cb)
    lib.callback.register(("%s:%s"):format(cache.resource, name), cb)
end

function Callbacks.await(name, source, ...)
    return lib.callback.await(("%s:%s"):format(cache.resource, name), source, ...)
end

5. Permission helper

Add one place to check framework, ACE, and custom permissions:

function Utils.hasPermission(source, permission)
    if IsPlayerAceAllowed(source, permission) then return true end
    return Bridge.framework.hasPermission(source, permission)
end

6. Input validation helpers

Add server-side validation for common unsafe inputs:

  • source exists
  • player is near coordinates
  • item name is valid
  • amount is positive
  • entity exists
  • player has permission
  • cooldown has not expired

This is especially useful for LLMs because it gives a standard pattern for safe event handling.

7. Cooldown/rate-limit helper

Add a generic cooldown utility for server events:

local cooldowns = {}

function Utils.checkCooldown(source, key, seconds)
    local id = ("%s:%s"):format(source, key)
    local now = os.time()

    if cooldowns[id] and cooldowns[id] > now then
        return false, cooldowns[id] - now
    end

    cooldowns[id] = now + seconds
    return true, 0
end

8. Cleanup registry

Track entities, blips, zones, targets, and handlers so they can be removed on resource stop.

Cleanup = {
    handlers = {}
}

function Cleanup.add(fn)
    Cleanup.handlers[#Cleanup.handlers + 1] = fn
end

AddEventHandler("onResourceStop", function(resourceName)
    if resourceName ~= cache.resource then return end

    for i = #Cleanup.handlers, 1, -1 do
        pcall(Cleanup.handlers[i])
    end
end)

9. Debug commands

When Config.debug is true, register useful commands such as:

  • print current bridge adapters
  • reload config where safe
  • inspect player framework data
  • test notifications
  • test dispatch
  • test inventory add/remove in development

Keep debug commands disabled in production.

10. Versioned config migration notes

Add a data/config_version.lua or Config.version value so support staff can tell whether a user has updated their config after a resource update.

config.version = 1

11. LLM editing rules

When an LLM modifies this resource, it should follow these rules:

  • Do not bypass init.lua validation.
  • Do not hardcode framework-specific logic outside the bridge unless unavoidable.
  • Prefer Bridge.* calls over direct third-party exports.
  • Keep server authority for money, items, rewards, and permissions.
  • Validate all client-triggered server events.
  • Use locale keys for user-facing text.
  • Use Config.debug for diagnostic output.
  • Put feature code in client/modules or server/modules, not in utility files.
  • Add new third-party compatibility through bridge/modules, not by scattering checks throughout feature code.
  • Keep moduleDefs as a loader registry only; use fxmanifest.lua to declare required and optional modules.

Adding a new bridge module

  1. Add a module definition to moduleDefs in bridge/loader.lua.
  2. Add auto-detection entries to autoDetectMap if applicable.
  3. Add config keys to data/configuration.lua.
  4. Create adapter files under bridge/modules/<module>/<adapter>/<context>.lua.
  5. Declare the module in required_modules or optional_modules in fxmanifest.lua.
  6. Use the module through Bridge.<module> in resource code.

Example module definition:

banking = {
    configKey = "banking",
    client = false,
    fallback = "none"
}

Example config:

config.banking = "auto"

Example manifest declaration:

optional_modules {
    "banking"
}

Common mistakes

Using assignment syntax for manifest metadata

Wrong:

required_modules = {
    "framework"
}

Right:

required_modules {
    "framework"
}

Making every module globally required

Do not mark modules as required in moduleDefs. Requirement status should be per resource.

Directly calling third-party exports everywhere

Avoid this:

exports.ox_inventory:AddItem(source, item, count)

Prefer this:

Bridge.inventory.addItem(source, item, count)

Trusting client events

Never trust client-provided rewards, prices, coordinates, or permissions without server validation.

Putting feature-specific code in utilities

Utilities should be generic. Feature logic belongs in modules.

Minimum dependency expectations

The template expects:

  • FiveM artifact compatible with the manifest constraints.
  • OneSync enabled.
  • ox_lib installed and started before this resource.
  • oxmysql installed and started before this resource when server database access is needed.

Manifest dependencies currently include:

dependencies {
    "/gameBuild:3095",
    "/server:26389",
    "/onesync",
    "ox_lib",
    "oxmysql"
}

Remove oxmysql only if the resource has no database usage.

Recommended development workflow

  1. Rename the resource folder and update resourceCfg.expectedName.
  2. Update manifest metadata.
  3. Configure data/configuration.lua.
  4. Declare bridge modules in fxmanifest.lua.
  5. Add adapter files for missing integrations.
  6. Keep new feature logic in client/modules and server/modules.
  7. Add localization keys before shipping.
  8. Test with Config.debug = true.
  9. Test startup failure cases by disabling dependencies.
  10. Set Config.debug = false before production release.

License

See LICENSE.md.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages