-
Notifications
You must be signed in to change notification settings - Fork 6
Callbacks
Complete reference for all event callback functions triggered by game server events.
- Callback Types
- Connection & Authentication
- Server Lifecycle
- Warehouse Events
- Special Event Entries
- Player Progression
- Command Handling
- NPC Interaction
- Inventory Management
- Item Acquisition
- Equipment Events
- Item Repair
- Map & Movement
- Combat & Death
- Shop & Trading
- Database Queries
Sync (Synchronous):
- Executed synchronously in C++ event flow
- Can return values to affect game logic
- Can prevent/allow actions
- Must complete quickly to avoid blocking
Async (Asynchronous):
- Executed asynchronously after event
- Cannot return values
- Cannot prevent actions
- Used for logging, notifications, custom logic
function onCharacterSelectEnter(oPlayer)Type: Async
Called when: Player enters character selection screen
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player object entering character select |
Usage:
function onCharacterSelectEnter(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("Player entering character select: %s", oPlayer.Name))
end
endfunction onPlayerConnect(oPlayer)Type: Async
Called when: Player successfully connects to game server
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player object that connected |
Usage:
function onPlayerConnect(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("Player connected: %s from IP: %s", oPlayer.Name, oPlayer.IP))
Message.Send(0, oPlayer.Index, 1, "Welcome to the server!")
end
endfunction onPlayerLogin(oPlayer)Type: Async
Called when: Player logs in with character
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player object that logged in |
Usage:
function onPlayerLogin(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("%s logged in - Level %d", oPlayer.Name, oPlayer.Level))
-- Award daily login bonus
if CheckDailyLogin(oPlayer.Index) then
Player.SetMoney(oPlayer.Index, 100000, false)
Message.Send(0, oPlayer.Index, 0, "Daily login bonus: 100,000 zen!")
end
end
endfunction onPlayerDisconnect(oPlayer)Type: Async
Called when: Player disconnects from server
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player object that disconnected |
Usage:
function onPlayerDisconnect(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("Player disconnected: %s", oPlayer.Name))
-- Clean up player-specific data
ClearPlayerTimers(oPlayer.Index)
RemoveFromActiveEvents(oPlayer.Index)
end
endfunction onGameServerStart()Type: Async
Called when: Game server starts
Parameters: None
Usage:
function onGameServerStart()
Log.Add("=== Game Server Started ===")
Log.Add(string.format("Server: %s (Code: %d)", Server.GetName(), Server.GetCode()))
Log.Add(string.format("Max Players: %d", Object.GetMaxOnlineUser()))
-- Initialize server-wide systems
InitializeEventScheduler()
LoadItemBags()
StartGlobalTimers()
endfunction onDisconnectAllPlayers()Type: Async
Called when: Server initiates forced shutdown (disconnects all players immediately)
Parameters: None
Usage:
function onDisconnectAllPlayers()
Log.Add("[Server] Forced shutdown initiated - disconnecting all players")
-- Save any pending data
SaveServerState()
-- Log shutdown event
Database.LogEvent("SERVER_FORCED_SHUTDOWN", os.date("%Y-%m-%d %H:%M:%S"))
endfunction onLogOutAllPlayers()Type: Async
Called when: Server initiates graceful shutdown (requests logout for all players)
Parameters: None
Usage:
function onLogOutAllPlayers()
Log.Add("[Server] Graceful shutdown initiated - requesting all players to log out")
-- Notify all players
Message.Send(0, -1, 1, "Server maintenance in 5 minutes. Please log out safely.")
-- Save all character data
for player in Object.Iterator(Object.IteratorType.PLAYER) do
if player ~= nil then
Database.SaveCharacter(player.Index)
end
end
endfunction onDisconnectAllPlayersWithReconnect()
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Async
Called when: Server initiates restart (disconnects players with reconnect allowed)
Parameters: None
Usage:
function onDisconnectAllPlayersWithReconnect()
Log.Add("[Server] Server restart initiated - players can reconnect")
-- Notify players about restart
Message.Send(0, -1, 1, "Server restarting now. You can reconnect in 2 minutes.")
-- Log restart event with timestamp
Database.LogEvent("SERVER_RESTART", os.date("%Y-%m-%d %H:%M:%S"))
-- Save restart flag for post-restart processing
Server.SetCustomValue("last_restart_time", os.time())
endfunction onOpenWarehouse(oPlayer)Type: Async
Called when: Player opens warehouse
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player opening warehouse |
Usage:
function onOpenWarehouse(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("%s opened warehouse", oPlayer.Name))
-- Check for VIP benefits
if oPlayer.userData.VIPType > 0 then
Message.Send(0, oPlayer.Index, 0, "VIP: Extended warehouse space active")
end
end
endfunction onCloseWarehouse(oPlayer)Type: Async
Called when: Player closes warehouse
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player closing warehouse |
Usage:
function onCloseWarehouse(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("%s closed warehouse", oPlayer.Name))
end
endfunction onBloodCastleEnter(oPlayer, EventLevel)Type: Async
Called when: Player enters Blood Castle event
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player entering event |
EventLevel |
int | Blood Castle level (1-8) |
Usage:
function onBloodCastleEnter(oPlayer, EventLevel)
if oPlayer ~= nil then
Log.Add(string.format("%s entered Blood Castle Level %d", oPlayer.Name, EventLevel))
Message.Send(0, oPlayer.Index, 1, string.format("Blood Castle %d - Good luck!", EventLevel))
end
endfunction onChaosCastleEnter(oPlayer, EventLevel)Type: Async
Called when: Player enters Chaos Castle event
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player entering event |
EventLevel |
int | Chaos Castle level (1-7) |
Usage:
function onChaosCastleEnter(oPlayer, EventLevel)
if oPlayer ~= nil then
Log.Add(string.format("%s entered Chaos Castle Level %d", oPlayer.Name, EventLevel))
end
endfunction onDevilSquareEnter(oPlayer, EventLevel)Type: Async
Called when: Player enters Devil Square event
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player entering event |
EventLevel |
int | Devil Square level (1-7) |
Usage:
function onDevilSquareEnter(oPlayer, EventLevel)
if oPlayer ~= nil then
Log.Add(string.format("%s entered Devil Square Level %d", oPlayer.Name, EventLevel))
end
endfunction onPlayerLevelUp(oPlayer)
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Async
Called when: Player gains a level
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player that leveled up |
Usage:
function onPlayerLevelUp(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("%s reached level %d", oPlayer.Name, oPlayer.Level))
-- Award level milestone rewards
if oPlayer.Level == 400 then
Message.Send(0, -1, 1, string.format("%s reached level 400!", oPlayer.Name))
Player.SetMoney(oPlayer.Index, 10000000, false)
end
end
endfunction onPlayerMasterLevelUp(oPlayer)Type: Async
Called when: Player gains a master level
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player that gained master level |
Usage:
function onPlayerMasterLevelUp(oPlayer)
if oPlayer ~= nil then
local mlevel = oPlayer.userData.MasterLevel
Log.Add(string.format("%s reached master level %d", oPlayer.Name, mlevel))
if mlevel == 200 then
Message.Send(0, -1, 1, string.format("%s achieved ML 200!", oPlayer.Name))
end
end
endfunction onUseCommand(oPlayer, szCmd)Type: Sync (can prevent command execution)
Called when: Player types a command (any string starting with /)
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player who typed the command |
szCmd |
string | Full command string as typed, including the leading / (e.g. "/post Hello World") |
Return value:
| Return | Effect |
|---|---|
1 |
Block command - C++ stops processing it |
0 or nil
|
Allow command - C++ continues normal execution |
Notes:
-
szCmdis the raw string. Split it into parts in Lua usingstring.gmatch(szCmd, "%S+"). - The first part (
parts[1]) is the command name including the/prefix. - Remaining parts are arguments in order.
Usage:
function onUseCommand(oPlayer, szCmd)
if oPlayer == nil then return 0 end
-- Split into parts: "/post Hello World" -> {"/post", "Hello", "World"}
local parts = {}
for part in string.gmatch(szCmd, "%S+") do
table.insert(parts, part)
end
local cmd = parts[1] or ""
-- Example: custom /online command
if cmd == "/online" then
local count = 0
Object.ForEachPlayer(function(oP)
count = count + 1
return true
end)
Message.Send(0, oPlayer.Index, 0, string.format("Players online: %d", count))
return 1 -- block default handler
-- Example: block non-GM players from using /move
elseif cmd == "/move" then
if oPlayer.GameMaster == 0 then
Message.Send(0, oPlayer.Index, 0, "You don't have permission to use that command.")
Log.Add(string.format("[CMD] %s tried to use %s", oPlayer.Name, szCmd))
return 1 -- block
end
end
return 0 -- allow all other commands
endfunction onPlayerReset(oPlayer)
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Sync
Called when: Player performs character reset
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player performing reset |
Usage:
function onPlayerReset(oPlayer)
if oPlayer ~= nil then
local resets = oPlayer.userData.Resets + 1
Log.Add(string.format("%s performed reset #%d", oPlayer.Name, resets))
-- Award reset rewards
Message.Send(0, oPlayer.Index, 1, string.format("Reset #%d complete!", resets))
end
endfunction onNpcTalk(oPlayer, oNpc)Type: Sync
Called when: Player talks to NPC
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player talking to NPC |
oNpc |
object (stObject) | NPC object (same structure as player/monster) |
Return value:
| Return | Effect |
|---|---|
1 |
Stop processing further C++ code |
0 or nil
|
Allow command - C++ continues normal execution |
Usage:
function onNpcTalk(oPlayer, oNpc)
if oPlayer ~= nil and oNpc ~= nil then
Log.Add(string.format("%s talked to NPC %d", oPlayer.Name, oNpc.Class))
-- Custom NPC dialog
if oNpc.Class == 257 then -- Custom NPC
Message.Send(0, oPlayer.Index, 0, "Welcome to my shop!")
end
end
return 0
endfunction onCloseWindow(oPlayer)Type: Sync
Called when: Player end conversation with NPC by closing NPC window
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player talking to NPC |
Return value:
| Return | Effect |
|---|---|
1 |
Stop processing further C++ code |
0 or nil
|
Allow command - C++ continues normal execution |
Usage:
function onCloseWindow(oPlayer)
if oPlayer ~= nil then
end
return 0
endfunction onItemUse(iResult, oPlayer, oItem, iItemSourcePos, iItemTargetPos)Type: Sync
Called when: Player uses right-clickable item
Parameters:
| Parameter | Type | Description |
|---|---|---|
iResult |
int | Use result (-1 = error and inventory unlock, 1 = success and stop C++ code execution, 0 = success and continue C++ code execution) |
oPlayer |
object (stObject) | Player moving item |
oItem |
object (stItemInfo) | Item being clicked |
iItemSourcePos |
int | Source inventory slot |
iItemTargetPos |
int | Target inventory slot |
function onInventoryMoveItem(oPlayer, iItemSourcePos, iItemTargetPos, btResult)Type: Async
Called when: Player moves item in main inventory
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player moving item |
iItemSourcePos |
int | Source inventory slot |
iItemTargetPos |
int | Target inventory slot |
btResult |
int | Move result (1 = success, 0 = failed) |
Usage:
function onInventoryMoveItem(oPlayer, iItemSourcePos, iItemTargetPos, btResult)
if oPlayer ~= nil and btResult == 1 then
Log.Add(string.format("%s moved item from slot %d to %d",
oPlayer.Name, iItemSourcePos, iItemTargetPos))
end
endfunction onEventInventoryMoveItem(oPlayer, iItemSourcePos, iItemTargetPos, btResult)
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Async
Called when: Player moves item in event inventory
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player moving item |
iItemSourcePos |
int | Source event inventory slot |
iItemTargetPos |
int | Target event inventory slot |
btResult |
int | Move result (1 = success, 0 = failed) |
Usage:
function onEventInventoryMoveItem(oPlayer, iItemSourcePos, iItemTargetPos, btResult)
if oPlayer ~= nil and btResult == 1 then
Log.Add(string.format("%s moved event item %d -> %d",
oPlayer.Name, iItemSourcePos, iItemTargetPos))
end
endfunction onMuunInventoryMoveItem(oPlayer, iItemSourcePos, iItemTargetPos, btResult)
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Async
Called when: Player moves Muun item in inventory
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player moving Muun item |
iItemSourcePos |
int | Source Muun inventory slot |
iItemTargetPos |
int | Target Muun inventory slot |
btResult |
int | Move result (1 = success, 0 = failed) |
Usage:
function onMuunInventoryMoveItem(oPlayer, iItemSourcePos, iItemTargetPos, btResult)
if oPlayer ~= nil and btResult == 1 then
Log.Add(string.format("%s moved Muun item %d -> %d",
oPlayer.Name, iItemSourcePos, iItemTargetPos))
end
endfunction onItemGet(oPlayer, sItemType, sItemLevel, btItemDur, btItemElement)Type: Async
Called when: Player picks up item from ground
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player picking up item |
sItemType |
short | Item type ID |
sItemLevel |
short | Item level |
btItemDur |
BYTE | Item durability |
btItemElement |
BYTE | Item element type |
Usage:
function onItemGet(oPlayer, sItemType, sItemLevel, btItemDur, btItemElement)
if oPlayer ~= nil then
local itemAttr = Item.GetAttr(sItemType)
if itemAttr ~= nil then
local itemName = itemAttr:GetName()
Log.Add(string.format("%s picked up: %s +%d",
oPlayer.Name, itemName, sItemLevel))
end
end
endfunction onEventItemGet(oPlayer, sItemType, sItemLevel, btItemDur, btItemElement)
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Async
Called when: Player acquires event item
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player acquiring item |
sItemType |
short | Item type ID |
sItemLevel |
short | Item level |
btItemDur |
BYTE | Item durability |
btItemElement |
BYTE | Item element type |
Usage:
function onEventItemGet(oPlayer, sItemType, sItemLevel, btItemDur, btItemElement)
if oPlayer ~= nil then
Log.Add(string.format("%s got event item: %d +%d",
oPlayer.Name, sItemType, sItemLevel))
end
endfunction onMuunItemGet(oPlayer, sItemType, sItemLevel, btItemDur, btItemElement)
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Async
Called when: Player acquires Muun item
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player acquiring Muun |
sItemType |
short | Item type ID |
sItemLevel |
short | Item level |
btItemDur |
BYTE | Item durability |
btItemElement |
BYTE | Item element type |
Usage:
function onMuunItemGet(oPlayer, sItemType, sItemLevel, btItemDur, btItemElement)
if oPlayer ~= nil then
Log.Add(string.format("%s acquired Muun: %d +%d",
oPlayer.Name, sItemType, sItemLevel))
end
endfunction onItemEquip(oPlayer, iItemSourcePos, iItemTargetPos, btResult)Type: Async
Called when: Player equips item
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player equipping item |
iItemSourcePos |
int | Source inventory slot |
iItemTargetPos |
int | Equipment slot |
btResult |
int | Equip result (1 = success, 0 = failed) |
Usage:
function onItemEquip(oPlayer, iItemSourcePos, iItemTargetPos, btResult)
if oPlayer ~= nil and btResult == 1 then
local item = oPlayer:GetInventoryItem(iItemTargetPos)
if item ~= nil then
Log.Add(string.format("%s equipped item in slot %d",
oPlayer.Name, iItemTargetPos))
-- Recalculate stats after equipment change
Player.ReCalc(oPlayer.Index)
end
end
endfunction onItemUnEquip(oPlayer, iItemSourcePos, iItemTargetPos, btResult)Type: Async
Called when: Player unequips item
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player unequipping item |
iItemSourcePos |
int | Equipment slot |
iItemTargetPos |
int | Target inventory slot |
btResult |
int | Unequip result (1 = success, 0 = failed) |
Usage:
function onItemUnEquip(oPlayer, iItemSourcePos, iItemTargetPos, btResult)
if oPlayer ~= nil and btResult == 1 then
Log.Add(string.format("%s unequipped item from slot %d",
oPlayer.Name, iItemSourcePos))
-- Recalculate stats
Player.ReCalc(oPlayer.Index)
end
endfunction onItemRepair(oPlayer, oItem)Type: Async
Called when: Player repairs item at NPC
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player repairing item |
oItem |
object (ItemInfo) | Item being repaired |
Usage:
function onItemRepair(oPlayer, oItem)
if oPlayer ~= nil and oItem ~= nil then
local itemAttr = Item.GetAttr(oItem.Type)
if itemAttr ~= nil then
local itemName = itemAttr:GetName()
Log.Add(string.format("%s repaired: %s", oPlayer.Name, itemName))
end
end
endfunction onCharacterJoinMap(oPlayer)Type: Async
Called when: Player joins map after login or teleport
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player joining map |
Usage:
function onCharacterJoinMap(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("%s joined map %d at (%d, %d)",
oPlayer.Name, oPlayer.MapNumber, oPlayer.X, oPlayer.Y))
-- Check for map-specific events
if oPlayer.MapNumber == 10 then -- Devias
Message.Send(0, oPlayer.Index, 0, "Welcome to Devias!")
end
end
endfunction onMoveMap(oPlayer, wMapNumber, btPosX, btPosY, iGateNumber)Type: Async
Called when: Player moves between maps
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player moving |
wMapNumber |
WORD | Target map number |
btPosX |
BYTE | Target X coordinate |
btPosY |
BYTE | Target Y coordinate |
iGateNumber |
int | Gate number used (-1 if not gate) |
Usage:
function onMoveMap(oPlayer, wMapNumber, btPosX, btPosY, iGateNumber)
if oPlayer ~= nil then
Log.Add(string.format("%s moved to map %d (%d, %d) via gate %d",
oPlayer.Name, wMapNumber, btPosX, btPosY, iGateNumber))
end
endfunction onMapTeleport(oPlayer, iGateNumber)
⚠️ Not available in s6 - This callback does not exist in the s6 build. It fires when a gate teleport completes without a full map transition (same-map gate or instant warp). In s6 this code path has no Lua callback. Gate teleports with a full map change fireonMoveMapin both s6 and s7+.
Type: Async
Called when: Player uses map gate/portal
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player using gate |
iGateNumber |
int | Gate index |
Usage:
function onMapTeleport(oPlayer, iGateNumber)
if oPlayer ~= nil then
Log.Add(string.format("%s used gate %d", oPlayer.Name, iGateNumber))
-- Check gate cooldown
local lastGateTick = oPlayer.ActionTickCount[1].tick
if (Timer.GetTick() - lastGateTick) < 5000 then
Message.Send(0, oPlayer.Index, 0, "Gate cooldown: 5 seconds")
else
ActionTick.Set(oPlayer.Index, 1, Timer.GetTick(), "Gate")
end
end
endfunction onTeleport(oPlayer, wMapNumber, btPosX, btPosY)Type: Async
Called when: Player uses teleport command or item
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player teleporting |
wMapNumber |
WORD | Target map number |
btPosX |
BYTE | Target X coordinate |
btPosY |
BYTE | Target Y coordinate |
Usage:
function onTeleport(oPlayer, wMapNumber, btPosX, btPosY)
if oPlayer ~= nil then
Log.Add(string.format("%s teleported to map %d (%d, %d)",
oPlayer.Name, wMapNumber, btPosX, btPosY))
end
endfunction onTeleportMagicUse(oPlayer, btPosX, btPosY)Type: Async
Called when: Player uses teleport magic skill
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player using teleport skill |
btPosX |
BYTE | Target X coordinate |
btPosY |
BYTE | Target Y coordinate |
Usage:
function onTeleportMagicUse(oPlayer, btPosX, btPosY)
if oPlayer ~= nil then
Log.Add(string.format("%s teleported to (%d, %d) using magic",
oPlayer.Name, btPosX, btPosY))
end
endfunction onCheckUserTarget(oPlayer, oTarget)Type: Sync (can block targeting)
Called when: A player attempts to attack any target - another player (PvP) or a monster
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Attacking player |
oTarget |
object (stObject) | Target (player or monster) |
Return value:
| Return | Effect |
|---|---|
1 (or any non-zero) |
Block - attack/targeting is denied |
0 or nil
|
Allow - attack proceeds normally |
Notes:
- Fires before the attack is processed, allowing full prevention
- Fires for both player vs. player and player vs. monster
-
oTargetmay benilif target index is<= 0- always nil-check before use - Check
oTarget.Typeto distinguish between player (Enums.ObjectType.USER) and monster (Enums.ObjectType.MONSTER) targets
Usage:
function onCheckUserTarget(oPlayer, oTarget)
if oPlayer == nil or oTarget == nil then return 0 end
-- PvP only: block attacks on players below level 50
if oTarget.Type == Enums.ObjectType.USER and oTarget.Level < 50 then
Message.Send(0, oPlayer.Index, 0, "You cannot attack players below level 50.")
return 1
end
-- Block friendly-fire within same guild
if oTarget.Type == Enums.ObjectType.USER and oPlayer.GuildNumber > 0 and oPlayer.GuildNumber == oTarget.GuildNumber then
Message.Send(0, oPlayer.Index, 0, "You cannot attack guild members.")
return 1
end
return 0
endfunction onPlayerKill(oPlayer, oTarget)Type: Async
Called when: Player kills another player
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player who killed |
oTarget |
object (stObject) | Player who was killed |
Usage:
function onPlayerKill(oPlayer, oTarget)
if oPlayer ~= nil and oTarget ~= nil then
Log.Add(string.format("%s killed %s", oPlayer.Name, oTarget.Name))
-- Award PK points or penalties
Message.Send(0, -1, 0, string.format("%s killed %s!",
oPlayer.Name, oTarget.Name))
end
endfunction onPlayerDie(oPlayer, oTarget)Type: Async
Called when: Player dies
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player who died |
oTarget |
Player/Monster | Object that killed player |
Usage:
function onPlayerDie(oPlayer, oTarget)
if oPlayer ~= nil then
if oTarget ~= nil then
Log.Add(string.format("%s was killed by %s",
oPlayer.Name, oTarget.Name or "Monster"))
else
Log.Add(string.format("%s died", oPlayer.Name))
end
-- Clear player timers on death
for i = 0, 2 do
ActionTick.Clear(oPlayer.Index, i)
end
end
endfunction onPlayerRespawn(oPlayer)Type: Async
Called when: Player respawns after death
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player respawning |
Usage:
function onPlayerRespawn(oPlayer)
if oPlayer ~= nil then
Log.Add(string.format("%s respawned at map %d",
oPlayer.Name, oPlayer.MapNumber))
Message.Send(0, oPlayer.Index, 0, "You have respawned!")
end
endfunction onMonsterKill(oPlayer, oTarget)Type: Async
Called when: Player kills monster
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player who killed monster |
oTarget |
Monster | Monster that was killed |
Usage:
function onMonsterKill(oPlayer, oTarget)
if oPlayer ~= nil and oTarget ~= nil then
Log.Add(string.format("%s killed monster %d",
oPlayer.Name, oTarget.Class))
-- Award bonus for specific monsters
if oTarget.Class == 275 then -- Golden Goblin
Player.SetMoney(oPlayer.Index, 1000000, false)
Message.Send(0, oPlayer.Index, 1, "Golden Goblin bonus: 1kk zen!")
end
end
endfunction onMonsterDie(oPlayer, oTarget)Type: Async
Called when: Monster dies
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player who killed (may be nil) |
oTarget |
Monster | Monster that died |
Usage:
function onMonsterDie(oPlayer, oTarget)
if oTarget ~= nil then
Log.Add(string.format("Monster %d died", oTarget.Class))
if oPlayer ~= nil then
Log.Add(string.format("Killed by: %s", oPlayer.Name))
end
end
endfunction onMonsterSpawn(oMonster)
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Async
Called when: Monster spawns on map
Parameters:
| Parameter | Type | Description |
|---|---|---|
oMonster |
object (stObject) | Monster that spawned |
Usage:
function onMonsterSpawn(oMonster)
if oMonster ~= nil then
Log.Add(string.format("Monster %d spawned at map %d",
oMonster.Class, oMonster.MapNumber))
end
endfunction onMonsterRespawn(oMonster)Type: Async
Called when: Monster respawns after death
Parameters:
| Parameter | Type | Description |
|---|---|---|
oMonster |
object (stObject) | Monster that respawned |
Usage:
function onMonsterRespawn(oMonster)
if oMonster ~= nil then
Log.Add(string.format("Monster %d respawned", oMonster.Class))
end
endfunction onUseDurationSkill(oPlayer, aTargetIndex, iSkill, btX, btY, btDir)Type: Sync (can prevent action)
Called when: Player uses a duration-based skill
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player using the skill |
aTargetIndex |
integer | Object index of the target |
iSkill |
integer | Skill number |
btX |
integer | Target X coordinate |
btY |
integer | Target Y coordinate |
btDir |
integer | Direction |
Return value: Return non-zero to block the skill.
Usage:
function onUseDurationSkill(oPlayer, aTargetIndex, iSkill, btX, btY, btDir)
if oPlayer ~= nil then
Log.Add(string.format("%s used duration skill %d on target %d", oPlayer.Name, iSkill, aTargetIndex))
end
return 0 -- allow
endfunction onUseNormalSkill(oPlayer, oTarget, iSkill)Type: Sync (can prevent action)
Called when: Player uses a normal (instant) skill on a target
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player using the skill |
oTarget |
object (stObject) | Target of the skill |
iSkill |
integer | Skill number |
Return value: Return non-zero to block the skill.
Usage:
function onUseNormalSkill(oPlayer, oTarget, iSkill)
if oPlayer ~= nil and oTarget ~= nil then
Log.Add(string.format("%s used skill %d on %s", oPlayer.Name, iSkill, oTarget.Name))
end
return 0 -- allow
endfunction onShopBuyItem(oPlayer, oItem)Type: Sync (can prevent purchase)
Called when: Player buys item from NPC shop
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player buying item |
oItem |
object (ItemInfo) | Item being purchased |
Usage:
function onShopBuyItem(oPlayer, oItem)
if oPlayer ~= nil and oItem ~= nil then
local itemAttr = Item.GetAttr(oItem.Type)
if itemAttr ~= nil then
local itemName = itemAttr:GetName()
Log.Add(string.format("%s bought: %s", oPlayer.Name, itemName))
end
end
endfunction onShopSellItem(oPlayer, oItem)Type: Sync (can prevent sale)
Called when: Player sells item to NPC shop
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player selling item |
oItem |
object (ItemInfo) | Item being sold |
Usage:
function onShopSellItem(oPlayer, oItem)
if oPlayer ~= nil and oItem ~= nil then
local itemAttr = Item.GetAttr(oItem.Type)
if itemAttr ~= nil then
local itemName = itemAttr:GetName()
Log.Add(string.format("%s sold: %s", oPlayer.Name, itemName))
end
end
endfunction onShopSellEventItem(oPlayer, oItem)
⚠️ Not available in s6 - This callback does not exist in the s6 build.
Type: Async
Called when: Player sells event item to NPC
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player selling event item |
oItem |
object (ItemInfo) | Event item being sold |
Usage:
function onShopSellEventItem(oPlayer, oItem)
if oPlayer ~= nil and oItem ~= nil then
Log.Add(string.format("%s sold event item: %d",
oPlayer.Name, oItem.Type))
end
endfunction onMossMerchantUse(oPlayer, iSectionId)Type: Sync (can prevent action)
Called when: Player interacts with Moss the Merchant
Parameters:
| Parameter | Type | Description |
|---|---|---|
oPlayer |
object (stObject) | Player using Moss |
iSectionId |
integer | Section/action ID selected by the player |
Return value: Return non-zero to block the action.
Usage:
function onMossMerchantUse(oPlayer, iSectionId)
if oPlayer ~= nil then
Log.Add(string.format("%s used Moss section %d", oPlayer.Name, iSectionId))
end
return 0 -- allow
endfunction onDSDBQueryReceive(iUserIndex, iQueryNumber, btIsLastPacket, iCurrentRow, btColumnCount, btCurrentPacket, oRow)Type: Async
Called when: DataServer database query result is received
Parameters:
| Parameter | Type | Description |
|---|---|---|
iUserIndex |
int | Player index who initiated query |
iQueryNumber |
int | Query identifier number |
btIsLastPacket |
bool |
1 if last packet, 0 if more coming |
iCurrentRow |
int | Current row number in packet |
btColumnCount |
int | Total columns in result |
btCurrentPacket |
int | Current packet number |
oRow |
object (LuaQueryResultDS) | Query result row object |
Usage:
function onDSDBQueryReceive(iUserIndex, iQueryNumber, btIsLastPacket, iCurrentRow, btColumnCount, btCurrentPacket, oRow)
if oRow ~= nil then
local columnName = oRow:GetColumnName()
local value = oRow:GetValue()
Log.Add(string.format("Query %d: %s = %s", iQueryNumber, columnName, value))
if btIsLastPacket == 1 then
Log.Add(string.format("Query %d complete", iQueryNumber))
end
end
endSee: Database-Structures for detailed examples
function onJSDBQueryReceive(iUserIndex, iQueryNumber, btIsLastPacket, iCurrentRow, btColumnCount, btCurrentPacket, oRow)Type: Async
Called when: JoinServer database query result is received
Parameters:
| Parameter | Type | Description |
|---|---|---|
iUserIndex |
int | Player index who initiated query |
iQueryNumber |
int | Query identifier number |
btIsLastPacket |
bool |
1 if last packet, 0 if more coming |
iCurrentRow |
int | Current row number in packet |
btColumnCount |
int | Total columns in result |
btCurrentPacket |
int | Current packet number |
oRow |
object (LuaQueryResultJS) | Query result row object |
Usage:
function onJSDBQueryReceive(iUserIndex, iQueryNumber, btIsLastPacket, iCurrentRow, btColumnCount, btCurrentPacket, oRow)
if oRow ~= nil then
local columnName = oRow:GetColumnName()
local value = oRow:GetValue()
Log.Add(string.format("Query %d: %s = %s", iQueryNumber, columnName, value))
if btIsLastPacket == 1 then
Log.Add(string.format("Query %d complete", iQueryNumber))
end
end
endSee: Database-Structures for detailed examples
function onPlayerLogin(oPlayer)
if oPlayer ~= nil then
-- Safe to access player
end
endfunction onShopBuyItem(oPlayer, oItem)
-- ✅ Quick validation
if oPlayer == nil or oItem == nil then
return
end
-- ❌ Don't do heavy processing
-- ❌ Don't call database queries
-- ❌ Don't iterate thousands of items
endfunction onPlayerLogin(oPlayer)
if oPlayer ~= nil then
-- ✅ Async - safe for DB queries
DB.QueryDS(oPlayer.Index, 1001,
string.format("SELECT * FROM CustomData WHERE CharName = '%s'", oPlayer.Name))
end
endfunction onPlayerDisconnect(oPlayer)
if oPlayer ~= nil then
-- Clear timers
for i = 0, 2 do
ActionTick.Clear(oPlayer.Index, i)
end
-- Remove from global tables
g_ActivePlayers[oPlayer.Index] = nil
g_QuestData[oPlayer.Index] = nil
end
end-- Send query
function onPlayerLogin(oPlayer)
DB.QueryDS(oPlayer.Index, 1, string.format("SELECT Level FROM Character WHERE Name = '%s'", oPlayer.Name))
end
-- Handle results
function onDSDBQueryReceive(iUserIndex, iQueryNumber, btIsLastPacket, iCurrentRow, btColumnCount, btCurrentPacket, oRow)
if iQueryNumber == 1 and oRow ~= nil then
local columnName = oRow:GetColumnName()
local value = oRow:GetValue()
if columnName == "Level" then
Log.Add(string.format("Player level: %s", value))
end
end
end- PLAYER - Player object structure
- ITEM - Item structures
- DATABASE - Database query structures
- GLOBAL_FUNCTIONS - All server functions
MuLua Scripting Plugin | Home
Version 1.5 | 2026-05-06 14:10 GMT+2