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
  • How Do They Work?
  • Internal Plugins
  • Structure Example
  • Plugin UI Support
  • Extending The Player
  • External Plugins
  • Why This Is Good
  1. FIVEM FREE RESOURCES
  2. BDSC
  3. Guides

What Are Plugins?

Plugins in BDSC are a clean way to extend the player object with custom logic either:

  • Internally, via the /plugins/ folder inside the core resource

  • Externally, from any other resource using the BDSC API

They hook into the core’s player lifecycle and attach your logic to each player object as they’re created.


How Do They Work?

When a player connects, BDSC creates a player object containing their boii_utils user account. It then runs the internal load() method on that object.

During this phase, all registered plugin extensions are executed. Each plugin receives the player object and attaches its logic using add_extension().

This allows your plugin to inject:

  • New synced or server-only data

  • Custom methods

  • Lifecycle hooks (on_load, on_save, on_unload)

Your plugin doesn’t override or wrap the core player. It becomes part of it.


Internal Plugins

Internal plugins live inside the /plugins/ folder of your BDSC-based resource. These can include their own modules, logic, and UI components, effectively acting like mini-resources.

Note: Files within plugin folders are lazy loaded for example: "plugins/**/server/*.lua" they do not account for file load order, consider this if its a requirement for your plugin.

Structure Example

plugins/
└── example/
    ├── init.lua              # Optional: Required if extending player logic (shared)
    ├── player/
    │   └── class.lua         # Optional: Player extension logic
    ├── ui/                   # Optional: Hooks into BDSC UI manifest
    ├── modules/
    │   └── example.lua       # Optional: Plugin-specific modules
    ├── client/
    │   └── example.lua       # Optional: Client-side logic
    ├── server/
    │   └── example.lua       # Optional: Server-side logic
    └── shared/
        └── example.lua       # Optional: Shared logic

Plugin UI Support

FiveM only allows a single ui_page per resource, which makes it impossible for each plugin to have its own independent UI page. To solve this, BDSC includes a custom UI loader system that acts as a unified entry point for all plugin-based UIs.

How it works: Each plugin can include its own UI files inside a ui/ folder. Instead of defining a separate ui_page, plugins just register their UI assets in the shared core/ui/manifest.json file. BDSC’s loader will then handle injecting all CSS/JS files as needed.

How to use it:

  1. Place your plugin’s UI files under plugins/myplugin/ui/.

  2. Add your CSS/JS paths to core/ui/manifest.json.

Example manifest entry:

[
    {
        "name": "your_plugin",
        "css": [
            "/plugins/your_plugin/your_file.css",
        ],
        "js": [
            "/plugins/your_plugin/your_file.js"
        ]
    }
]

This allows you to modularize UI logic per plugin, while still working within FiveM’s UI limitations.


Extending The Player

plugins/example/player/class.lua

local Example = {}
Example.__index = Example

function Example.new(player)
    local self = setmetatable({ player = player }, Example)
    self:on_load()
    return self
end

function Example:on_load()
    self.player:add_data("example", {
        enabled = true,
        value = 0
    }, true)

    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)

    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

function Example:on_save()
    local data = self.player:get_data("example") or {}
    bdsc.log("debug", "example", "example_save_player", json.encode(data))
end

function Example:on_unload()
    bdsc.log("debug", "example", "example_unload_player", self.player.source)
end

return Example

plugins/example/init.lua

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

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

External Plugins

External plugins live in any other resource. They use the exported bdsc.get() function to hook into the core lifecycle.

Use this pattern for marketplace plugins or modular systems that must stay outside the core.

Minimal Example

Create your class file the same as you would for an internal plugin:

local Example = {}
Example.__index = Example

function Example.new(player)
    local self = setmetatable({ player = player }, Example)
    self:on_load()
    return self
end

function Example:on_load()
    self.player:add_data("example", {
        enabled = true,
        value = 0
    }, true)
    
    print("player loaded externally")
end

function Example:on_save()
    local data = self.player:get_data("example") or {}
    print("player saved externally")
end

function Example:on_unload()
    print("player unloaded externally")
end

return Example

Somewhere in your server side code add the following, ensuring to path to your class file correctly.

local bdsc = exports.bdsc:import()
local manager = exports.bdul:get("core.player.manager", { bdsc = bdsc })
local class = exports.bdul:get("path.to.your.class.file", { bdsc = bdsc })

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

Why This Is Good

This plugin system makes your logic:

  • Modular – Each plugin is fully self-contained, with its own UI, data, methods, and lifecycle logic.

  • Portable – External plugins can live in any resource and still integrate cleanly with the core.

  • Composable – Multiple plugins can stack together without interfering with each other.

  • Integrated – All plugins run at the right time during the player lifecycle (on_load, on_save, on_unload).

Use internal plugins for logic that belongs in your core repo. Use external plugins for marketplace drops, private releases, or premium addons.

This keeps your project clean, scalable, and developer-friendly.

Last updated 10 hours ago