How to access and manage player characters using the Fetch component and DataStore pattern
The Characters system manages player characters in Mythic Framework, including creation, selection, loading, and data persistence. Character data is accessed through the Fetch component using the DataStore pattern.
Critical: Do NOT use COMPONENTS.Characters:GetCharacter() - this method does not exist.Always use the Fetch component:
Copy
local player = Fetch:Source(source)local char = player:GetData('Character')
The primary way to access character data is through the Fetch component.Pattern:
Copy
local player = Fetch:Source(source)if not player then return endlocal char = player:GetData('Character')if not char then return end-- Access character datalocal stateId = char:GetData('SID')local firstName = char:GetData('First')
Example in Event Handler:
Copy
-- Server sideRegisterNetEvent('myresource:server:doSomething', function() local src = source local player = Fetch:Source(src) if not player then print('[ERROR] Player not found') return end local char = player:GetData('Character') if not char then TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'You must be logged in as a character', type = 'error' }) return end -- Safe to access character data local stateId = char:GetData('SID') local name = string.format('%s %s', char:GetData('First'), char:GetData('Last')) local cash = char:GetData('Cash') local bank = char:GetData('Bank') print('Character:', name, 'SID:', stateId, 'Cash:', cash)end)
Get character by player server ID (most common).Example:
Copy
-- Get character from sourcelocal player = Fetch:Source(source)if player then local char = player:GetData('Character') if char then print('Found character:', char:GetData('First'), char:GetData('Last')) endend
-- Find character by SIDlocal player = Fetch:SID(123)if player then local char = player:GetData('Character') local source = player:GetData('Source') -- Send notification TriggerClientEvent('mythic-notifications:client:Send', source, { message = 'You received a message', type = 'info' })end
Find player by any character field (phone, name, etc).Example:
Copy
-- Find by phone numberlocal player = Fetch:CharacterData('Phone', '555-0123')if player then local char = player:GetData('Character') print('Found:', char:GetData('First'), char:GetData('Last'))end-- Find by SID using CharacterDatalocal player = Fetch:CharacterData('SID', 123)
Get specific data from offline character (database query).Example:
Copy
-- Get offline character data (blocking)local phone = Fetch:GetOfflineData(123, 'Phone')local cash = Fetch:GetOfflineData(123, 'Cash')print('Offline character phone:', phone)
GetOfflineData is synchronous and blocks the thread. Use sparingly and only when player is offline.
RegisterNetEvent('myresource:server:action', function() local src = source local player = Fetch:Source(src) if not player then print('[ERROR] Player not found') return end local char = player:GetData('Character') if not char then TriggerClientEvent('mythic-notifications:client:Send', src, { message = 'You must be logged in as a character', type = 'error' }) return end -- Safe to use char now local stateId = char:GetData('SID')end)
-- Remove cash with validationlocal player = Fetch:Source(source)local char = player:GetData('Character')local price = 250local currentCash = char:GetData('Cash')if currentCash >= price then char:SetData('Cash', currentCash - price) TriggerClientEvent('mythic-notifications:client:Send', source, { message = string.format('Paid $%d', price), type = 'success' })else TriggerClientEvent('mythic-notifications:client:Send', source, { message = 'Insufficient funds', type = 'error' })end
Checking Money:
Copy
-- Check if character has enough moneyfunction CanAfford(source, amount) local player = Fetch:Source(source) if not player then return false end local char = player:GetData('Character') if not char then return false end local cash = char:GetData('Cash') return cash >= amountend-- Usageif CanAfford(source, 500) then -- Process purchaseelse -- Send errorend
-- Get all online playerslocal players = Fetch:All()for source, player in pairs(players) do local char = player:GetData('Character') if char then local name = string.format('%s %s', char:GetData('First'), char:GetData('Last')) local stateId = char:GetData('SID') print(source, name, stateId) -- Send server-wide event TriggerClientEvent('myresource:client:serverEvent', source, eventData) endendprint('Total characters online:', Fetch:CountCharacters())
-- You have a character SID, need the player sourcefunction GetSourceFromSID(stateId) local player = Fetch:SID(stateId) if player then return player:GetData('Source') end return nilend-- Usagelocal targetSource = GetSourceFromSID(123)if targetSource then TriggerClientEvent('myresource:client:event', targetSource, data)end
Event:Characters:Server:PlayerDroppedTriggered when player disconnects.Parameters:
source (number) - Player server ID
Example:
Copy
AddEventHandler('Characters:Server:PlayerDropped', function(source) local player = Fetch:Source(source) if player then local char = player:GetData('Character') if char then print('Character logged out:', char:GetData('SID')) -- Save data TriggerEvent('inventory:server:saveInventory', source) end endend)
local player = Fetch:Source(source)if not player then return endlocal char = player:GetData('Character')if not char then return end
❌ Wrong:
Copy
local char = COMPONENTS.Characters:GetCharacter(source) -- DOESN'T EXIST!
Use SID for Database Operations
Character SID is the primary key for database queries:
Copy
-- ✅ Good: Use SIDlocal char = player:GetData('Character')local stateId = char:GetData('SID')Database.Game:findOne({ collection = 'vehicles', query = {owner = stateId}}, callback)-- ❌ Bad: Don't use sourceDatabase.Game:findOne({ collection = 'vehicles', query = {owner = source} -- Wrong! Source is player ID, not character ID}, callback)
DataStore Pattern
Use GetData/SetData methods, not direct table access:
Copy
-- ✅ Good: Use DataStore methodslocal firstName = char:GetData('First')char:SetData('Cash', 1000)-- ❌ Bad: Direct access (may not work)local firstName = char.Firstchar.Cash = 1000
Check if Character is Online
Copy
function IsCharacterOnline(stateId) return Fetch:SID(stateId) ~= nilend-- Usageif IsCharacterOnline(123) then -- Character is online, use Fetch local player = Fetch:SID(123) local char = player:GetData('Character')else -- Character is offline, use database or GetOfflineData local phone = Fetch:GetOfflineData(123, 'Phone')end
Validate Before Modifying Money
Copy
function ModifyMoney(source, account, amount, reason) local player = Fetch:Source(source) if not player then return false end local char = player:GetData('Character') if not char then return false end local current = char:GetData(account == 'cash' and 'Cash' or 'Bank') if amount < 0 and current < math.abs(amount) then return false -- Insufficient funds end char:SetData(account == 'cash' and 'Cash' or 'Bank', current + amount) -- Log transaction COMPONENTS.Logger:Info('Economy', 'Money modified', { console = true, file = true, database = true }, { characterSID = char:GetData('SID'), account = account, amount = amount, reason = reason }) return trueend
-- Server sideAddEventHandler('jobs:server:processPaycheck', function() local players = Fetch:All() for source, player in pairs(players) do local char = player:GetData('Character') if char then local job = char:GetData('Job') local jobGrade = char:GetData('JobGrade') -- Calculate pay based on job and grade local payAmount = GetJobPayAmount(job, jobGrade) if payAmount > 0 then -- Add to bank local currentBank = char:GetData('Bank') char:SetData('Bank', currentBank + payAmount) -- Notify player TriggerClientEvent('mythic-notifications:client:Send', source, { message = string.format('Paycheck: $%d deposited', payAmount), type = 'success' }) -- Log COMPONENTS.Logger:Info('Jobs', 'Paycheck processed', { console = true, file = true }, { characterSID = char:GetData('SID'), job = job, grade = jobGrade, amount = payAmount }) end end endend)
Critical Pattern: Always use Fetch:Source(source) to get the player, then player:GetData('Character') to get the character. This is the ONLY correct way to access character data.