Advanced Player Logging System: Complete Monitoring in MU
Set up a complete player action logging system in MU Online: SQL tables, stored procedures, automatic alerts, and admin panel integration.
Why an Advanced Logging System is Essential
On MU Online private servers, the absence of detailed logs makes it impossible to investigate item dupes, player disputes, and exploits. MuServer's default system records basic events, but with proper configuration you can track every relevant action — from an Excellent item drop to a speed hack attempt — with a precise timestamp and the player's IP address.
This tutorial covers the complete stack: GameServer configuration, dedicated SQL table creation, logging stored procedures, SQL Agent Jobs for automatic alerts, and data retention best practices.
Step 1: Enable Native GameServer Logs
The central configuration file is located at GameServer/Data/GS_Config.ini. Open it in any text editor and locate or add the following keys under the [GameServer] section:
[GameServer]
; --- Logging ---
LogEnable=1
LogLevel=3
; 1=basic | 2=medium | 3=full (recommended for admins)
TradeLog=1
TradeLogLevel=2
DropLog=1
DropLogLevel=2
ChatLog=1
ChatLogLevel=1
; Level 1 = public chat only, 2 = includes whisper
HackLog=1
PKLog=1
; Text log output directory
LogPath=./Log/
After saving, restart the GameServer. Log files will be generated in GameServer/Log/ with the following naming convention:
GS_YYYYMMDD.log— general logTrade_YYYYMMDD.log— player-to-player tradesDrop_YYYYMMDD.log— items dropped on the groundHack_YYYYMMDD.log— security events
Step 2: Create SQL Tables for Structured Logging
Text file logs are hard to query. The solution is to mirror critical events into SQL tables. Connect to the MuOnline database via SQL Server Management Studio and execute:
USE MuOnline;
GO
-- Item drop log table
CREATE TABLE Log_ItemDrop (
LogID BIGINT IDENTITY(1,1) PRIMARY KEY,
LogDate DATETIME DEFAULT GETDATE(),
CharName VARCHAR(10) NOT NULL,
AccountID VARCHAR(10) NOT NULL,
MapNumber TINYINT NOT NULL,
MapX SMALLINT NOT NULL,
MapY SMALLINT NOT NULL,
ItemIndex SMALLINT NOT NULL,
ItemLevel TINYINT NOT NULL,
ItemOption TINYINT NOT NULL, -- 0=normal, 4=excellent
ItemSerial BIGINT NOT NULL,
DropType TINYINT NOT NULL, -- 1=floor, 2=trade, 3=warehouse
PlayerIP VARCHAR(20)
);
GO
-- Trade log table
CREATE TABLE Log_Trade (
LogID BIGINT IDENTITY(1,1) PRIMARY KEY,
LogDate DATETIME DEFAULT GETDATE(),
CharGive VARCHAR(10) NOT NULL,
CharReceive VARCHAR(10) NOT NULL,
ItemIndex SMALLINT NOT NULL,
ItemLevel TINYINT NOT NULL,
ItemOption TINYINT NOT NULL,
ItemSerial BIGINT NOT NULL,
ZenAmount INT DEFAULT 0
);
GO
-- Admin panel alerts table
CREATE TABLE T_AdminAlerts (
AlertID INT IDENTITY(1,1) PRIMARY KEY,
AlertDate DATETIME DEFAULT GETDATE(),
AlertType VARCHAR(30) NOT NULL,
CharName VARCHAR(10),
Description VARCHAR(500),
IsRead BIT DEFAULT 0
);
GO
MEMB_INFO and Character tables in your database to confirm the schema.Step 3: Stored Procedures for Log Insertion
Create procedures that the GameServer calls via ODBC (in versions that support SQL extension) or that you invoke via triggers. For servers using native SQL log extension:
USE MuOnline;
GO
CREATE PROCEDURE WZ_InsertItemDropLog
@CharName VARCHAR(10),
@AccountID VARCHAR(10),
@MapNumber TINYINT,
@MapX SMALLINT,
@MapY SMALLINT,
@ItemIndex SMALLINT,
@ItemLevel TINYINT,
@ItemOption TINYINT,
@ItemSerial BIGINT,
@DropType TINYINT,
@PlayerIP VARCHAR(20)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO Log_ItemDrop
(CharName, AccountID, MapNumber, MapX, MapY,
ItemIndex, ItemLevel, ItemOption, ItemSerial, DropType, PlayerIP)
VALUES
(@CharName, @AccountID, @MapNumber, @MapX, @MapY,
@ItemIndex, @ItemLevel, @ItemOption, @ItemSerial, @DropType, @PlayerIP);
END;
GO
CREATE PROCEDURE WZ_InsertTradeLog
@CharGive VARCHAR(10),
@CharReceive VARCHAR(10),
@ItemIndex SMALLINT,
@ItemLevel TINYINT,
@ItemOption TINYINT,
@ItemSerial BIGINT,
@ZenAmount INT = 0
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO Log_Trade
(CharGive, CharReceive, ItemIndex, ItemLevel, ItemOption, ItemSerial, ZenAmount)
VALUES
(@CharGive, @CharReceive, @ItemIndex, @ItemLevel, @ItemOption, @ItemSerial, @ZenAmount);
END;
GO
Step 4: SQL Agent Job for Suspicious Item Detection
Configure a SQL Server Agent Job that runs every 5 minutes and automatically detects drops of high-level or rare items:
- Open SQL Server Management Studio
- Expand SQL Server Agent → right-click Jobs → New Job
- Name:
MU_DetectSuspiciousDrops - Under Steps → New Step, paste the following script:
USE MuOnline;
GO
-- Detect drops of Level 13+ or Excellent items in the last 5 minutes
INSERT INTO T_AdminAlerts (AlertType, CharName, Description)
SELECT
'SUSPICIOUS_DROP',
d.CharName,
'Drop: Item=' + CAST(d.ItemIndex AS VARCHAR) +
' Lv=' + CAST(d.ItemLevel AS VARCHAR) +
' Opt=' + CAST(d.ItemOption AS VARCHAR) +
' Map=' + CAST(d.MapNumber AS VARCHAR) +
' Pos=(' + CAST(d.MapX AS VARCHAR) + ',' + CAST(d.MapY AS VARCHAR) + ')' +
' IP=' + ISNULL(d.PlayerIP, 'N/A')
FROM Log_ItemDrop d
WHERE d.LogDate >= DATEADD(MINUTE, -5, GETDATE())
AND (d.ItemLevel >= 13 OR d.ItemOption >= 4);
-- Detect trades with Level 15+ items (possible dupe)
INSERT INTO T_AdminAlerts (AlertType, CharName, Description)
SELECT
'TRADE_LEVEL15',
t.CharGive,
'Trade: ' + t.CharGive + ' -> ' + t.CharReceive +
' Item=' + CAST(t.ItemIndex AS VARCHAR) +
' Lv=' + CAST(t.ItemLevel AS VARCHAR)
FROM Log_Trade t
WHERE t.LogDate >= DATEADD(MINUTE, -5, GETDATE())
AND t.ItemLevel >= 15;
- Under Schedules → New Schedule: Frequency = Daily, Recurs every 5 minutes
- Click OK to save the Job
msdb.dbo.sp_send_dbmail with the contents of unread T_AdminAlerts rows.Step 5: Batch Script to Archive Old Logs
Logs accumulate quickly. Create the file GameServer/LogArchive.bat to compress and move logs older than 30 days:
@echo off
REM File: GameServer/LogArchive.bat
REM Schedule in Windows Task Scheduler - daily at 03:00
set LOG_DIR=C:\MuServer\GameServer\Log
set ARCHIVE_DIR=C:\MuServer\GameServer\Log\Archive
set DAYS_OLD=30
if not exist "%ARCHIVE_DIR%" mkdir "%ARCHIVE_DIR%"
REM Move old logs to the archive folder
forfiles /p "%LOG_DIR%" /s /m *.log /d -%DAYS_OLD% /c "cmd /c move @path %ARCHIVE_DIR%\"
REM Compress moved files (requires 7-Zip installed at C:\Program Files\7-Zip\)
"C:\Program Files\7-Zip\7z.exe" a "%ARCHIVE_DIR%\logs_%date:~10,4%%date:~4,2%%date:~7,2%.7z" "%ARCHIVE_DIR%\*.log" -sdel
echo Archiving complete: %date% %time%
forfiles command.Step 6: Useful SQL Queries for Investigations
Use these queries in your day-to-day operations to investigate player reports:
-- Full history of a player in the last 24 hours
SELECT LogDate, DropType, ItemIndex, ItemLevel, ItemOption,
MapNumber, MapX, MapY, PlayerIP
FROM Log_ItemDrop
WHERE CharName = 'PlayerName'
AND LogDate >= DATEADD(HOUR, -24, GETDATE())
ORDER BY LogDate DESC;
-- Check if an item serial appears on multiple players (dupe detection)
SELECT CharName, LogDate, DropType
FROM Log_ItemDrop
WHERE ItemSerial = 123456789
ORDER BY LogDate;
-- Players with the highest volume of rare item drops (possible bot farm)
SELECT CharName, COUNT(*) AS TotalDrops
FROM Log_ItemDrop
WHERE ItemLevel >= 10
AND LogDate >= DATEADD(DAY, -7, GETDATE())
GROUP BY CharName
ORDER BY TotalDrops DESC;
-- Unread alerts for the admin panel
SELECT AlertDate, AlertType, CharName, Description
FROM T_AdminAlerts
WHERE IsRead = 0
ORDER BY AlertDate DESC;
-- Mark alerts as read after review
UPDATE T_AdminAlerts SET IsRead = 1
WHERE IsRead = 0 AND AlertDate < DATEADD(HOUR, -1, GETDATE());
Maintenance and Data Retention
Keeping logs indefinitely consumes disk space and degrades query performance. Configure a cleanup routine in SQL Server Agent:
-- Monthly cleanup job: delete logs older than 90 days
DELETE FROM Log_ItemDrop WHERE LogDate < DATEADD(DAY, -90, GETDATE());
DELETE FROM Log_Trade WHERE LogDate < DATEADD(DAY, -90, GETDATE());
DELETE FROM T_AdminAlerts WHERE AlertDate < DATEADD(DAY, -30, GETDATE()) AND IsRead = 1;
MuOnline_Archive. This preserves history without weighing down the main database.Common Troubleshooting
GameServer is not writing text logs: Verify that the GameServer/Log/ folder exists and that the GameServer process has write permission to it. On Windows Server, the user running GameServer.exe needs Modify permission on the Log folder.
Stored procedure is not being called: On MuServer versions that do not support native SQL extension for logs, you will need middleware or a script that parses the text files and inserts new lines into SQL every minute. Create a PowerShell script that reads GS_YYYYMMDD.log and inserts new entries into the database on a scheduled task.
Log_ItemDrop table has grown too large: Run DBCC SHRINKFILE after cleanup and rebuild the index: ALTER INDEX ALL ON Log_ItemDrop REBUILD. This reclaims space and maintains query performance.
Perguntas frequentes
Where does the GameServer write logs by default?
The GameServer writes text logs to GameServer/Log/, with files separated by date in the format GS_YYYYMMDD.log. Each line contains a timestamp, event type, and player data.
How do I enable item trade logging between players?
In GameServer/Data/GS_Config.ini, set TradeLog=1 and TradeLogLevel=2 to record all traded items including their serial code, level, and options.
Is it possible to log hack attempts and exploit tries?
Yes. Set HackLog=1 in GS_Config.ini. Packet injection attempts, speed hacks, and invalid item use are recorded in the Log_HackCheck table in the MuOnline database, including the player's IP address.
How do I create automatic alerts when a player drops rare items?
Create a SQL Job in SQL Server Agent that runs every 5 minutes querying Log_ItemDrop where ItemLevel >= 13 or ItemOption = 4 (excellent), then inserts alerts into the T_AdminAlerts table for display in the web admin panel.