Documentation
TebexDiscordYouTubeGitHub
  • Introduction
  • FIVEM FREE RESOURCES
    • BDTK
      • Guides
      • API
        • Bridges
          • Framework
          • Notifications
          • DrawText UI
        • Modules
          • Appearance
          • Buckets
          • Callbacks
          • Commands
          • Cooldowns
          • Debugging
          • Entities
          • Environment
          • Geometry
    • BDSC
      • Guides
        • What Are Plugins?
        • Making Your First Plugin
      • API
        • Player
        • Manager
        • Utility
    • boii_utils
      • Installation
      • Configuration
      • Modules
      • API
        • Callbacks
        • Characters
        • Commands
        • Cooldowns
        • Debugging
        • Entities
        • Environment
        • Framework Bridge
        • Geometry
        • Items
        • Keys
        • Licences
        • Maths
        • Methods
        • Player
        • Requests
        • Strings
        • Tables
        • Timestamps
        • UI Bridges
        • Vehicles
        • Version
        • XP
        • UI Elements
  • FIVEM PAID RESOURCES
    • Page 2
Powered by GitBook
On this page
  • What Is a Plugin?
  • Why Use Plugins?
  • Plugin Structure
  • How To
  • Step 1: Write the Extension Class
  • Step 2: Register the Plugin
  • Step 3: Add Server + Client Logic (optional)
  • Step 4: Add UI (Optional)
  • Step 5: Add Optional Utilities
  • External Plugin Example
  1. FIVEM FREE RESOURCES
  2. BDSC
  3. Guides

Making Your First Plugin

BDSC’s plugin system was designed for extending the player object in a modular, clean, and lifecycle-safe way. Whether internal to your core or part of a standalone release, plugins are the best way to inject logic, state, UI, and methods into your players.

This guide will walk you through building your first full plugin using the provided example plugin as a template.


What Is a Plugin?

In BDSC, a plugin is more than just a function hook. It’s a full-featured extension module that can:

  • Attach data and methods to players

  • Handle lifecycle events (on_load, on_save, on_unload)

  • Provide its own shared/client/server logic

  • Register its own UI components

  • Operate internally or externally


Why Use Plugins?

This plugin system ensures your logic is:

  • Modular – Everything is self-contained, easy to read, and easy to reuse.

  • Composable – Multiple plugins can attach to the same player cleanly.

  • Integrated – Plugins run automatically at the correct points in the player lifecycle.

  • Portable – External plugins can be dropped into any resource or shared as paid modules.

Use internal plugins for logic that belongs in your core. Use external plugins for marketplace drops, private releases, or premium add-ons.


Plugin Structure

Plugins live in the plugins/ folder of your BDSC-based resource. Their internal structure is flexible and modular — you can add only the parts you need.

plugins/
└── example/
    ├── init.lua                  # Required if extending player logic
    ├── player/
    │   └── class.lua             # Player extension
    ├── modules/
    │   └── example.lua           # Optional internal utils
    ├── client/
    │   └── example.lua           # Optional client logic
    ├── server/
    │   └── example.lua           # Optional server logic
    ├── shared/
    │   └── example.lua           # Optional shared constants
    └── ui/
        ├── style.css             # Optional UI styling
        └── logic.js              # Optional UI behavior

How To

Step 1: Write the Extension Class

plugins/example/player/class.lua

This file defines the plugin’s logic and lifecycle hooks.

local Example = {}
Example.__index = Example

--- Creates the extension and attaches it to a player.
--- This runs automatically when the player is created.
--- @param player table: The player object (provided by BDSC).
--- @return table: Your extension instance.
function Example.new(player)
    local self = setmetatable({ player = player }, Example)
    self:on_load() -- Run your setup logic immediately
    return self
end

--- Called after the player finishes loading.
--- You can add data or register new methods on the player object here.
function Example:on_load()
    -- Create a new synced data category called "example"
    self.player:add_data("example", {
        enabled = true,
        value = 0
    }, true) -- true|false: true = data will be replicated to client, false = server only

    -- Add a method `example_add(amount)` to increase `value`
    self.player:add_method("example_add", function(p, amount)
        local data = p:get_data("example")
        data.value = data.value + (amount or 1)
        p:sync_data("example")
    end)

    -- Add a method `example_toggle()` to flip the enabled flag
    self.player:add_method("example_toggle", function(p)
        local data = p:get_data("example")
        data.enabled = not data.enabled
        p:sync_data("example")
    end)
end

--- Called when the player is saved.
--- Add persistence logic here if needed (e.g. writing to the database).
function Example:on_save()
    local data = self.player:get_data("example") or {}
    bdsc.log("debug", "example", "example_save_player", json.encode(data))
end

--- Called when the player unloads.
--- Clean up or log anything you want here.
function Example:on_unload()
    bdsc.log("debug", "example", "example_unload_player", self.player.source)
end

return Example

Step 2: Register the Plugin

plugins/example/init.lua

if not bdsc.is_server then return end

local manager = utils:get("core.player.manager", { bdsc = bdsc })
local class = utils:get("plugins.example.player.class", { bdsc = bdsc })

manager.register_hook("example", function(player)
    local extension = class.new(player)
    player:add_extension("example", extension)
end)

Step 3: Add Server + Client Logic (optional)

server/example.lua

--- Fired internally by BDSC when a player is created.
AddEventHandler("bdsc:sv:player_joined", function(player)
    local player = bdsc.get_player(source)
    if not player then return end

    local data = player:get_data("example")
    if data then
        print(("[example][server] Player %s joined with example data: enabled = %s, value = %s"):format(source, tostring(data.enabled), data.value))
    end
end)

client/example.lua

--- Fired internally by BDSC when a player is created.
AddEventHandler("bdsc:cl:player_joined", function()
    local data = bdsc.p_data.example
    if data then
        print(("[example][client] Player data loaded. Enabled = %s, Value = %s"):format(tostring(data.enabled), data.value))
    end
end)

Step 4: Add UI (Optional)

If your plugin includes frontend files, put them in the ui/ folder and register them in the global manifest:

core/ui/manifest.json

[
    {
        "name": "example",
        "css": ["/plugins/example/ui/style.css"],
        "js": ["/plugins/example/ui/logic.js"]
    }
]

BDSC handles injection automatically through its unified UI loader.


Step 5: Add Optional Utilities

modules/example.lua

local m = {}

function m.increment(val, amount)
    return (val or 0) + (amount or 1)
end

return m

shared/example.lua

local example = {}

function example.default_data()
    return {
        enabled = true,
        value = 0
    }
end

return example

External Plugin Example

Want to register a plugin from outside the core? Use the same class file pattern and hook in via exports:

local bdsc = exports.bdsc:import()
local manager = exports.bdul:get("core.player.manager", { bdsc = bdsc })
local class = exports.bdul:get("yourplugin.player.class", { bdsc = bdsc })

manager.register_hook("example", function(player)
    local extension = class.new(player)
    player:add_extension("example", extension)
end)

Last updated 12 hours ago