Brazil's biggest MU Online portal — since 2003
Tutorial Advanced Tutoriais

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.

VI ViciadosMU Team · Updated on 3 jul 2026 · ⏱ 12 min read

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/
Atenção: LogLevel=3 increases disk I/O volume. On a VPS with an HDD, monitor I/O usage after enabling this. On SSD there is no noticeable impact for servers with up to 500 simultaneous players.

After saving, restart the GameServer. Log files will be generated in GameServer/Log/ with the following naming convention:

  • GS_YYYYMMDD.log — general log
  • Trade_YYYYMMDD.log — player-to-player trades
  • Drop_YYYYMMDD.log — items dropped on the ground
  • Hack_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
Nota: The table and column names above follow the MuServer Season 6 EP3 convention. In Season 9+ versions some column names may differ — verify the 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:

  1. Open SQL Server Management Studio
  2. Expand SQL Server Agent → right-click JobsNew Job
  3. Name: MU_DetectSuspiciousDrops
  4. Under StepsNew 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;
  1. Under SchedulesNew Schedule: Frequency = Daily, Recurs every 5 minutes
  2. Click OK to save the Job
Dica: To receive these alerts by email, configure Database Mail in SQL Server and add a step to the Job that executes 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%
Atenção: Verify the 7-Zip path on your server before executing this script. If 7-Zip is not installed, remove the compression line and keep only the 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;
Dica: Before deleting, back up the log tables to a separate database called 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.

VI

ViciadosMU Team

Equipe editorial do ViciadosMU — portal de MU Online no ar desde 2003.

Keep reading

Related articles