Understanding component-based architecture in Mythic Framework
Mythic Framework uses a component-based architecture where functionality is encapsulated into reusable, composable components. This approach promotes modularity, reusability, and maintainability.
Use RequestDependencies to ensure required components are loaded:
Copy
-- mythic-shop/server/component.luaexports['mythic-base']:RequestDependencies('Shop', { 'Inventory', 'Economy', 'Logger'}, function(errors) if #errors > 0 then print('Failed to load Shop dependencies:', json.encode(errors)) return end exports['mythic-base']:RegisterComponent('Shop', { _protected = true, _name = 'shop', Purchase = function(self, player, item, price) -- Check if player has money if not COMPONENTS.Economy:Has(player, price) then return false, 'Insufficient funds' end -- Charge player COMPONENTS.Economy:Charge(player, price) -- Give item COMPONENTS.Inventory:AddItem(player, item, 1) -- Log purchase COMPONENTS.Logger:Info('Shop', 'Purchase made', { player = player, item = item, price = price }) return true end, Sell = function(self, player, item, price) -- Check if player has item if not COMPONENTS.Inventory:HasItem(player, item, 1) then return false, 'Item not found' end -- Remove item COMPONENTS.Inventory:RemoveItem(player, item, 1) -- Pay player COMPONENTS.Economy:Add(player, price) return true end })end)
exports['mythic-base']:RegisterComponent('ItemFactory', { _templates = {}, RegisterTemplate = function(self, itemId, template) self._templates[itemId] = template end, Create = function(self, itemId, overrides) local template = self._templates[itemId] if not template then return nil, 'Template not found' end -- Clone template local item = {} for k, v in pairs(template) do item[k] = v end -- Apply overrides if overrides then for k, v in pairs(overrides) do item[k] = v end end -- Add metadata item.id = COMPONENTS.Utils:GenerateUUID() item.created = os.time() return item end})
-- Component triggers eventexports['mythic-base']:RegisterComponent('Shop', { Purchase = function(self, player, item, price) local success = self:_processPurchase(player, item, price) if success then -- Trigger event for other systems to react TriggerEvent('Shop:PurchaseComplete', player, item, price) end return success end})-- Other components listenAddEventHandler('Shop:PurchaseComplete', function(player, item, price) -- Award loyalty points COMPONENTS.Loyalty:AddPoints(player, math.floor(price / 10))end)
Public methods should be well-documented and intuitive:
Copy
exports['mythic-base']:RegisterComponent('Garage', { --- Spawn a vehicle from garage ---@param player number Player server ID ---@param vehicleId number Vehicle database ID ---@return boolean success ---@return table|string vehicle or error message SpawnVehicle = function(self, player, vehicleId) -- Implementation end})
3. Error Handling
Always handle errors gracefully:
Copy
exports['mythic-base']:RegisterComponent('Payment', { Charge = function(self, player, amount) -- Validate inputs if not player or amount <= 0 then return false, 'Invalid parameters' end -- Check balance local balance = self:GetBalance(player) if balance < amount then return false, 'Insufficient funds' end -- Attempt charge local success = self:_deductBalance(player, amount) if not success then COMPONENTS.Logger:Error('Payment', 'Failed to deduct balance') return false, 'Transaction failed' end return true end})
4. Dependency Management
Always declare and wait for dependencies:
Copy
// ✅ GOODexports['mythic-base']:RequestDependencies('MyComponent', { 'Database', 'Logger'}, function(errors) if #errors == 0 then -- Safe to use dependencies endend)// ❌ BADlocal db = COMPONENTS.Database -- May be nil!
5. Testability
Design components to be testable:
Copy
exports['mythic-base']:RegisterComponent('Calculator', { -- Pure function - easy to test Add = function(self, a, b) return a + b end, -- Dependency injection - easy to mock SaveResult = function(self, result, callback, database) database = database or COMPONENTS.Database.Game database:insertOne({ collection = 'calculations', document = { result = result } }, function(success, insertedDoc) if callback then callback(success, insertedDoc) end end) end})
-- Organize related components under namespaceexports['mythic-base']:RegisterComponent('Inventory.Items', {...})exports['mythic-base']:RegisterComponent('Inventory.Crafting', {...})exports['mythic-base']:RegisterComponent('Inventory.Shops', {...})-- Access via namespaceCOMPONENTS.Inventory.Items:Get(id)COMPONENTS.Inventory.Crafting:Craft(recipe)COMPONENTS.Inventory.Shops:Purchase(item)