How to Add VIP and Bonus Systems to Your MU Online Server
Learn how to configure VIP ranks and bonus systems on your MU Online private server — from database setup to in-game rewards and automated expiration.
Introduction: Why VIP and Bonus Systems Matter
A well-designed VIP and bonus system gives server administrators two powerful tools: a way to reward dedicated players and a mechanism to keep the economy balanced through controlled benefit tiers. Unlike direct stat boosts handed out arbitrarily, a structured VIP system is transparent, auditable, and easy to modify without touching core game logic.
This guide covers the full implementation pipeline — database schema, server-side configuration files, and the in-game command hooks that glue everything together. It assumes you are running a Season 6 or Season 9 server file on Windows Server with SQL Server 2014 or later.
.ini files and stored procedures. If your server file uses a different approach (XML configs, Lua scripts), the concepts are identical but the file paths and syntax will differ.Setting Up the VIP Database Schema
Before touching any configuration file, lay the groundwork in SQL Server. Open SQL Server Management Studio and connect to your MuOnline database instance.
Run the following script to create the core tables:
-- VIP tier definitions
-- TierID → 1 = Bronze, 2 = Silver, 3 = Gold
CREATE TABLE [dbo].[VipTiers] (
TierID INT PRIMARY KEY,
TierName NVARCHAR(20) NOT NULL,
ExpMultiplier FLOAT NOT NULL DEFAULT 1.0, -- e.g. 1.5 → +50% EXP
DropMultiplier FLOAT NOT NULL DEFAULT 1.0, -- e.g. 1.2 → +20% drops
ZenMultiplier FLOAT NOT NULL DEFAULT 1.0,
StorageSlots INT NOT NULL DEFAULT 0, -- extra warehouse rows
DailyBonusPts INT NOT NULL DEFAULT 0
);
-- Insert default tiers
INSERT INTO [dbo].[VipTiers] VALUES
(1, 'Bronze', 1.30, 1.10, 1.10, 0, 50),
(2, 'Silver', 1.60, 1.25, 1.20, 1, 120),
(3, 'Gold', 2.00, 1.50, 1.40, 2, 250);
-- Per-account VIP records
CREATE TABLE [dbo].[AccountVip] (
AccountID VARCHAR(10) PRIMARY KEY,
TierID INT NOT NULL DEFAULT 1,
StartDate DATETIME NOT NULL DEFAULT GETDATE(),
EndDate DATETIME NOT NULL,
IsActive BIT NOT NULL DEFAULT 1,
GrantedBy VARCHAR(10) NULL, -- GM who granted it
SuspendedByBan BIT NOT NULL DEFAULT 0,
FOREIGN KEY (TierID) REFERENCES VipTiers(TierID)
);
-- Bonus points ledger
CREATE TABLE [dbo].[BonusPoints] (
EntryID INT IDENTITY(1,1) PRIMARY KEY,
AccountID VARCHAR(10) NOT NULL,
Points INT NOT NULL,
Source VARCHAR(50) NOT NULL, -- 'DailyVip', 'Event', 'GM'
AwardedAt DATETIME NOT NULL DEFAULT GETDATE(),
ExpiresAt DATETIME NULL -- NULL → never expires
);
> [!WARNING] > Always back up your MuOnline database before running DDL statements. A failed migration that partially creates tables can leave foreign key constraints in a broken state, which will prevent GameServer from starting.
Once the tables exist, create the stored procedure that GameServer will call each time it needs to resolve a player's active benefits:
CREATE PROCEDURE [dbo].[usp_GetVipBenefits]
@AccountID VARCHAR(10)
AS
BEGIN
SET NOCOUNT ON;
SELECT
av.TierID,
vt.TierName,
vt.ExpMultiplier,
vt.DropMultiplier,
vt.ZenMultiplier,
vt.StorageSlots,
vt.DailyBonusPts,
av.EndDate,
-- Remaining days → useful for in-game display
DATEDIFF(DAY, GETDATE(), av.EndDate) AS DaysRemaining
FROM [dbo].[AccountVip] av
INNER JOIN [dbo].[VipTiers] vt ON av.TierID = vt.TierID
WHERE av.AccountID = @AccountID
AND av.IsActive = 1
AND av.EndDate > GETDATE()
AND av.SuspendedByBan = 0;
END;
Configuring the Server Files
With the database ready, open your server directory and locate the main configuration folder. The path varies by server file edition, but it is typically:
GameServer\Config\
Inside you will find files such as GameServerInfo.ini and ExpRate.ini. Create or edit a file named VipSystem.ini:
; VipSystem.ini — loaded by GameServer on startup
; Lines beginning with ; are comments
[VIP_SYSTEM]
Enabled = 1 ; 0 → disable entirely without removing tables
CheckIntervalMin = 60 ; how often (minutes) expiry is re-checked
ExpiredTierFallback = 0 ; TierID to apply on expiry (0 → no VIP)
[BONUS_POINTS]
Enabled = 1
DailyAwardHour = 0 ; UTC hour when daily points are credited (0 → midnight)
MaxAccumulatedPts = 99999 ; cap so the ledger does not grow unbounded
PointsExpiryDays = 90 ; 0 → never expire organically awarded points
[GM_COMMANDS]
; Command format used in-game: /vipgrant <account> <tier> <days>
GrantVipCommand = /vipgrant
RevokeVipCommand = /viprevoke
AddPointsCommand = /addpts
CheckVipCommand = /vipinfo
> [!TIP] > Keep CheckIntervalMin at 60 or higher in production. Setting it too low (under 10 minutes) creates frequent SQL round-trips that can cause noticeable lag on servers with 200+ concurrent players. The expiry check is not time-critical — a player whose VIP lapses at 2:00 AM will not notice a delay until 3:00 AM.
Registering GM Commands
Open GameServer\Source\GMCommands.cpp (or the equivalent handler file for your server files). Locate the section where existing / commands are registered and add entries for the new VIP commands.
The pattern follows the existing command table format:
// VIP command handlers — add alongside existing GM command entries
{ "/vipgrant", CMD_GM_LEVEL_3, CmdVipGrant, "[account] [tier 1-3] [days]" },
{ "/viprevoke", CMD_GM_LEVEL_3, CmdVipRevoke, "[account]" },
{ "/addpts", CMD_GM_LEVEL_2, CmdAddPoints, "[account] [points]" },
{ "/vipinfo", CMD_GM_LEVEL_1, CmdVipInfo, "[account]" },
Each handler function calls the corresponding stored procedure via your server's existing ODBC or ADO connection wrapper, passing the parameters extracted from the command string.
Testing the System Before Going Live
Never push a new system directly to a live server. Use the following checklist on a staging instance:
- Grant a VIP tier to a test account:
/vipgrant testaccount 2 30 - Log in with that account and verify the EXP multiplier is active by killing a monster and comparing the EXP gain against the base rate documented in
ExpRate.ini. - Wait for (or manually trigger) the daily bonus point credit and confirm an entry appears in the
BonusPointstable. - Set
EndDateto a past timestamp in the database and wait one check interval to confirm the account loses VIP status cleanly. - Verify the
/vipinfocommand returns accurate data for both VIP and non-VIP accounts.
Log all test results before promoting the configuration to production. If your server file supports hot-reload of .ini files via a console command, use it — restarting GameServer for every config tweak wastes time and drops active connections.
Maintaining the System Over Time
A VIP system generates ongoing database growth. Schedule a SQL Server Agent job (or a Windows Task Scheduler entry pointing to a .sql script) to run weekly maintenance:
-- Deactivate expired VIP records older than 30 days (keeps history)
UPDATE [dbo].[AccountVip]
SET IsActive = 0
WHERE EndDate < DATEADD(DAY, -30, GETDATE())
AND IsActive = 1;
-- Purge expired bonus point entries beyond the configured expiry window
DELETE FROM [dbo].[BonusPoints]
WHERE ExpiresAt IS NOT NULL
AND ExpiresAt < GETDATE();
Keeping old AccountVip rows with IsActive = 0 rather than deleting them gives you an audit trail. If a player opens a support ticket claiming their VIP was lost, you can query the history directly without digging through backups.
Summary
You now have a complete, layered VIP and bonus system:
- Tier definitions stored in SQL with individual multipliers per benefit category.
- Per-account records with start/end dates, active flags, and ban-suspension support.
- A separate bonus points ledger with source tracking and optional expiry.
- Server-side
.iniconfiguration that controls behavior without requiring recompilation. - GM commands at three privilege levels for granting, revoking, and inspecting VIP status.
The same pattern scales to additional tiers or new benefit types (for example, reduced cooldowns or access to exclusive maps) by adding columns to VipTiers and updating the stored procedure and .ini mappings accordingly.
Perguntas frequentes
What database tables are required for the VIP system?
At minimum you need a VIP account table (AccountID, VipType, StartDate, EndDate, IsActive) and a VIP benefits table that maps each VIP tier to its stat multipliers and drop-rate bonuses. Most Season 6+ server files already include stub tables — check your MuOnline database for existing entries before creating new ones.
How do I prevent VIP benefits from stacking with each other?
In your VIP benefit calculation function, apply a tier priority check before summing multipliers. Store the active tier in a session variable and only apply the highest matching tier. Using a CASE statement in SQL or an if-else chain in your GameServer source is the safest approach.
Can I grant time-limited bonus points without a VIP tier?
Yes. Create a separate BonusPoints table with AccountID, Points, ExpirationDate, and Source columns. Award points through a stored procedure called from GM commands or event scripts. Points are consumed independently of VIP status.
What happens to active VIP when a player is banned?
The VIP record stays in the database but the account is locked. When the ban is lifted, check whether EndDate is still in the future. If not, the tier has expired naturally. You may want to add a flag column (SuspendedByBan BIT) to pause the timer during the ban period.