Skip to main content
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.

Event Types

Native FiveM Events

Built-in FiveM events like playerConnecting, onResourceStart, etc.

Network Events

Client-server communication via TriggerServerEvent and TriggerClientEvent

Local Events

Same-side communication via TriggerEvent (doesn’t cross network)

Mythic Events

Framework-specific events with middleware support

Event Naming Conventions

-- Pattern: resource:side:action
'mythic-inventory:server:AddItem'
'mythic-inventory:client:UpdateUI'
'mythic-characters:shared:CharacterLoaded'

-- Pattern: System:Type:Event
'Core:Shared:Ready'
'Proxy:Shared:RegisterReady'
'Middleware:Server:PlayerConnecting'

Core Framework Events

Initialization Events

-- Proxy system ready — safe to register components
AddEventHandler('Proxy:Shared:RegisterReady', function()
    exports['mythic-base']:RegisterComponent('MyComponent', { ... })
end)

-- Core components loaded — safe to fetch and use components
AddEventHandler('Core:Shared:Ready', function()
    exports['mythic-base']:RequestDependencies('MyResource', {
        'Callbacks', 'Fetch', 'Logger'
    }, function(errors)
        if #errors == 0 then
            RetrieveComponents()
        end
    end)
end)

-- A component was registered or extended
AddEventHandler('Proxy:Shared:ExtendReady', function(componentName)
    print('Component ready:', componentName)
end)

Player Events (Server)

-- Player connecting (before fully connected)
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
    local source = source
    deferrals.defer()
    deferrals.update('Checking whitelist...')

    local allowed = CheckWhitelist(source)
    if allowed then
        deferrals.done()
    else
        deferrals.done('Not whitelisted')
    end
end)

-- Player dropped
AddEventHandler('playerDropped', function(reason)
    local source = source
    print('Player left:', GetPlayerName(source), 'Reason:', reason)
end)

Character Events

-- Server-side: character selected and loaded
AddEventHandler('mythic-characters:server:CharacterSelected', function(source, character)
    print('Character loaded:', character.SID)
end)

-- Client-side: player spawned in world
AddEventHandler('mythic-characters:client:Spawned', function(character, location)
    print('Spawned at:', location.x, location.y, location.z)
end)

Registering Event Handlers

Server-Side

-- Listen to local server event
AddEventHandler('mythic-inventory:server:UseItem', function(slot)
    local source = source
    local char = Fetch:Source(source):GetData('Character')
    -- Handle item use
end)

-- Listen to network event from client (must use RegisterNetEvent)
RegisterNetEvent('mythic-inventory:server:DropItem', function(slot, count)
    local source = source
    -- Handle drop
end)

Client-Side

-- Listen to network event from server (must use RegisterNetEvent)
RegisterNetEvent('mythic-inventory:client:UpdateInventory', function(inventory)
    SendNUIMessage({
        type = 'SET_INVENTORY',
        inventory = inventory
    })
end)

Triggering Events

Local Events (Same Side)

-- Server to server (same or different resource)
TriggerEvent('mythic-economy:server:AddMoney', source, 500)

-- Client to client
TriggerEvent('mythic-hud:client:ShowNotification', {
    message = 'Welcome!',
    type = 'info'
})

Network Events (Cross Network)

-- Client to server
TriggerServerEvent('mythic-inventory:server:UseItem', slot)

-- Server to specific client
TriggerClientEvent('mythic-inventory:client:UpdateInventory', source, inventory)

-- Server to all clients
TriggerClientEvent('mythic-notifications:client:SendAll', -1, {
    message = 'Server restarting in 5 minutes',
    type = 'warning'
})

Middleware System

Mythic includes a middleware system for intercepting and processing events before they reach handlers. Location: mythic-base/core/sv_middleware.lua

Registering Middleware

-- Middleware with priority (lower number = runs first)
Middleware:Add('playerConnecting', function(source, ...)
    local args = {...}
    local name = args[1]

    Logger:Info('Connections', 'Player connecting', {
        source = source,
        name = name
    })

    -- Return true to allow event to continue
    return true, ...
end, 10)

-- Higher priority middleware (runs before priority 10)
Middleware:Add('playerConnecting', function(source, ...)
    if #GetPlayers() >= GetConvarInt('sv_maxclients', 32) then
        -- Return false to block the event
        return false, 'Server is full'
    end
    return true, ...
end, 5)

Middleware Priority

Lower numbers run first:
PriorityOrderUse Case
1-5FirstAnti-cheat, validation
10-50MiddleBusiness logic
100+LastLogging, monitoring

Callback System

For request-response patterns, use callbacks instead of fire-and-forget events.

Server Callbacks

-- Register callback on server
Callbacks:RegisterServerCallback('mythic-garage:GetVehicles', function(source, data, cb)
    local char = Fetch:Source(source):GetData('Character')

    Database.Game:find({
        collection = 'vehicles',
        query = { owner = char:GetData('SID') }
    }, function(success, vehicles)
        cb(success and vehicles or {})
    end)
end)

-- Client calls server callback
Callbacks:ServerCallback('mythic-garage:GetVehicles', {}, function(vehicles)
    print('Got vehicles:', #vehicles)
end)

Client Callbacks

-- Register callback on client
Callbacks:RegisterClientCallback('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 specific client
Callbacks:ClientCallback(source, 'mythic-hud:GetPosition', {}, function(position)
    print('Player position:', position.x, position.y, position.z)
end)

Performance Considerations

Avoid triggering events in tight loops:
-- Bad: triggers 100 events per tick
CreateThread(function()
    while true do
        for i = 1, 100 do
            TriggerEvent('someEvent', i)
        end
        Wait(0)
    end
end)

-- Good: batch data into one event
CreateThread(function()
    while true do
        local batch = {}
        for i = 1, 100 do
            table.insert(batch, i)
        end
        TriggerEvent('someEvent', batch)
        Wait(1000)
    end
end)
Network events have overhead. Send them at reasonable intervals:
-- Bad: network event every frame
CreateThread(function()
    while true do
        TriggerServerEvent('updatePosition', GetEntityCoords(PlayerPedId()))
        Wait(0)
    end
end)

-- Good: update periodically
CreateThread(function()
    while true do
        TriggerServerEvent('updatePosition', GetEntityCoords(PlayerPedId()))
        Wait(5000)
    end
end)
Keep event payloads small to reduce network traffic:
-- Bad: sending entire inventory every update
TriggerClientEvent('updateInventory', source, entireInventory)

-- Good: send only what changed
TriggerClientEvent('updateInventorySlot', source, slot, item)

Next Steps

Component System

Complement events with components

Callback API

Complete callback API reference

Middleware API

Middleware API reference

Resource Structure

How to structure resources