Cómo Agregar VIP y Bonos al Servidor de MU Online
Aprende a configurar rangos VIP y sistemas de bonos en tu servidor privado de MU Online, desde el esquema de base de datos hasta los comandos GM en juego.
Introducción: Por Qué Son Importantes el VIP y los Bonos
Un sistema de VIP y bonos bien diseñado le otorga al administrador del servidor dos herramientas poderosas: una forma de recompensar a los jugadores comprometidos y un mecanismo para mantener la economía equilibrada mediante niveles de beneficios controlados. A diferencia de los aumentos de estadísticas otorgados de manera arbitraria, un sistema VIP estructurado es transparente, auditable y fácil de modificar sin tocar la lógica central del juego.
Esta guía cubre todo el flujo de implementación: esquema de base de datos, archivos de configuración del lado del servidor y los enlaces de comandos en juego que unen todo. Se asume que estás ejecutando un server file Season 6 o Season 9 en Windows Server con SQL Server 2014 o posterior.
.ini y procedimientos almacenados. Si tu server file utiliza un enfoque diferente (configuraciones XML, scripts Lua), los conceptos son idénticos, pero las rutas de archivos y la sintaxis variarán.Configurar el Esquema de Base de Datos para VIP
Antes de tocar cualquier archivo de configuración, sienta las bases en SQL Server. Abre SQL Server Management Studio y conéctate a tu instancia de base de datos MuOnline.
Ejecuta el siguiente script para crear las tablas principales:
-- Definición de niveles VIP
-- TierID → 1 = Bronce, 2 = Plata, 3 = Oro
CREATE TABLE [dbo].[VipTiers] (
TierID INT PRIMARY KEY,
TierName NVARCHAR(20) NOT NULL,
ExpMultiplier FLOAT NOT NULL DEFAULT 1.0, -- 1.5 → +50% EXP
DropMultiplier FLOAT NOT NULL DEFAULT 1.0, -- 1.2 → +20% drops
ZenMultiplier FLOAT NOT NULL DEFAULT 1.0,
StorageSlots INT NOT NULL DEFAULT 0, -- filas extra de almacén
DailyBonusPts INT NOT NULL DEFAULT 0
);
-- Insertar niveles por defecto
INSERT INTO [dbo].[VipTiers] VALUES
(1, 'Bronce', 1.30, 1.10, 1.10, 0, 50),
(2, 'Plata', 1.60, 1.25, 1.20, 1, 120),
(3, 'Oro', 2.00, 1.50, 1.40, 2, 250);
-- Registros VIP por cuenta
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 que lo otorgó
SuspendedByBan BIT NOT NULL DEFAULT 0,
FOREIGN KEY (TierID) REFERENCES VipTiers(TierID)
);
-- Libro mayor de puntos de bono
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 → nunca expiran
);
> [!ATENCION] > Siempre realiza una copia de seguridad de tu base de datos MuOnline antes de ejecutar instrucciones DDL. Una migración fallida que crea tablas parcialmente puede dejar restricciones de clave foránea en un estado inconsistente, lo que puede impedir que el GameServer arranque.
Una vez que existan las tablas, crea el procedimiento almacenado que GameServer llamará cada vez que necesite resolver los beneficios activos de un jugador:
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,
-- Días restantes → útil para mostrar en juego
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;
Configurar los Archivos del Servidor
Con la base de datos lista, abre el directorio de tu servidor y localiza la carpeta de configuración principal. La ruta varía según la edición del server file, pero generalmente es:
GameServer\Config\
Dentro encontrarás archivos como GameServerInfo.ini y ExpRate.ini. Crea o edita un archivo llamado VipSystem.ini:
; VipSystem.ini — cargado por GameServer al iniciar
; Las líneas que comienzan con ; son comentarios
[VIP_SYSTEM]
Enabled = 1 ; 0 → desactiva sin eliminar las tablas
CheckIntervalMin = 60 ; frecuencia (minutos) con que se verifica la expiración
ExpiredTierFallback = 0 ; TierID al expirar (0 → sin VIP)
[BONUS_POINTS]
Enabled = 1
DailyAwardHour = 0 ; hora UTC en que se acreditan puntos diarios
MaxAccumulatedPts = 99999 ; límite para que el libro mayor no crezca sin control
PointsExpiryDays = 90 ; 0 → los puntos otorgados no expiran nunca
[GM_COMMANDS]
; Formato del comando en juego: /vipgrant <cuenta> <nivel> <días>
GrantVipCommand = /vipgrant
RevokeVipCommand = /viprevoke
AddPointsCommand = /addpts
CheckVipCommand = /vipinfo
> [!CONSEJO] > Mantén CheckIntervalMin en 60 o más en producción. Configurarlo demasiado bajo (menos de 10 minutos) genera consultas SQL frecuentes que pueden provocar lag notable en servidores con más de 200 jugadores simultáneos. La verificación de expiración no es crítica en tiempo real — un jugador cuyo VIP vence a las 2:00 AM no notará un retraso hasta las 3:00 AM.
Registrar los Comandos GM
Abre GameServer\Source\GMCommands.cpp (o el archivo manejador equivalente para tus server files). Localiza la sección donde se registran los comandos / existentes y agrega entradas para los nuevos comandos VIP.
El patrón sigue el formato de la tabla de comandos existente:
// Manejadores de comandos VIP — agregar junto a las entradas GM existentes
{ "/vipgrant", CMD_GM_LEVEL_3, CmdVipGrant, "[cuenta] [nivel 1-3] [dias]" },
{ "/viprevoke", CMD_GM_LEVEL_3, CmdVipRevoke, "[cuenta]" },
{ "/addpts", CMD_GM_LEVEL_2, CmdAddPoints, "[cuenta] [puntos]" },
{ "/vipinfo", CMD_GM_LEVEL_1, CmdVipInfo, "[cuenta]" },
Cada función manejadora llama al procedimiento almacenado correspondiente a través del envoltorio de conexión ODBC o ADO existente en tu servidor, pasando los parámetros extraídos de la cadena del comando.
Probar el Sistema Antes de Publicarlo
Nunca lleves un nuevo sistema directamente a un servidor en producción. Usa la siguiente lista de verificación en una instancia de prueba:
- Otorga un nivel VIP a una cuenta de prueba:
/vipgrant cuentaprueba 2 30 - Inicia sesión con esa cuenta y verifica que el multiplicador de EXP esté activo matando un monstruo y comparando la ganancia de EXP con la tasa base documentada en
ExpRate.ini. - Espera a (o activa manualmente) el crédito de puntos de bono diario y confirma que aparece una entrada en la tabla
BonusPoints. - Establece
EndDatecon una marca de tiempo pasada en la base de datos y espera un intervalo de verificación para confirmar que la cuenta pierde el estado VIP de forma limpia. - Verifica que el comando
/vipinfodevuelva datos precisos tanto para cuentas VIP como para cuentas sin VIP.
Registra todos los resultados de las pruebas antes de llevar la configuración a producción. Si tu server file admite recarga en caliente de los archivos .ini mediante un comando de consola, úsalo — reiniciar el GameServer para cada ajuste de configuración pierde tiempo y desconecta a los jugadores activos.
Mantenimiento del Sistema a Largo Plazo
Un sistema VIP genera crecimiento continuo en la base de datos. Programa un trabajo del Agente de SQL Server (o una entrada del Programador de tareas de Windows que apunte a un script .sql) para ejecutar mantenimiento semanal:
-- Desactivar registros VIP expirados hace más de 30 días (conserva historial)
UPDATE [dbo].[AccountVip]
SET IsActive = 0
WHERE EndDate < DATEADD(DAY, -30, GETDATE())
AND IsActive = 1;
-- Purgar entradas de puntos de bono expiradas fuera de la ventana configurada
DELETE FROM [dbo].[BonusPoints]
WHERE ExpiresAt IS NOT NULL
AND ExpiresAt < GETDATE();
Mantener las filas antiguas de AccountVip con IsActive = 0 en lugar de eliminarlas te proporciona un rastro de auditoría. Si un jugador abre un ticket de soporte reclamando que su VIP se perdió, puedes consultar el historial directamente sin revisar las copias de seguridad.
Resumen
Ahora tienes un sistema de VIP y bonos completo, estructurado en capas:
- Definiciones de nivel almacenadas en SQL con multiplicadores individuales por categoría de beneficio.
- Registros por cuenta con fechas de inicio y fin, banderas de estado activo y soporte para suspensión por ban.
- Un libro mayor de puntos de bono separado con seguimiento de origen y expiración opcional.
- Configuración del lado del servidor en
.inique controla el comportamiento sin necesidad de recompilación. - Comandos GM en tres niveles de privilegio para otorgar, revocar e inspeccionar el estado VIP.
El mismo patrón escala a niveles adicionales o nuevos tipos de beneficios (por ejemplo, tiempos de espera reducidos o acceso a mapas exclusivos) simplemente agregando columnas a VipTiers y actualizando el procedimiento almacenado y los mapeos en el .ini.
Perguntas frequentes
¿Qué tablas de base de datos se necesitan para el sistema VIP?
Como mínimo necesitas una tabla de cuentas VIP (AccountID, TierID, StartDate, EndDate, IsActive) y una tabla de beneficios que mapee cada nivel VIP a sus multiplicadores de estadísticas y bonos de drop. La mayoría de los server files Season 6 en adelante ya incluyen tablas de marcador de posición — revisa tu base de datos MuOnline antes de crear tablas nuevas.
¿Cómo evito que los beneficios VIP se acumulen entre niveles?
En tu función de cálculo de beneficios VIP, aplica una verificación de prioridad de nivel antes de sumar multiplicadores. Guarda el nivel activo en una variable de sesión y aplica únicamente el nivel más alto que coincida. Usar un CASE en SQL o una cadena if-else en el código fuente del GameServer es el enfoque más seguro.
¿Puedo otorgar puntos de bono con tiempo limitado sin nivel VIP?
Sí. Crea una tabla BonusPoints separada con las columnas AccountID, Points, ExpirationDate y Source. Otorga puntos mediante un procedimiento almacenado llamado desde comandos GM o scripts de eventos. Los puntos se consumen de forma independiente al estado VIP.
¿Qué ocurre con el VIP activo cuando un jugador es baneado?
El registro VIP permanece en la base de datos, pero la cuenta queda bloqueada. Al levantar el ban, verifica si EndDate todavía es futuro. Si no, el nivel expiró de forma natural. Puedes agregar una columna de bandera (SuspendedByBan BIT) para pausar el temporizador mientras dura el ban.