Network Communication and Events
Time to connect your client plugin to the server! This guide covers the event system (BridgeFunction) and network communication with packets.
The Event System (BridgeFunction)
BridgeFunction is how you hook into game events. Think of it as subscribing to notifications about what's happening in the game.
BridgeFunction:push(eventName, callback)
Parameters:
eventName- Name of the event to listen forcallback- Function to call when the event happens
Multiple functions can be registered for the same event.
Available Events
OnLoad
Called when the client finishes loading. This is where you initialize everything.
Use for: Creating windows, loading images, creating fonts
BridgeFunction:push("OnLoad", function()
LogPrint("Client loaded!")
-- Create windows
local window = UIWindowAdd(
UI_GROUP_INGAME, GetWindowIndex(),
0, 2, 100, 100, 300, 200, 1,
MyPlugin.renderWindow, MyPlugin.updateWindow
)
-- Load images
UILoadImage("Data/Custom/myimage.png", 10001, GL_LINEAR)
-- Create fonts
local myFont = UIFontCreate("Arial", 14, 1)
end)
OnServer
Called when on the server selection screen.
Use for: Showing server selection specific windows
BridgeFunction:push("OnServer", function()
LogPrint("Server selection screen")
local window = UIWindowGet(UI_GROUP_SERVER, MyPlugin.serverWindowIndex)
if window then
window:setState(1) -- Show window
end
end)
OnSelect
Called when on the character selection screen.
Use for: Showing character selection specific windows
BridgeFunction:push("OnSelect", function()
LogPrint("Character selection screen")
local window = UIWindowGet(UI_GROUP_SELECT, MyPlugin.selectWindowIndex)
if window then
window:setState(1)
end
end)
OnInGame
Called when entering the game (after selecting character). This is when character data becomes available.
Use for: Initializing character data, showing HUD
BridgeFunction:push("OnInGame", function()
LogPrint("Entered the game!")
-- Get character data
local charData = CharacterGetAttrTable()
LogPrint("Name: " .. charData.index)
LogPrint("Level: " .. charData.level)
LogPrint("Class: " .. charData.class)
-- Show game window
local window = UIWindowGet(UI_GROUP_INGAME, MyPlugin.index)
if window then
-- Update window data
window:setAttach({
playerName = "Player",
level = charData.level,
hp = charData.curHP,
maxHp = charData.maxHP
})
end
end)
OnPacketRecv
Called when receiving a packet from the server. For advanced use when you need to intercept raw packets.
Parameters: buff (buffer), size (packet size)
BridgeFunction:push("OnPacketRecv", function(buff, size)
-- Load packet for reading
local packet = PacketLoad(buff, size, 0)
-- Check if it's a packet we're interested in
if packet.index == 0xC1 then -- Example: C1 packet type
-- Process packet...
LogPrint("Received C1 packet")
end
-- IMPORTANT: Always free the packet after use
packet:free()
end)
Character Data
Get all the info about the player's character:
CharacterGetAttrTable()
Returns: Table with all character attributes
Available Fields
local char = CharacterGetAttrTable()
char.index -- Character index
char.class -- Class (0-14)
char.level -- Level (1-400)
char.masterLevel -- Master Level (0-400)
char.reset -- Reset count
char.masterReset -- Master reset count
char.point -- Available points
char.curHP -- Current HP
char.maxHP -- Max HP
char.curMP -- Current MP
char.maxMP -- Max MP
char.curBP -- Current BP (AG)
char.maxBP -- Max BP
char.curSD -- Current Shield
char.maxSD -- Max Shield
char.strength -- Total strength
char.dexterity -- Total dexterity
char.vitality -- Total vitality
char.energy -- Total energy
char.leadership -- Total leadership
char.addStrength -- Added strength
char.addDexterity -- Added dexterity
char.addVitality -- Added vitality
char.addEnergy -- Added energy
char.addLeadership -- Added leadership
Complete Example
local char = CharacterGetAttrTable()
LogPrint("Character Data:")
LogPrint("Class: " .. CharacterGetClassName(char.class))
LogPrint("Level: " .. char.level)
LogPrint("Master Level: " .. char.masterLevel)
LogPrint("Reset: " .. char.reset)
LogPrint("Master Reset: " .. char.masterReset)
LogPrint("HP: " .. char.curHP .. "/" .. char.maxHP)
LogPrint("MP: " .. char.curMP .. "/" .. char.maxMP)
LogPrint("Strength: " .. char.strength .. " (+" .. char.addStrength .. ")")
LogPrint("Dexterity: " .. char.dexterity .. " (+" .. char.addDexterity .. ")")
Getting Class Names
CharacterGetClassName(class)
Returns: Class name string
Classes:
- 0 = Dark Wizard (DW)
- 1 = Dark Knight (DK)
- 2 = Fairy Elf (FE)
- 3 = Magic Gladiator (MG)
- 4 = Dark Lord (DL)
- 5 = Summoner (SU)
- 6 = Rage Fighter (RF)
- 7 = Grow Lancer (GL)
- 8 = Rune Wizard (RW)
- 9 = Slayer (SL)
- 10 = Gun Crusher (GC)
- 11 = Light Wizard (LW)
- 12 = Lemuria Mage (LM)
- 13 = Illusion Knight (IK)
- 14 = Abyss Lord (AL)
local className = CharacterGetClassName(1)
LogPrint(className) -- "Dark Knight"
Network Packets
Now for the fun part - talking to the server!
Registering Packet IDs
First, you need to register your packet IDs. These MUST match the server exactly!
-- File: Client/GGPlugins/Toolkit/PluginPacket.lua
PluginPacket = {
MyPlugin = 15000,
AnotherPlugin = 15001,
ThirdPlugin = 15002,
-- Add your IDs here
}
IMPORTANT: Always use IDs >= 15000 to avoid conflicts with the game's packets.
Sending Data to Server
MagicWorld:sendData(packetId, data)
Parameters:
packetId- Packet ID (from PluginPacket)data- Table with values to send (array format)
Supported data types:
- numbers (integers and decimals)
- strings
- tables (will be serialized)
Send Examples
-- Send just a number
MagicWorld:sendData(PluginPacket.MyPlugin, {100})
-- Send a string
MagicWorld:sendData(PluginPacket.MyPlugin, {"Hello Server"})
-- Send multiple values
MagicWorld:sendData(PluginPacket.MyPlugin, {
"PlayerName", -- String
400, -- Level
50, -- Reset
12500 -- Zen
})
-- Send complex table
MagicWorld:sendData(PluginPacket.MyPlugin, {
{
action = "buy",
itemId = 100,
quantity = 5
}
})
Practical Example - Button Sending Data
function MyPlugin.renderButton(window, button)
if button.hover == 0 then
button:renderAsset(GlobalAsset.buttonN, 0, 0, button.w, button.h)
elseif button.click == 3 then
-- Released click - send data
local textInput = window:getTextInput(0)
if textInput and textInput.inputText ~= "" then
-- Send text to server
MagicWorld:sendData(PluginPacket.MyPlugin, {
"chat_message",
textInput.inputText
})
textInput:setInputText("") -- Clear field
NoticeSend(1, "Message sent!")
end
end
button:renderText("Send", 0, 0, button.w, button.h, 4,
RGBA(255,255,255,255), FontM)
end
Receiving Data from Server
Register a callback to handle packets from the server:
MagicWorld:pushRecv(packetId, callback)
Callback receives: packet object with reading methods
Basic Example
MagicWorld:pushRecv(PluginPacket.MyPlugin, function(packet)
-- Read data from packet
local message = packet:getString()
local value = packet:getNumber()
LogPrint("Received: " .. message .. ", Value: " .. value)
-- Show notification to player
NoticeSend(1, message)
-- IMPORTANT: Always free the packet!
packet:free()
end)
Packet Object Methods
Reading Methods
-- packet.index
-- Property: Packet ID
local packetId = packet.index
-- packet:getNumber()
-- Returns: integer (4 bytes)
local intValue = packet:getNumber()
-- packet:getSingle()
-- Returns: float (4 bytes)
local floatValue = packet:getSingle()
-- packet:getString(size)
-- Returns: string
-- size: optional max length
local text = packet:getString()
local limitedText = packet:getString(20) -- Max 20 chars
-- packet:free()
-- Frees packet memory (ALWAYS use after processing!)
packet:free()
Complete Example - Custom Chat System
Here's a full working chat system:
CustomChat = {
index = GetWindowIndex(),
group = UI_GROUP_INGAME,
messages = {},
maxMessages = 10,
}
-- Register packet
PluginPacket.CustomChat = 15010
function CustomChat.renderWindow(window)
UIRenderWindow(window, 0, 0, window.w, window.h, 0)
-- Title
window:renderText("Custom Chat", 0, 10, window.w, 30, 4,
RGBA(255,215,0,255), FontL)
-- Message area
window:renderColor(10, 40, window.w-20, 200, RGBA(0,0,0,200))
-- Render messages
local y = 45
local data = window:getAttach()
if data and data.messages then
for i = #data.messages, math.max(1, #data.messages - 7), -1 do
local msg = data.messages[i]
window:renderText(msg, 15, y, window.w-30, 20, 1,
RGBA(255,255,255,255), FontS)
y = y + 22
end
end
-- Input label
window:renderText("Type:", 10, 245, 80, 25, 1,
RGBA(200,200,200,255), FontM)
end
function CustomChat.renderTextInput(window, textInput)
UIRenderWindowColor(textInput, 0, 0, textInput.w, textInput.h,
RGBA(0,0,0,200), RGBA(100,100,100,255))
end
function CustomChat.renderButton(window, button)
if button.hover == 0 then
button:renderAsset(GlobalAsset.buttonN, 0, 0, button.w, button.h)
elseif button.click == 0 then
button:renderAsset(GlobalAsset.buttonH, 0, 0, button.w, button.h)
elseif button.click == 3 then
button:renderAsset(GlobalAsset.buttonC, 0, 0, button.w, button.h)
-- Send message
local textInput = window:getTextInput(0)
if textInput and textInput.inputText ~= "" then
-- Send to server
MagicWorld:sendData(PluginPacket.CustomChat, {textInput.inputText})
textInput:setInputText("")
end
end
button:renderText("Send", 0, 0, button.w, button.h, 4,
RGBA(255,255,255,255), FontM)
end
-- Initialization
BridgeFunction:push("OnLoad", function()
local w = 400
local h = 320
local window = UIWindowAdd(
CustomChat.group, CustomChat.index,
0, 2,
GetWindowCenterX(w), GetWindowCenterY(h),
w, h, 1,
CustomChat.renderWindow, nil
)
if window then
window:setDragArea(0, 0, w, 35)
window:setAttach({messages = {}})
-- Text field
local textInput = window:addTextInput(0, 1, 100, 0, 10, 275, 300, 30,
CustomChat.renderTextInput)
if textInput then
textInput:setInputTextFont(FontM)
textInput:setInputTextSpot(5, 0)
textInput:setInputTextSize(0, textInput.h)
end
-- Send button
window:addButton(0, 1, 315, 275, 75, 30, CustomChat.renderButton)
end
end)
-- Receive messages from server
MagicWorld:pushRecv(PluginPacket.CustomChat, function(packet)
local sender = packet:getString()
local message = packet:getString()
local fullMessage = sender .. ": " .. message
-- Add to messages
local window = UIWindowGet(CustomChat.group, CustomChat.index)
if window then
local data = window:getAttach()
if data then
table.insert(data.messages, fullMessage)
-- Limit message count
if #data.messages > CustomChat.maxMessages then
table.remove(data.messages, 1)
end
end
end
-- Show notification
NoticeSend(1, fullMessage)
-- Free packet
packet:free()
end)
Example - Data Sync with Server
DataSync = {
index = GetWindowIndex(),
group = UI_GROUP_INGAME,
}
PluginPacket.DataSync = 15020
-- Request data from server
function DataSync:requestData()
MagicWorld:sendData(PluginPacket.DataSync, {"request", "player_stats"})
end
-- Receive response from server
MagicWorld:pushRecv(PluginPacket.DataSync, function(packet)
local type = packet:getString()
if type == "player_stats" then
local kills = packet:getNumber()
local deaths = packet:getNumber()
local kda = packet:getSingle() -- Float
local window = UIWindowGet(DataSync.group, DataSync.index)
if window then
window:setAttach({
kills = kills,
deaths = deaths,
kda = kda
})
end
NoticeSend(1, string.format("Stats: %d/%d (KDA: %.2f)", kills, deaths, kda))
end
packet:free()
end)
-- Request data when entering game
BridgeFunction:push("OnInGame", function()
DataSync:requestData()
end)
Best Practices
-
ALWAYS free packets with
packet:free()after processing - Prevents memory leaks -
Packet IDs must be >= 15000 - Avoids conflicts with game packets
-
IDs must match on client and server - Edit PluginPacket.lua on both sides identically
-
Use OnInGame for character data - CharacterGetAttrTable() only works when in game
-
Validate received data - Always check if values are valid before using them
-
Don't spam packets - Avoid sending packets every frame - Use timers or specific events
-
Use NoticeSend() for feedback - Inform the player about actions executed
Quick Reference
-- Events
BridgeFunction:push("OnLoad", function() end)
BridgeFunction:push("OnInGame", function() end)
-- Character data
local char = CharacterGetAttrTable()
local className = CharacterGetClassName(char.class)
-- Send to server
MagicWorld:sendData(PluginPacket.MyPlugin, {"action", value})
-- Receive from server
MagicWorld:pushRecv(PluginPacket.MyPlugin, function(packet)
local data = packet:getString()
packet:free()
end)
That's network communication covered! Next up: utility functions for HTTP, JSON, sounds, and more.