Core framework exports and component proxy system API reference
The mythic-base resource is the foundation of Mythic Framework, providing the component proxy system, logging, database access, and essential utilities that all other resources depend on.
Component Naming: Use PascalCase for component names (e.g., ‘Inventory’, ‘VehicleManager’, ‘MyFeature’)
Protected Components: Attempting to override a protected component will fail with an error. Use ExtendComponent to add methods to protected components.
-- Fetch a componentlocal Inventory = exports['mythic-base']:FetchComponent('Inventory')if Inventory then -- Use the component local items = Inventory:Get(characterId)else print('[ERROR] Inventory component not found!')end
Using COMPONENTS Global:
Copy
-- COMPONENTS is a global table populated by FetchComponent-- More convenient than repeated exports callsAddEventHandler('Core:Shared:Ready', function() -- Components are now available in COMPONENTS global COMPONENTS.Logger:Info('MyResource', 'Starting up') local character = COMPONENTS.Characters:GetCharacter(source) local inventory = COMPONENTS.Inventory:Get(character.SID)end)
Fetch Multiple Components:
Copy
-- Fetch multiple components at oncelocal Logger = exports['mythic-base']:FetchComponent('Logger')local Database = exports['mythic-base']:FetchComponent('Database')local Characters = exports['mythic-base']:FetchComponent('Characters')-- Or use COMPONENTS globallocal Logger = COMPONENTS.Loggerlocal Database = COMPONENTS.Databaselocal Characters = COMPONENTS.Characters
COMPONENTS Global: After Core:Shared:Ready event, all components are available in the COMPONENTS global table. Use COMPONENTS.ComponentName instead of repeatedly calling FetchComponent.
Timing: Don’t fetch components at the top level of your script! They may not be registered yet. Wait for Core:Shared:Ready or use RequestDependencies.
-- mythic-inventory-extras/server/component.lua-- Extend Inventory component with new methodsexports['mythic-base']:ExtendComponent('Inventory', { -- Add weight calculation GetWeight = function(self, characterId) local inventory = self:Get(characterId) local totalWeight = 0 for _, item in pairs(inventory.items) do totalWeight = totalWeight + (item.weight * item.count) end return totalWeight end, -- Add bulk operation AddMultipleItems = function(self, characterId, items) for _, itemData in ipairs(items) do self:AddItem(characterId, itemData.item, itemData.count) end end, -- Add search functionality FindItem = function(self, characterId, itemName) local inventory = self:Get(characterId) for slot, item in pairs(inventory.items) do if item.name == itemName then return slot, item end end return nil end})
Override Method While Preserving Original:
Copy
-- Save reference to original methodlocal originalAddItem = COMPONENTS.Inventory.AddItem-- Extend with wrapper that adds loggingexports['mythic-base']:ExtendComponent('Inventory', { AddItem = function(self, characterId, item, count, metadata) -- Log before COMPONENTS.Logger:Debug('Inventory', 'Adding item', { character = characterId, item = item, count = count }) -- Call original method local success = originalAddItem(self, characterId, item, count, metadata) -- Log after if success then TriggerEvent('inventory:itemAdded', characterId, item, count) end return success end})
Add Integration Methods:
Copy
-- mythic-crafting/server/component.lua-- Extend Inventory with crafting-specific methodsexports['mythic-base']:ExtendComponent('Inventory', { CanCraft = function(self, characterId, recipe) for _, ingredient in ipairs(recipe.ingredients) do if not self:HasItem(characterId, ingredient.item, ingredient.count) then return false end end return true end, ConsumeRecipe = function(self, characterId, recipe) for _, ingredient in ipairs(recipe.ingredients) do self:RemoveItem(characterId, ingredient.item, ingredient.count) end end})
-- mythic-shops/server/component.luaexports['mythic-base']:RequestDependencies('Shops', { 'Inventory', 'Finance', 'Logger'}, function(errors) if #errors > 0 then -- Dependencies failed to load print('[ERROR] Shops failed to load dependencies:') for _, err in ipairs(errors) do print(' - ' .. err) end return end -- All dependencies loaded, safe to register component exports['mythic-base']:RegisterComponent('Shops', { Purchase = function(self, player, item, price) -- Can safely use dependencies here if COMPONENTS.Finance:Charge(player, price) then COMPONENTS.Inventory:AddItem(player, item, 1) COMPONENTS.Logger:Info('Shops', 'Purchase complete') return true end return false end })end)
Multiple Dependencies:
Copy
exports['mythic-base']:RequestDependencies('VehicleShop', { 'Database', 'Logger', 'Vehicles', 'Finance', 'Inventory', 'Characters'}, function(errors) if #errors > 0 then error('[VehicleShop] Missing dependencies: ' .. json.encode(errors)) return end -- All dependencies available exports['mythic-base']:RegisterComponent('VehicleShop', { -- Component implementation })end)
Graceful Degradation:
Copy
exports['mythic-base']:RequestDependencies('MyFeature', { 'Inventory', -- Required 'Phone' -- Optional enhancement}, function(errors) local hasPhone = true -- Check which dependencies failed for _, err in ipairs(errors) do if string.find(err, 'Phone') then print('[WARNING] Phone not available, notifications disabled') hasPhone = false else -- Other dependency failed, can't continue print('[ERROR]', err) return end end exports['mythic-base']:RegisterComponent('MyFeature', { Notify = function(self, player, message) if hasPhone then -- Use phone notifications COMPONENTS.Phone:SendNotification(player, message) else -- Fallback to chat TriggerClientEvent('chat:addMessage', player, { args = { message } }) end end })end)
Always Use RequestDependencies: Even if dependencies are in fxmanifest.lua, use RequestDependencies to ensure components are registered before you use them.
Timeout: Dependencies have a timeout (default 30 seconds). If a dependency doesn’t load in time, it will error in the callback.
Load Order: Resources still need proper load order in resources.cfg. RequestDependencies handles component registration timing, not resource loading.
-- mythic-hud/client/main.luaAddEventHandler('Core:Shared:Ready', function() -- Fetch client components local Logger = exports['mythic-base']:FetchComponent('Logger') local Callbacks = exports['mythic-base']:FetchComponent('Callback') Logger:Info('HUD', 'Initializing HUD') -- Register callbacks Callbacks:RegisterCallback('hud:getData', function(data, cb) cb({ status = 'ok' }) end) -- Start HUD SendNUIMessage({ type = 'INIT' })end)
Server-Side Initialization:
Copy
-- mythic-jobs/server/main.luaAddEventHandler('Core:Shared:Ready', function() -- Components now available in COMPONENTS global COMPONENTS.Logger:Info('Jobs', 'Loading jobs system') -- Load jobs from database local jobs = COMPONENTS.Database:find('jobs', {}) COMPONENTS.Logger:Info('Jobs', 'Loaded ' .. #jobs .. ' jobs') -- Set up event handlers RegisterNetEvent('jobs:server:clockIn', function(jobId) local char = COMPONENTS.Characters:GetCharacter(source) COMPONENTS.Jobs:ClockIn(char.SID, jobId) end)end)
-- Assume dependencies existexports['mythic-base']:RegisterComponent('MyFeature', { DoThing = function(self) COMPONENTS.Inventory:AddItem(...) -- May be nil! end})
✅ Good:
Copy
exports['mythic-base']:RequestDependencies('MyFeature', { 'Inventory'}, function(errors) if #errors == 0 then exports['mythic-base']:RegisterComponent('MyFeature', { DoThing = function(self) COMPONENTS.Inventory:AddItem(...) -- Guaranteed to exist end }) endend)
Document Your Components
Copy
exports['mythic-base']:RegisterComponent('MyFeature', { --- Add an item to player's inventory ---@param player number Player server ID ---@param item string Item name ---@param count number Quantity ---@return boolean success GiveItem = function(self, player, item, count) -- Implementation end})
Use Protected Wisely
When to use _protected = true:
Core framework components
Components with complex internal state
Components where overriding would break other resources