Scripts útiles para Game Master en lote en el servidor de MU Online
Automatiza tareas repetitivas de Game Master en tu servidor de MU Online con scripts en lote: distribución de ítems, baneos, resets masivos, limpieza de cuentas inactivas y anuncios programados.
Administrar un servidor de MU Online con cientos o miles de cuentas activas hace inviable ejecutar cada acción de moderación, evento o mantenimiento manualmente, una cuenta a la vez. Los scripts de GM en lote resuelven este cuello de botella, permitiendo distribuir premios de evento a una lista de g
Administrar un servidor de MU Online con cientos o miles de cuentas activas hace inviable ejecutar cada acción de moderación, evento o mantenimiento manualmente, una cuenta a la vez. Los scripts de GM en lote resuelven este cuello de botella, permitiendo distribuir premios de evento a una lista de ganadores, banear cuentas por patrón de comportamiento, resetear personajes en masa o limpiar cuentas inactivas con un único comando revisado y auditable. Este tutorial reúne los scripts más útiles para el día a día de la administración, con el cuidado que las acciones en lote exigen —porque un UPDATE sin WHERE correcto puede destruir la economía del servidor en segundos.
Por qué centralizar las acciones de GM en scripts
Las acciones hechas manualmente, comando por comando, tienen tres problemas: son lentas a escala, no dejan rastro de auditoría confiable y son inconsistentes entre distintos GMs. Un script versionado resuelve los tres: corre en segundos sobre miles de filas, registra un log de cada acción, y cualquier GM autorizado lo ejecuta de la misma forma. La regla de oro es siempre correr la versión SELECT del filtro antes de la versión UPDATE/DELETE, confirmando el número de filas afectadas.
Requisitos previos
- Acceso de lectura y escritura a la base de datos del servidor (MSSQL o MySQL, según el emulador).
- Backup reciente de la base de datos antes de cualquier ejecución en lote.
- Una tabla de auditoría (
GMActionLogo equivalente) para registrar quién ejecutó qué y cuándo. - Ambiente de prueba/staging con una copia de la base de datos para validar scripts nuevos antes de correrlos en producción.
Estructura de log de auditoría
Antes de cualquier script en lote, asegúrate de que exista una tabla para registrar las acciones administrativas:
CREATE TABLE GMActionLog (
LogId INT IDENTITY(1,1) PRIMARY KEY,
GMAccount VARCHAR(50) NOT NULL,
ActionType VARCHAR(50) NOT NULL,
TargetAccount VARCHAR(50) NULL,
Details VARCHAR(500) NULL,
ExecutedAt DATETIME DEFAULT GETDATE()
);
Toda acción en lote descrita abajo debe terminar con un INSERT en esa tabla, aunque el resto del script sea simple.
Script 1 — Distribución de ítems en lote (premio de evento)
Para entregar un ítem a una lista de ganadores de evento sin repetir comando por comando:
-- Verificación antes de ejecutar
SELECT AccountID, CharacterName FROM Character
WHERE CharacterName IN ('Jogador1','Jogador2','Jogador3');
-- Distribución (ejemplo MuEMU/IGCN, ajusta la tabla según el emulador)
INSERT INTO WarehouseItems (AccountID, ItemIndex, ItemLevel, ItemOptions, Durability)
SELECT AccountID, 168, 15, 0, 255
FROM Character
WHERE CharacterName IN ('Jogador1','Jogador2','Jogador3');
INSERT INTO GMActionLog (GMAccount, ActionType, Details)
VALUES ('admin_root', 'ITEM_DISTRIBUTION', 'Premio evento verano - 3 cuentas');
Evita entregar ítems directamente al inventario del personaje en línea —prefiere el warehouse (baúl), que no exige que el jugador esté desconectado.
Script 2 — Prevención de entrega duplicada
Para eventos recurrentes, controla quién ya recibió el premio con una tabla de log dedicada:
CREATE TABLE ItemDistributionLog (
AccountID VARCHAR(50),
EventCode VARCHAR(50),
DistributedAt DATETIME DEFAULT GETDATE(),
PRIMARY KEY (AccountID, EventCode)
);
-- Entrega solo a quien todavía no recibió
INSERT INTO WarehouseItems (AccountID, ItemIndex, ItemLevel)
SELECT c.AccountID, 168, 15
FROM Character c
WHERE c.CharacterName IN ('Jogador1','Jogador2')
AND NOT EXISTS (
SELECT 1 FROM ItemDistributionLog d
WHERE d.AccountID = c.AccountID AND d.EventCode = 'VERAO2026'
);
Este patrón evita el error clásico de correr el script dos veces por accidente y duplicar premios valiosos.
Script 3 — Baneo en lote por patrón de comportamiento
Para banear cuentas que comparten características sospechosas (ej. la misma IP con múltiples cuentas farmeando con bot):
-- Verificación: cuántas cuentas serán afectadas
SELECT AccountID, LastIP, LastLoginDate FROM Account
WHERE LastIP IN ('203.0.113.10','203.0.113.11') AND BanStatus = 0;
-- Baneo
UPDATE Account
SET BanStatus = 1, BanReason = 'Uso de bot - IP compartida sospechosa'
WHERE LastIP IN ('203.0.113.10','203.0.113.11') AND BanStatus = 0;
INSERT INTO GMActionLog (GMAccount, ActionType, Details)
VALUES ('admin_root', 'BAN_BATCH', 'Ban por IP sospechosa - 2 IPs, ver SELECT anterior');
Siempre filtra por BanStatus = 0 para no reprocesar cuentas ya baneadas y ensuciar el log.
Script 4 — Reset masivo de personajes (evento de reset gratuito)
Para un evento de reset gratuito limitado a personajes por encima de determinado nivel:
-- Verificación
SELECT Name, cLevel, ResetCount FROM Character WHERE cLevel >= 400;
-- Reset masivo
UPDATE Character
SET cLevel = 1, Experience = 0, LevelUpPoint = 0,
ResetCount = ResetCount + 1,
Strength = 30, Dexterity = 30, Vitality = 30, Energy = 30
WHERE cLevel >= 400;
INSERT INTO GMActionLog (GMAccount, ActionType, Details)
VALUES ('admin_root', 'MASS_RESET', 'Evento reset gratuito - cLevel >= 400');
Los valores base de atributo (Strength, Dexterity, etc.) varían según la clase —ajusta la consulta con un CASE por clase si tu servidor no usa atributos base uniformes.
Script 5 — Limpieza de cuentas inactivas
Para identificar y opcionalmente archivar cuentas sin inicio de sesión desde hace mucho tiempo, liberando nombres de personaje y espacio en la base de datos:
-- Identificar cuentas inactivas hace más de 365 días
SELECT AccountID, LastLoginDate FROM Account
WHERE LastLoginDate < DATEADD(DAY, -365, GETDATE());
-- Marcar (no eliminar directamente) para archivado
UPDATE Account
SET AccountStatus = 'ARCHIVED_CANDIDATE'
WHERE LastLoginDate < DATEADD(DAY, -365, GETDATE()) AND AccountStatus = 'ACTIVE';
Nunca hagas DELETE directo de cuentas antiguas en un solo paso —márcalas para archivado, espera un período de gracia (30-60 días) anunciado públicamente, y solo entonces elimínalas o muévelas a una tabla de historial.
Script 6 — Anuncios programados en el servidor
Para notificar a los jugadores sobre mantenimiento o eventos sin depender de un GM escribiendo justo a tiempo, usa una tabla de anuncios leída por un servicio programado:
CREATE TABLE ScheduledAnnouncements (
Id INT IDENTITY(1,1) PRIMARY KEY,
Message VARCHAR(500),
ScheduledTime DATETIME,
Sent BIT DEFAULT 0
);
INSERT INTO ScheduledAnnouncements (Message, ScheduledTime)
VALUES ('Mantenimiento en 30 minutos - guarda tu progreso', DATEADD(MINUTE, 30, GETDATE()));
Un servicio externo (job programado) consulta esa tabla cada minuto y envía el mensaje vía pipe de comando del GameServer cuando llega ScheduledTime, marcando Sent = 1 para no repetirlo.
Buenas prácticas al correr scripts en lote
| Práctica | Por qué |
|---|---|
| Siempre correr SELECT antes del UPDATE/DELETE | Confirma el número de filas afectadas antes de comprometer datos |
| Correr primero en staging/copia de la base de datos | Evita descubrir un error de lógica directamente en producción |
| Registrar toda acción en la tabla de auditoría | Permite investigar reclamos y revertir decisiones con contexto |
Usar transacciones (BEGIN TRAN/COMMIT) | Permite un ROLLBACK inmediato si el resultado no coincide con lo esperado |
| Nunca dar acceso de ejecución a todos los GMs | Reduce el riesgo de error o abuso; centraliza los scripts sensibles en pocos administradores |
Usando transacciones para reducir el riesgo
Envolver el script en una transacción permite confirmar el resultado antes de comprometerlo:
BEGIN TRAN;
UPDATE Account SET BanStatus = 1 WHERE LastIP = '203.0.113.10';
-- Verifica el resultado
SELECT * FROM Account WHERE LastIP = '203.0.113.10';
-- Si está correcto:
COMMIT;
-- Si algo está mal:
-- ROLLBACK;
Este patrón es especialmente valioso para acciones irreversibles como el baneo y el reset masivo, donde un error de filtro sale caro.
Organizando los scripts en un repositorio
Al igual que el código del servidor, los scripts de GM deben estar versionados. Una estructura simple:
gm-scripts/
├── eventos/
│ ├── reset-gratuito-2026-07.sql
│ └── premio-verano.sql
├── moderacion/
│ ├── ban-por-ip.sql
│ └── limpieza-inactivos.sql
└── README.md
Cada archivo debe contener, en un comentario, la fecha de uso, el GM responsable y el resultado esperado (cuántas filas afectadas). Esto transforma scripts sueltos en un historial auditable de la administración del servidor.
Errores comunes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| Ítem entregado por duplicado | Script ejecutado dos veces sin verificación de log | Agrega una tabla de control de distribución (Script 2) |
| Cuentas equivocadas baneadas | Filtro del WHERE más amplio de lo previsto | Siempre corre el SELECT de verificación antes del UPDATE |
| El reset afectó personajes que no debía | Rango de nivel o clase mal filtrado | Ajusta el WHERE y valida con SELECT en staging primero |
| Pérdida de datos tras DELETE masivo | Ausencia de backup inmediatamente anterior | Siempre haz backup y prefiere marcar para archivado en vez de eliminar directo |
| El anuncio programado no se disparó | El servicio programado no está corriendo o la tabla no se lee | Verifica el job/cron responsable y el pipe de comando del GameServer |
Lista de verificación antes de correr un script de GM en lote
- Backup de la base de datos hecho inmediatamente antes de la ejecución.
- Versión SELECT del filtro corrida y verificada.
- Script probado en staging o copia de la base de datos.
- Transacción (BEGIN TRAN) usada para acciones irreversibles.
- Acción registrada en la tabla de auditoría (GMActionLog).
- Script versionado en el repositorio de scripts de GM.
- Acceso de ejecución restringido a administradores autorizados.
Con estos scripts organizados y auditables, el siguiente paso natural es integrarlos al pipeline de mantenimiento del servidor como un todo —consulta el tutorial de creación de servidor de MU Online para entender cómo este flujo administrativo encaja en la operación completa.
Preguntas frecuentes
¿Es seguro correr scripts SQL directamente en producción para acciones de GM?
Solo con un backup inmediatamente anterior y una prueba previa en staging o en una copia de la base de datos. Las acciones en lote (UPDATE/DELETE sin WHERE preciso) son la causa más común de accidentes graves en servidores de MU —siempre corre primero un SELECT con los mismos filtros para confirmar cuántas filas serán afectadas.
¿Estos scripts reemplazan los comandos de GM in-game?
No del todo. Los comandos in-game (como /additem o /ban) son mejores para acciones puntuales y para GMs sin acceso técnico a la base de datos. Los scripts en lote tienen sentido cuando la acción afecta a decenas o cientos de cuentas a la vez, lo cual sería inviable comando por comando.
¿Cómo evitar que un script de distribución de ítems duplique entregas?
Registra cada entrega en una tabla de log (ej. ItemDistributionLog) con el ID de la cuenta y el ID del evento, y haz que el script verifique esa tabla antes de insertar el ítem nuevamente. Esto también sirve como auditoría en caso de que un jugador reclame que no recibió el premio.
¿Puedo programar estos scripts para que corran solos?
Sí, usando el SQL Server Agent (MSSQL) o un cron job que llame a un script que ejecute la consulta. Es común para tareas como la limpieza de cuentas inactivas o el reseteo de eventos diarios, pero las acciones sensibles (baneo, remoción de ítem) deben mantener aprobación manual antes de ejecutarse.
¿Cuál es el riesgo de dar reset masivo a todos los personajes?
Si la consulta no filtra correctamente por nivel mínimo o clase, puedes resetear personajes que no debían, causando pérdida de builds y reclamos masivos. Siempre corre el SELECT de verificación antes y avisa a la comunidad con anticipación sobre la ventana de mantenimiento.