Server Events
All character events fire on the server-side.Lifecycle Events
Created, Selected, Deleted
State Changes
Job changes, money changes, updates
Persistent
Save and load events
Hookable
Add custom logic to character operations
Lifecycle Events
mythic-characters:server:CharacterCreated
Fired when a new character is successfully created.Copy
AddEventHandler('mythic-characters:server:CharacterCreated', function(source, character)
-- Handler code
end)
Player server ID who created the character
Newly created character object with all fields
Copy
-- Give starter items
AddEventHandler('mythic-characters:server:CharacterCreated', function(source, character)
-- Create inventory
COMPONENTS.Inventory:Create(character.SID)
-- Give starter items
COMPONENTS.Inventory:AddItem(character.SID, 'phone', 1, {
number = character.Phone
})
COMPONENTS.Inventory:AddItem(character.SID, 'water', 2)
COMPONENTS.Inventory:AddItem(character.SID, 'sandwich', 2)
COMPONENTS.Inventory:AddItem(character.SID, 'id_card', 1, {
name = character.First .. ' ' .. character.Last,
dob = character.DOB,
gender = character.Gender
})
COMPONENTS.Logger:Info('Characters', 'New character created', {
player = source,
character = character.SID,
name = character.First .. ' ' .. character.Last
})
end)
-- Set default job
AddEventHandler('mythic-characters:server:CharacterCreated', function(source, character)
COMPONENTS.Jobs:SetJob(character.SID, 'unemployed', 0)
end)
-- Send welcome notification
AddEventHandler('mythic-characters:server:CharacterCreated', function(source, character)
TriggerClientEvent('mythic-notifications:client:Send', source, {
message = 'Welcome to the city, ' .. character.First .. '!',
type = 'success',
duration = 5000
})
end)
-- Create default bank account
AddEventHandler('mythic-characters:server:CharacterCreated', function(source, character)
COMPONENTS.Database:insertOne('bank_accounts', {
owner = character.SID,
type = 'checking',
balance = 5000, -- Starting balance
accountNumber = GenerateAccountNumber(),
createdAt = os.time()
})
end)
mythic-characters:server:CharacterSelected
Fired when a player selects and loads a character.Copy
AddEventHandler('mythic-characters:server:CharacterSelected', function(source, character)
-- Handler code
end)
Player server ID
Selected character object
Copy
-- Load character data for other systems
AddEventHandler('mythic-characters:server:CharacterSelected', function(source, character)
-- Load inventory
local inventory = COMPONENTS.Inventory:Get(character.SID)
TriggerClientEvent('mythic-inventory:client:SetInventory', source, inventory)
-- Load vehicles
local vehicles = COMPONENTS.Database:find('vehicles', { owner = character.SID })
TriggerClientEvent('mythic-vehicles:client:SetVehicles', source, vehicles)
-- Load properties
local properties = COMPONENTS.Database:find('properties', { owner = character.SID })
TriggerClientEvent('mythic-properties:client:SetProperties', source, properties)
-- Set job
TriggerClientEvent('mythic-jobs:client:SetJob', source, {
job = character.job,
grade = character.jobGrade,
onDuty = character.onDuty or false
})
end)
-- Log character selection
AddEventHandler('mythic-characters:server:CharacterSelected', function(source, character)
COMPONENTS.Logger:Info('Characters', 'Character selected', {
player = source,
playerName = GetPlayerName(source),
character = character.SID,
characterName = character.First .. ' ' .. character.Last
})
-- Update last login
COMPONENTS.Characters:UpdateCharacter(character.SID, {
['metadata.lastLogin'] = os.time()
})
end)
-- Check for pending notifications
AddEventHandler('mythic-characters:server:CharacterSelected', function(source, character)
local notifications = COMPONENTS.Database:find('notifications', {
target = character.SID,
read = false
})
if #notifications > 0 then
TriggerClientEvent('mythic-phone:client:ReceiveNotifications', source, notifications)
end
end)
mythic-characters:server:CharacterDeleted
Fired when a character is deleted.Copy
AddEventHandler('mythic-characters:server:CharacterDeleted', function(source, characterId)
-- Handler code
end)
Player server ID who deleted the character
SID of the deleted character
Copy
-- Clean up character data
AddEventHandler('mythic-characters:server:CharacterDeleted', function(source, characterId)
-- Delete inventory
COMPONENTS.Inventory:Delete(characterId)
-- Delete all vehicles
COMPONENTS.Database:deleteMany('vehicles', { owner = characterId })
-- Delete properties
COMPONENTS.Database:deleteMany('property_ownership', { owner = characterId })
-- Delete phone data
COMPONENTS.Database:deleteMany('phone_messages', {
['$or'] = {
{ sender = characterId },
{ receiver = characterId }
}
})
-- Delete bank accounts
COMPONENTS.Database:deleteMany('bank_accounts', { owner = characterId })
COMPONENTS.Logger:Info('Characters', 'Character data cleaned up', {
character = characterId
})
end)
-- Log deletion for audit
AddEventHandler('mythic-characters:server:CharacterDeleted', function(source, characterId)
COMPONENTS.Database:insertOne('audit_log', {
type = 'character_deleted',
player = GetPlayerIdentifier(source, 0),
character = characterId,
timestamp = os.time()
})
end)
State Change Events
mythic-characters:server:JobChanged
Fired when a character’s job or job grade changes.Copy
AddEventHandler('mythic-characters:server:JobChanged', function(characterId, newJob, newGrade, oldJob, oldGrade)
-- Handler code
end)
Character SID
New job identifier
New job grade
Previous job identifier
Previous job grade
Copy
-- Give job-specific items
AddEventHandler('mythic-characters:server:JobChanged', function(characterId, newJob, newGrade, oldJob, oldGrade)
if newJob == 'police' then
-- Give police equipment
COMPONENTS.Inventory:AddItem(characterId, 'weapon_pistol', 1)
COMPONENTS.Inventory:AddItem(characterId, 'radio', 1)
COMPONENTS.Inventory:AddItem(characterId, 'handcuffs', 1)
elseif newJob == 'ems' then
-- Give EMS equipment
COMPONENTS.Inventory:AddItem(characterId, 'medkit', 5)
COMPONENTS.Inventory:AddItem(characterId, 'bandage', 10)
end
-- Remove old job items
if oldJob == 'police' then
COMPONENTS.Inventory:RemoveItem(characterId, 'weapon_pistol', 999)
COMPONENTS.Inventory:RemoveItem(characterId, 'handcuffs', 999)
end
end)
-- Update permissions
AddEventHandler('mythic-characters:server:JobChanged', function(characterId, newJob, newGrade, oldJob, oldGrade)
local player = Fetch:SID(characterId)
if player then
local source = player:GetData('Source')
-- Update Discord roles or permissions
TriggerEvent('discord:updateRoles', source, newJob)
end
end)
-- Notify player
AddEventHandler('mythic-characters:server:JobChanged', function(characterId, newJob, newGrade, oldJob, oldGrade)
local player = Fetch:SID(characterId)
if player then
local source = player:GetData('Source')
local jobLabel = COMPONENTS.Jobs:GetJobLabel(newJob)
local gradeLabel = COMPONENTS.Jobs:GetGradeLabel(newJob, newGrade)
TriggerClientEvent('mythic-notifications:client:Send', source, {
message = 'Job changed to ' .. jobLabel .. ' - ' .. gradeLabel,
type = 'info'
})
end
end)
mythic-characters:server:MoneyChanged
Fired when a character’s cash or bank balance changes.Copy
AddEventHandler('mythic-characters:server:MoneyChanged', function(characterId, account, amount, newBalance, reason)
-- Handler code
end)
Character SID
Account type:
'cash' or 'bank'Change amount (positive = added, negative = removed)
New balance after change
Transaction reason
Copy
-- Update client HUD
AddEventHandler('mythic-characters:server:MoneyChanged', function(characterId, account, amount, newBalance, reason)
local player = Fetch:SID(characterId)
if player then
local source = player:GetData('Source')
TriggerClientEvent('mythic-hud:client:UpdateMoney', source, {
account = account,
balance = newBalance,
change = amount
})
end
end)
-- Log large transactions
AddEventHandler('mythic-characters:server:MoneyChanged', function(characterId, account, amount, newBalance, reason)
if math.abs(amount) >= 10000 then -- Log transactions $10k+
COMPONENTS.Logger:Info('Economy', 'Large transaction', {
character = characterId,
account = account,
amount = amount,
newBalance = newBalance,
reason = reason
}, { file = true })
end
end)
-- Achievement tracking
AddEventHandler('mythic-characters:server:MoneyChanged', function(characterId, account, amount, newBalance, reason)
if account == 'bank' and newBalance >= 1000000 then
-- Unlock millionaire achievement
COMPONENTS.Achievements:Unlock(characterId, 'millionaire')
end
end)
-- Transaction log
AddEventHandler('mythic-characters:server:MoneyChanged', function(characterId, account, amount, newBalance, reason)
COMPONENTS.Database:insertOne('transactions', {
character = characterId,
account = account,
amount = amount,
balance = newBalance,
reason = reason,
timestamp = os.time()
})
end)
Client Events
mythic-characters:client:Spawned
Fired on the client when the character spawns in the world.Copy
AddEventHandler('mythic-characters:client:Spawned', function(character, location)
-- Handler code
end)
Character data
Spawn location
{x, y, z, heading}Copy
-- Client-side initialization
AddEventHandler('mythic-characters:client:Spawned', function(character, location)
-- Initialize HUD
SendNUIMessage({
type = 'INIT_CHARACTER',
character = {
name = character.First .. ' ' .. character.Last,
job = character.job,
cash = character.cash,
bank = character.bank
}
})
-- Load player appearance
TriggerEvent('mythic-appearance:client:LoadAppearance', character.metadata.appearance)
-- Enable controls
FreezeEntityPosition(PlayerPedId(), false)
print('Spawned as', character.First, character.Last, 'at', location.x, location.y, location.z)
end)
-- Camera transition
AddEventHandler('mythic-characters:client:Spawned', function(character, location)
-- Fade out character selection screen
DoScreenFadeOut(500)
Wait(500)
-- Spawn player
SetEntityCoords(PlayerPedId(), location.x, location.y, location.z)
SetEntityHeading(PlayerPedId(), location.heading)
-- Fade in game world
Wait(1000)
DoScreenFadeIn(1000)
end)
mythic-characters:client:CharacterUpdated
Fired on the client when character data is updated.Copy
AddEventHandler('mythic-characters:client:CharacterUpdated', function(character)
-- Handler code
end)
Copy
-- Update HUD
AddEventHandler('mythic-characters:client:CharacterUpdated', function(character)
SendNUIMessage({
type = 'UPDATE_CHARACTER',
character = character
})
end)
Using Events for Custom Logic
Hook Into Character Creation
Copy
-- Add custom welcome package
AddEventHandler('mythic-characters:server:CharacterCreated', function(source, character)
-- Custom welcome logic
local welcomePackage = {
{ item = 'phone', count = 1 },
{ item = 'water', count = 5 },
{ item = 'sandwich', count = 5 },
{ item = 'starter_pack', count = 1 }
}
for _, item in ipairs(welcomePackage) do
COMPONENTS.Inventory:AddItem(character.SID, item.item, item.count)
end
-- Give starting money
COMPONENTS.Characters:AddMoney(character.SID, 'bank', 5000, 'Welcome bonus')
end)
Track Playtime
Copy
local playtime = {}
-- Start tracking on character select
AddEventHandler('mythic-characters:server:CharacterSelected', function(source, character)
playtime[character.SID] = os.time()
end)
-- Save playtime on disconnect
AddEventHandler('playerDropped', function(reason)
local player = Fetch:Source(source)
if player then
local char = player:GetData('Character')
if char and playtime[char:GetData('SID')] then
local stateId = char:GetData('SID')
local sessionTime = os.time() - playtime[stateId]
local metadata = char:GetData('MetaData') or {}
local totalPlaytime = (metadata.playtime or 0) + sessionTime
-- Update database directly as player is disconnecting
Database.Game:updateOne({
collection = 'characters',
query = {SID = stateId},
update = {
['$set'] = {['MetaData.playtime'] = totalPlaytime}
}
})
playtime[stateId] = nil
end
end
end)
Custom Job Logic
Copy
-- Custom logic when becoming police
AddEventHandler('mythic-characters:server:JobChanged', function(characterId, newJob, newGrade, oldJob, oldGrade)
if newJob == 'police' then
-- Grant police access
local player = Fetch:SID(characterId)
if player then
local source = player:GetData('Source')
-- Give police permissions
ExecuteCommand('add_principal identifier.' .. GetPlayerIdentifier(source, 0) .. ' group.police')
-- Notify
TriggerClientEvent('mythic-notifications:client:Send', source, {
message = 'You are now a police officer',
type = 'success'
})
end
elseif oldJob == 'police' then
-- Remove police access when leaving
local player = Fetch:SID(characterId)
if player then
local source = player:GetData('Source')
ExecuteCommand('remove_principal identifier.' .. GetPlayerIdentifier(source, 0) .. ' group.police')
end
end
end)
Best Practices
Don't Block Event Handlers
Don't Block Event Handlers
❌ Bad:✅ Good:
Copy
AddEventHandler('mythic-characters:server:CharacterCreated', function(source, character)
-- Long synchronous operation
for i = 1, 1000000 do
-- Heavy work
end
})
Copy
AddEventHandler('mythic-characters:server:CharacterCreated', function(source, character)
-- Use thread for heavy work
CreateThread(function()
-- Heavy work
end)
})
Validate Character Exists
Validate Character Exists
Copy
AddEventHandler('mythic-characters:server:JobChanged', function(characterId, ...)
-- Validate character exists (check if online)
local player = Fetch:SID(characterId)
if not player then
COMPONENTS.Logger:Warn('Characters', 'JobChanged for offline character', {
console = true,
file = true
}, {
characterId = characterId
})
return
end
-- Process event
end)
Clean Up Event Data
Clean Up Event Data
Copy
local sessionData = {}
AddEventHandler('mythic-characters:server:CharacterSelected', function(source, character)
sessionData[character.SID] = {
loginTime = os.time(),
source = source
}
end)
AddEventHandler('playerDropped', function()
local player = Fetch:Source(source)
if player then
local char = player:GetData('Character')
if char then
sessionData[char:GetData('SID')] = nil -- Clean up
end
end
end)
Next Steps
Characters - Exports
Character management methods
Characters - Data Structure
Character data schema
Event System
Understanding the event system
Inventory Events
Inventory-related events
Event Timing: Character events fire in a specific order. Use this to your advantage - for example,
CharacterCreated fires before CharacterSelected, so you can set up initial data in the creation handler.