Event middleware system for pre/post processing and validation
The Middleware component provides a middleware layer for intercepting and processing events before they reach their handlers, enabling validation, logging, modification, and blocking of events.
local rateLimits = {}COMPONENTS.Middleware:Add('mythic-inventory:server:UseItem', function(source, slot) local now = os.time() local lastUse = rateLimits[source] or 0 if now - lastUse < 1 then -- 1 second cooldown COMPONENTS.Logger:Warn('RateLimit', 'Item use rate limit', { source = source }) return false, 'Rate limited' end rateLimits[source] = now return true, slotend, 5)-- Clean up on disconnectAddEventHandler('playerDropped', function() rateLimits[source] = nilend)
-- Require job for certain actionsCOMPONENTS.Middleware:Add('mythic-police:server:Arrest', function(source, targetId) local char = COMPONENTS.Characters:GetCharacter(source) if not char or char.job ~= 'police' then COMPONENTS.Logger:Warn('Permissions', 'Non-police arrest attempt', { source = source, target = targetId }) return false, 'Not a police officer' end -- Check on-duty if not char.onDuty then return false, 'You must be on duty' end return true, targetIdend, 10)
-- Built-in: Check server capacityCOMPONENTS.Middleware:Add('playerConnecting', function(source, name, setKickReason, deferrals) local maxPlayers = GetConvarInt('sv_maxclients', 32) local currentPlayers = #GetPlayers() if currentPlayers >= maxPlayers then deferrals.done('Server is full') return false end return true, name, setKickReason, deferralsend, 10)-- Built-in: Ban checkCOMPONENTS.Middleware:Add('playerConnecting', function(source, name, setKickReason, deferrals) local identifiers = GetPlayerIdentifiers(source) -- Check if banned local ban = COMPONENTS.Punishment:GetBan(identifiers) if ban and ban.active then deferrals.done('You are banned: ' .. ban.reason) return false end return true, name, setKickReason, deferralsend, 5)
-- ✅ Good: Validation before loggingCOMPONENTS.Middleware:Add('event', validateData, 10)COMPONENTS.Middleware:Add('event', logEvent, 50)-- ❌ Bad: Logging before validationCOMPONENTS.Middleware:Add('event', logEvent, 10)COMPONENTS.Middleware:Add('event', validateData, 50)
Always Return Values
❌ Bad:
Copy
COMPONENTS.Middleware:Add('event', function(source, data) if not IsValid(data) then return false -- Missing parameters! } -- Missing return statement!end)
✅ Good:
Copy
COMPONENTS.Middleware:Add('event', function(source, data) if not IsValid(data) then return false, 'Invalid data' end return true, data -- Always return paramsend)
COMPONENTS.Middleware:Add('event', function(source, data) -- Only modify if needed if not data.timestamp then data.timestamp = os.time() end return true, dataend)
Log Blocked Events
Always log when blocking:
Copy
COMPONENTS.Middleware:Add('event', function(source, data) if not IsValid(data) then -- Log the block COMPONENTS.Logger:Warn('Middleware', 'Event blocked', { event = 'eventName', source = source, reason = 'Invalid data', data = data }) return false, 'Invalid data' end return true, dataend)
Avoid Heavy Operations
❌ Bad (Slow):
Copy
COMPONENTS.Middleware:Add('event', function(source, data) -- Heavy database query in middleware local all = COMPONENTS.Database:find('collection', {}) -- ...process... return true, dataend)
✅ Good (Fast):
Copy
COMPONENTS.Middleware:Add('event', function(source, data) -- Quick validation only if not data.id then return false, 'Missing ID' end return true, dataend)
Heavy operations should be in event handlers, not middleware.
Currently, middleware cannot be removed after registration. Plan your middleware carefully:Workaround with flags:
Copy
local middlewareEnabled = trueCOMPONENTS.Middleware:Add('event', function(source, data) if not middlewareEnabled then -- Skip middleware return true, data end -- Middleware logic return true, dataend)-- Later: disable middlewaremiddlewareEnabled = false
Performance Tip: Middleware adds overhead to every event. Keep middleware logic fast and lightweight. Use it for validation and logging, not heavy processing.