Cómo Implementar un Sistema de Referidos en tu Servidor MU
Aprende a implementar un sistema de referidos completo en tu servidor MU Online: base de datos, lógica de recompensas y panel de seguimiento paso a paso.
Un sistema de referidos bien implementado convierte a tus jugadores actuales en embajadores orgánicos de tu servidor. A diferencia de estrategias puramente publicitarias, este mecanismo premia la recomendación genuina: el jugador que invita obtiene un beneficio, y el recién llegado recibe un incentivo para quedarse. En este tutorial construirás el sistema completo desde la base de datos hasta la validación en el GameServer.
Diseño de la Base de Datos
Antes de escribir una sola línea de lógica, el esquema debe estar bien definido. Necesitas tres tablas nuevas que se relacionan con la tabla accounts existente de tu servidor MU.
-- Tabla principal de referidos
-- referidor_account → cuenta que entregó el código
-- referido_account → cuenta recién registrada que usó el código
-- estado: PENDIENTE → VALIDADO → RECOMPENSADO
CREATE TABLE referral_registro (
id INT NOT NULL AUTO_INCREMENT,
referidor VARCHAR(10) NOT NULL, -- login del referidor
referido VARCHAR(10) NOT NULL, -- login de la cuenta nueva
codigo VARCHAR(20) NOT NULL, -- código único generado
fecha_registro DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
fecha_validado DATETIME NULL,
estado ENUM('PENDIENTE','VALIDADO','RECOMPENSADO') NOT NULL DEFAULT 'PENDIENTE',
PRIMARY KEY (id),
UNIQUE KEY uq_referido (referido), -- una cuenta solo puede ser referida una vez
UNIQUE KEY uq_codigo (codigo),
FOREIGN KEY (referidor) REFERENCES accounts(account) ON DELETE CASCADE,
FOREIGN KEY (referido) REFERENCES accounts(account) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Configuración de recompensas (permite ajustar sin redeployar)
CREATE TABLE referral_config (
clave VARCHAR(50) NOT NULL PRIMARY KEY,
valor VARCHAR(255) NOT NULL,
notas TEXT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Historial de recompensas entregadas
CREATE TABLE referral_recompensas (
id INT NOT NULL AUTO_INCREMENT,
referral_id INT NOT NULL,
cuenta VARCHAR(10) NOT NULL,
tipo VARCHAR(50) NOT NULL, -- 'item', 'zen', 'puntos'
detalle TEXT NULL,
fecha DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (referral_id) REFERENCES referral_registro(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Valores iniciales de configuración
INSERT INTO referral_config (clave, valor, notas) VALUES
('nivel_minimo', '20', 'Nivel mínimo que debe alcanzar el referido para validar'),
('item_recompensa', '13,0', 'Código de ítem para el referidor: serie,indice'),
('zen_recompensa', '5000000','Zen entregado al referidor por cada referido validado');
estado usa un ENUM con tres fases deliberadamente: PENDIENTE evita recompensas por cuentas que nunca llegan a jugar, VALIDADO confirma que el referido cumplió los requisitos, y RECOMPENSADO garantiza que no se entregue el beneficio dos veces aunque el job corra en paralelo.Generación y Asignación de Códigos
Cada cuenta registrada debe recibir un código único al momento del registro. La forma más limpia es un stored procedure que el panel web invoca tras crear la cuenta.
DELIMITER $$
CREATE PROCEDURE sp_generar_codigo_referral (IN p_account VARCHAR(10))
BEGIN
DECLARE v_codigo VARCHAR(20);
DECLARE v_intentos INT DEFAULT 0;
-- Genera un código hasta encontrar uno no usado
-- Formato: primeras 4 letras de la cuenta + 8 hex aleatorios
-- Ejemplo: HERO → HEROa3f9c012
REPEAT
SET v_codigo = CONCAT(
UPPER(LEFT(p_account, 4)),
LOWER(CONV(FLOOR(RAND() * 0xFFFFFFFF), 10, 16))
);
SET v_intentos = v_intentos + 1;
UNTIL NOT EXISTS (
SELECT 1 FROM referral_registro WHERE codigo = v_codigo
) OR v_intentos > 10 END REPEAT;
IF v_intentos > 10 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'No se pudo generar un código único tras 10 intentos';
END IF;
-- Almacena el código en una tabla auxiliar de códigos por cuenta
-- Si la cuenta ya tiene código, no hace nada (INSERT IGNORE)
INSERT IGNORE INTO referral_codigos (account, codigo)
VALUES (p_account, v_codigo);
SELECT v_codigo AS codigo_generado;
END$$
DELIMITER ;
Cuando un nuevo jugador se registra e ingresa el código de otro, el panel web ejecuta un segundo procedure que crea el vínculo:
-- Vincula referidor → referido al momento del registro
-- p_referido → cuenta recién creada
-- p_codigo → código ingresado en el formulario de registro
-- Retorna: 0 = OK | 1 = código inválido | 2 = cuenta ya fue referida
CALL sp_vincular_referido('NuevoJugador', 'HEROa3f9c012');
> [!CONSEJO] > Muestra el campo de código de referido como opcional en el formulario de registro. Los campos obligatorios aumentan la fricción y reducen las conversiones. Un texto como "¿Alguien te recomendó el servidor? Ingresa su código y ambos reciben recompensas" funciona mejor que un campo marcado con asterisco rojo.
Validación Automática con un Job Programado
La validación no debe ocurrir en tiempo real para evitar cargar el GameServer. El enfoque correcto es un job que corre cada hora en el servidor de base de datos y revisa qué referidos cumplieron los requisitos.
DELIMITER $$
CREATE EVENT ev_validar_referidos
ON SCHEDULE EVERY 1 HOUR
DO
BEGIN
DECLARE v_nivel_min INT;
SELECT CAST(valor AS UNSIGNED) INTO v_nivel_min
FROM referral_config WHERE clave = 'nivel_minimo';
-- Marca como VALIDADO todo referido pendiente que ya alcanzó el nivel mínimo
-- y cuya cuenta tiene al menos 24 h de antigüedad (evita bots rápidos)
UPDATE referral_registro rr
INNER JOIN accounts a ON a.account = rr.referido
INNER JOIN character c ON c.AccountID = rr.referido
SET
rr.estado = 'VALIDADO',
rr.fecha_validado = NOW()
WHERE
rr.estado = 'PENDIENTE'
AND c.cLevel >= v_nivel_min
AND TIMESTAMPDIFF(HOUR, a.RegisterDate, NOW()) >= 24;
END$$
DELIMITER ;
La entrega de recompensas corre en un segundo evento que se activa cada hora con un desfase de 30 minutos para no colisionar:
Flujo de estados por hora:
:00 → ev_validar_referidos PENDIENTE → VALIDADO
:30 → ev_entregar_recompensas VALIDADO → RECOMPENSADO
> [!ATENCION] > Los MySQL Events requieren que el servidor tenga event_scheduler = ON. Verifica con SHOW VARIABLES LIKE 'event_scheduler';. Si devuelve OFF, agrega la línea event_scheduler = ON en la sección [mysqld] de tu my.ini y reinicia el servicio MySQL. Sin esto, los eventos nunca se ejecutarán y los referidos quedarán en estado PENDIENTE indefinidamente.
Panel Web de Seguimiento
Los jugadores necesitan ver su código personal y cuántos referidos han acumulado. Esta consulta alimenta la página de perfil:
-- Resumen del referidor para mostrar en el panel web
SELECT
rc.codigo AS mi_codigo,
COUNT(rr.id) AS total_referidos,
SUM(rr.estado = 'RECOMPENSADO') AS referidos_recompensados,
SUM(rr.estado = 'VALIDADO') AS referidos_pendientes_recompensa,
SUM(rr.estado = 'PENDIENTE') AS referidos_sin_validar
FROM referral_codigos rc
LEFT JOIN referral_registro rr ON rr.codigo = rc.codigo
WHERE rc.account = :cuenta_actual -- parámetro del panel web
GROUP BY rc.codigo;
En el frontend puedes presentar esta información como una tabla simple:
Mi codigo de referido: HEROa3f9c012
─────────────────────────────────────────
Referidos totales → 12
Recompensados → 9
En espera de recompensa → 2
Sin validar aún → 1
─────────────────────────────────────────
Recompensas recibidas → 9 ítems
→ 45.000.000 zen
Consideraciones de Seguridad y Antifraude
Un sistema de referidos sin controles es un vector de abuso. Las siguientes capas de protección son fundamentales:
Verificacion de IP en el registro. Guarda la IP de registro en la tabla accounts si no lo haces ya. Antes de crear el vínculo de referido, verifica que el referidor y el referido no compartan la misma IP de registro:
-- Rechaza el vínculo si ambas cuentas se registraron desde la misma IP
SELECT COUNT(*) INTO @misma_ip
FROM accounts
WHERE account IN (p_referidor, p_referido)
AND ip_registro = (SELECT ip_registro FROM accounts WHERE account = p_referido);
IF @misma_ip > 1 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'IP duplicada detectada';
END IF;
Limite de referidos por IP de juego. Además del registro, comprueba que las sesiones de juego del referido no provengan de la misma IP que el referidor durante los primeros 7 dias.
Auditoria de logs. Guarda en una tabla referral_audit cada cambio de estado con el timestamp y el proceso que lo ejecuto. Cuando detectes un patron sospechoso, podras reconstruir la cadena de eventos exactamente.
> [!CONSEJO] > Implementa una lista de revision manual para referidos que alcanzan el nivel minimo en menos de 2 horas desde el registro. Un jugador legitimo dificilmente llega al nivel 20 en ese tiempo; es una senal de personaje boost o trampa. Muevelos a un estado EN_REVISION en lugar de VALIDADO y revisalos a mano antes de entregar la recompensa.
Pruebas antes del Lanzamiento
Antes de abrir el sistema al publico ejecuta estas verificaciones en orden:
- Crea dos cuentas de prueba en tu entorno de desarrollo.
- Genera el codigo de la cuenta A con
CALL sp_generar_codigo_referral('CuentaA'). - Registra la cuenta B usando ese codigo y verifica que
referral_registrotiene una fila en estadoPENDIENTE. - Sube la cuenta B al nivel minimo configurado manualmente en la base de datos.
- Ejecuta
CALL ev_validar_referidosdirectamente para no esperar la hora. - Confirma que el estado cambio a
VALIDADOy quefecha_validadotiene valor. - Ejecuta el evento de recompensas y verifica que aparece un registro en
referral_recompensasy que el estado esRECOMPENSADO. - Intenta vincular una tercera cuenta con el mismo codigo del referido original y confirma que el sistema lo rechaza correctamente.
Solo tras pasar todos estos pasos el sistema esta listo para produccion. Un referral mal implementado puede generar decenas de reclamos de jugadores en las primeras horas de operacion, por lo que las pruebas exhaustivas no son opcionales.
Perguntas frequentes
¿Es posible limitar cuántos referidos puede tener un jugador?
Sí. En la tabla referral_config puedes agregar una columna max_referidos INT DEFAULT 10 y verificar ese límite en el stored procedure antes de insertar el registro. Si se supera el tope, el procedure devuelve un código de error que tu frontend o GameServer captura y muestra al jugador.
¿Cómo evitar que los jugadores creen cuentas falsas para cobrar recompensas?
Aplica un umbral de actividad mínima antes de validar el referido. Por ejemplo, exige que la cuenta referida alcance nivel 20 y haya iniciado sesión al menos 3 días distintos. Estos controles van en el trigger trg_referral_validar y reducen drásticamente el abuso.
¿Las recompensas pueden ser en puntos de tienda en lugar de ítems?
Absolutamente. Cambia el INSERT en referral_recompensas para apuntar a la tabla de puntos de tu panel web (normalmente accounts_shop_points o similar) en lugar de character_items. El resto de la lógica de validación permanece igual.
¿Puedo usar este sistema con servidores basados en Season diferente a la VI?
El esquema SQL es agnóstico a la season. Solo debes ajustar los nombres de columnas de la tabla character (el campo de nivel puede llamarse cLevel o Level según el source) y los códigos de ítem que correspondan a tu versión.