Skip to main content

Overview

The event global provides an event system for intercepting and handling packets, connection events, and game events. All packet interception happens in real-time as data flows between the client and server.

Registering Event Handlers

Use event.on() to register event handlers:
event.on
function
event_name
string
required
The name of the event to listen for
callback
function
required
The function to call when the event fires. Receives a context object.
event.on("server:SendToServer", function(ctx)
    -- Handle event
end)

Available Events

Connection Events

client:Connect
event
Fired when the game client connects to the proxy
event.on("client:Connect", function(ctx)
    logger.info("Client connected to proxy")
end)
client:Disconnect
event
Fired when the game client disconnects from the proxy
event.on("client:Disconnect", function(ctx)
    logger.info("Client disconnected")
end)
server:Connect
event
Fired when the proxy connects to the game server
event.on("server:Connect", function(ctx)
    logger.info("Connected to game server")
end)
server:Disconnect
event
Fired when the proxy disconnects from the game server
event.on("server:Disconnect", function(ctx)
    logger.info("Disconnected from server")
end)

Packet Events

client:SendToClient
event
Fired when a packet is sent from the server to the client
event.on("client:SendToClient", function(ctx)
    if ctx:has_packet() then
        local pkt = ctx:parse()
        -- Process packet
    end
end)
server:SendToServer
event
Fired when a packet is sent from the client to the server
event.on("server:SendToServer", function(ctx)
    if ctx:has_packet() then
        local pkt = ctx:parse()
        -- Process packet
    end
end)

Game Events

You can also listen for specific game packet events by name:
event.on("OnSpawn", function(ctx)
    -- Fired when a player spawns
end)

event.on("Input", function(ctx)
    -- Fired when player sends chat/input
end)

Event Context

All event handlers receive a context object with these methods:
ctx:has_packet
function
Returns true if the event has an associated packet
if ctx:has_packet() then
    -- Safe to call ctx:parse()
end
ctx:parse
function
Parse and return the packet data. Alias for ctx:parse_packet().
local pkt = ctx:parse()
ctx:get_packet
function
Get the raw packet object
local pkt = ctx:get_packet()
ctx:get_data
function
Get the raw packet bytes as a string
local data = ctx:get_data()
logger.info("Packet size: {} bytes", #data)
ctx:cancel
function
Cancel the packet (prevent it from being sent)
ctx:cancel()

Examples

Intercepting Chat Messages

event.on("Input", function(ctx)
    if ctx:has_packet() then
        local pkt = ctx:get_packet()
        logger.info("[Chat] {}", pkt.text)
        
        -- Block specific messages
        if pkt.text == "!block" then
            ctx:cancel()
            logger.info("Blocked message")
        end
    end
end)

Modifying Packets

event.on("server:SendToServer", function(ctx)
    local pkt = ctx:parse()
    
    -- Modify packet fields directly
    if pkt.net_id then
        pkt.net_id = 999
    end
    
    -- Cancel original and send modified version
    ctx:cancel()
    send.to_server(pkt)
end)

Tracking Player Spawns

local spawned_players = {}

event.on("OnSpawn", function(ctx)
    if ctx:has_packet() then
        local pkt = ctx:get_packet()
        
        spawned_players[pkt.net_id] = {
            name = pkt.name,
            spawn_time = os.time()
        }
        
        logger.info("Player {} spawned (net_id: {})", pkt.name, pkt.net_id)
    end
end)

Auto-Response System

event.on("Input", function(ctx)
    if ctx:has_packet() then
        local pkt = ctx:get_packet()
        
        if pkt.text == "!hello" then
            ctx:cancel()
            
            local response = LogPacket.new()
            response.msg = "`2Hello from the proxy!"
            send.to_client(response)
        end
    end
end)

Packet Statistics

local stats = {
    client_packets = 0,
    server_packets = 0
}

event.on("client:SendToClient", function(ctx)
    stats.client_packets = stats.client_packets + 1
end)

event.on("server:SendToServer", function(ctx)
    stats.server_packets = stats.server_packets + 1
end)

scheduler.schedule_periodic(10000, function()
    logger.info("Stats - Client: {}, Server: {}",
        stats.client_packets,
        stats.server_packets
    )
    return true
end)

Complex Packet Filtering

event.on("ServerBoundPacket", function(ctx)
    local pkt = ctx:get_packet()
    if not pkt then return end
    
    -- Handle text packets
    if pkt.text_parse then
        local action = pkt.text_parse:get("action")
        if action ~= "" then
            logger.info("[Text] Action: {}", action)
        end
    end
    
    -- Handle game packets
    if pkt.game_packet then
        logger.info("[Game] Type: {} NetID: {}",
            pkt.game_packet.type,
            pkt.game_packet.net_id
        )
        
        -- Check for jump flag
        if pkt.game_packet.flags & packet.PacketFlag.PACKET_FLAG_ON_JUMP ~= 0 then
            logger.info("[Game] Player jumped!")
        end
    end
    
    -- Handle variant packets
    if pkt.variant then
        logger.info("[Variant] Function: {}", pkt.variant:get(0))
    end
end)

Best Practices

Always check if a packet exists
event.on("OnSpawn", function(ctx)
    if ctx:has_packet() then
        local pkt = ctx:get_packet()
        -- Safe to use pkt
    end
end)
Cancel before resending modified packetsWhen modifying packets, always cancel the original:
local pkt = ctx:parse()
pkt.field = "modified"

ctx:cancel()  -- Must cancel first
send.to_server(pkt)
Use specific events when possibleInstead of:
event.on("server:SendToServer", function(ctx)
    local pkt = ctx:parse()
    if pkt.type == "OnSpawn" then
        -- Handle spawn
    end
end)
Prefer:
event.on("OnSpawn", function(ctx)
    -- Directly handles OnSpawn packets
end)

See Also

Packets API

Learn about sending packets

World API

Access world and player data