Understanding event-driven communication in Mythic Framework
Mythic Framework uses an event-driven architecture that enables loosely-coupled communication between resources, between client and server, and between different parts of the same resource.
-- Proxy system readyAddEventHandler('Proxy:Shared:RegisterReady', function() -- Proxy system initialized -- Safe to register componentsend)-- Core components loadedAddEventHandler('Core:Shared:Ready', function() -- All core components available -- Safe to fetch and use components local Logger = COMPONENTS.Logger local Database = COMPONENTS.Databaseend)-- Component registered/extendedAddEventHandler('Proxy:Shared:ExtendReady', function(componentName) -- A component was registered or extended print('Component ready:', componentName)end)-- Resource startedAddEventHandler('onResourceStart', function(resourceName) if resourceName == GetCurrentResourceName() then -- This resource just started print('Resource started:', resourceName) endend)
-- Player connecting (before fully connected)AddEventHandler('playerConnecting', function(name, setKickReason, deferrals) local source = source deferrals.defer() deferrals.update('Checking whitelist...') -- Check whitelist, bans, etc. local allowed = CheckWhitelist(source) if allowed then deferrals.done() else deferrals.done('Not whitelisted') endend)-- Player fully joinedAddEventHandler('playerJoining', function() local source = source print('Player joined:', GetPlayerName(source))end)-- Player droppedAddEventHandler('playerDropped', function(reason) local source = source print('Player left:', GetPlayerName(source), 'Reason:', reason) -- Save player data COMPONENTS.Characters:SaveCharacter(source)end)
-- Listen to server eventAddEventHandler('mythic-inventory:server:UseItem', function(slot) local source = source -- Player who triggered event -- Get player's character local char = COMPONENTS.Characters:GetCharacter(source) -- Get item in slot local item = COMPONENTS.Inventory:GetItemInSlot(char.SID, slot) if item then -- Use the item COMPONENTS.Items:UseItem(source, item) endend)-- Listen to client event from serverRegisterNetEvent('mythic-inventory:server:DropItem', function(slot, count) local source = source -- Handle dropend)
-- Listen to client eventAddEventHandler('mythic-hud:client:UpdateStatus', function(status) -- Update HUD with new status SendNUIMessage({ type = 'UPDATE_STATUS', status = status })end)-- Listen to server event (must be registered)RegisterNetEvent('mythic-inventory:client:UpdateInventory', function(inventory) -- Update UI SendNUIMessage({ type = 'SET_INVENTORY', inventory = inventory })end)
-- Server to server (same resource or different)TriggerEvent('mythic-economy:server:AddMoney', source, 500)-- Client to client (same resource or different)TriggerEvent('mythic-hud:client:ShowNotification', { message = 'Welcome!', type = 'info'})
-- Client to serverTriggerServerEvent('mythic-inventory:server:UseItem', slot)-- Server to specific clientTriggerClientEvent('mythic-inventory:client:UpdateInventory', source, inventory)-- Server to all clientsTriggerClientEvent('mythic-notifications:client:SendAll', -1, { message = 'Server restarting in 5 minutes', type = 'warning'})-- Server to all clients except onefor _, playerId in ipairs(GetPlayers()) do if playerId ~= source then TriggerClientEvent('someEvent', playerId, data) endend
-- Register middleware for playerConnectingCOMPONENTS.Middleware:Add('playerConnecting', function(source, ...) local args = {...} local name = args[1] -- Log connection attempt COMPONENTS.Logger:Info('Connections', 'Player connecting', { source = source, name = name }) -- Allow event to continue return true, ...end, 10) -- Priority 10 (lower = earlier)-- Register middleware that can blockCOMPONENTS.Middleware:Add('playerConnecting', function(source, ...) local args = {...} -- Check if server is full if #GetPlayers() >= GetConvarInt('sv_maxclients', 32) then -- Block event return false, 'Server is full' end -- Allow event return true, ...end, 5) -- Higher priority runs first
-- Register callback on clientCOMPONENTS.Callback:RegisterCallback('mythic-hud:GetPosition', function(data, cb) local ped = PlayerPedId() local coords = GetEntityCoords(ped) cb({ x = coords.x, y = coords.y, z = coords.z })end)-- Server requests data from clientCOMPONENTS.Callback:DoCallback(source, 'mythic-hud:GetPosition', {}, function(position) print('Player position:', position.x, position.y, position.z)end)
// ❌ BAD - Triggers 100 events per tickCreateThread(function() while true do for i = 1, 100 do TriggerEvent('someEvent', i) end Wait(0) endend)// ✅ GOOD - Batch dataCreateThread(function() while true do local batch = {} for i = 1, 100 do table.insert(batch, i) end TriggerEvent('someEvent', batch) Wait(1000) endend)
Network Events
Network events have overhead. Use sparingly:
Copy
// ❌ BAD - Network event every frameCreateThread(function() while true do local coords = GetEntityCoords(PlayerPedId()) TriggerServerEvent('updatePosition', coords) Wait(0) endend)// ✅ GOOD - Update periodicallyCreateThread(function() while true do local coords = GetEntityCoords(PlayerPedId()) TriggerServerEvent('updatePosition', coords) Wait(5000) -- Every 5 seconds endend)
Event Handler Count
Too many handlers for one event slows processing:
Copy
// Avoid registering handlers in loopsfor i = 1, 100 do AddEventHandler('someEvent', function() -- Handler logic end)end// Instead, use one handlerAddEventHandler('someEvent', function() for i = 1, 100 do -- Process logic endend)
Data Size
Keep event data small to reduce network traffic:
Copy
// ❌ BAD - Sending entire inventory every updateTriggerClientEvent('updateInventory', source, entireInventory)// ✅ GOOD - Send only what changedTriggerClientEvent('updateInventorySlot', source, slot, item)
-- Monitor specific eventAddEventHandler('mythic-inventory:server:UseItem', function(...) local args = {...} print('UseItem called with args:', json.encode(args)) -- Continue to other handlersend)
Events are powerful but can be overused. For tight integration between systems, consider using components instead of events. Events are best for loose coupling and notifications.