El mayor portal de MU Online de Brasil — desde 2003
Tutorial Intermedio Tutoriais

Cómo Crear Sistema de Voto y Recompensa en el Servidor de MU Online

Aprende a implementar un sistema de voto con recompensa automática en tu servidor de MU Online usando SQL Server, PHP y sitios de votación como GTOP100 y Xtremetop100.

EQ Equipo ViciadosMU · Actualizado el 3 jul 2026 · ⏱ 12 min de lectura

Descripción General del Sistema

Un sistema de voto y recompensa incentiva a los jugadores a promocionar tu servidor en los principales sitios de ranking de servidores privados de MU Online, como GTOP100, Xtremetop100 y MuOnline.com.br. Cada voto suma puntos en el ranking y, a cambio, el jugador recibe ítems, Zen, W-Coins o resets automáticos.

El flujo completo funciona así:

Sitio de votación → Pingback HTTP a tu servidor → Script PHP recibe y valida → Registro en SQL Server → Stored Procedure entrega la recompensa

Nota: Este tutorial cubre servidores Season 6 con MuServer en SQL Server 2008/2012/2014. Para Season 13+, los nombres de tablas pueden variar — confirma consultando el esquema de tu base de datos antes de ejecutar cualquier comando.

Paso 1: Crear la Tabla de Control de Votos en SQL Server

Abre SQL Server Management Studio (SSMS) y conéctate a la base de datos MuOnline. Ejecuta:

USE MuOnline;
GO

CREATE TABLE VoteLog (
    VoteID      INT IDENTITY(1,1) PRIMARY KEY,
    AccountID   VARCHAR(10)  NOT NULL,
    SiteID      VARCHAR(30)  NOT NULL,  -- 'gtop100', 'xtremetop100', etc.
    VoteKey     VARCHAR(64)  NOT NULL,  -- clave única devuelta por el sitio
    VoteDate    DATETIME     NOT NULL DEFAULT GETDATE(),
    VoteStatus  TINYINT      NOT NULL DEFAULT 0,  -- 0=pendiente, 1=entregado
    IPAddress   VARCHAR(45)  NOT NULL DEFAULT ''
);
GO

-- Índice para consultas rápidas por cuenta
CREATE INDEX IX_VoteLog_AccountID ON VoteLog (AccountID, VoteStatus);
GO
Dica: El campo VoteKey almacena el token único que el sitio de votación envía en el pingback. Sirve como prueba de voto legítimo y evita inserciones duplicadas — agrega una restricción UNIQUE para mayor protección: ALTER TABLE VoteLog ADD CONSTRAINT UQ_VoteKey UNIQUE (VoteKey);

Paso 2: Crear la Stored Procedure de Entrega de Recompensa

La stored procedure lee los votos pendientes e inserta los ítems de recompensa en la tabla del personaje. Ajusta ItemCode, ItemLevel e ItemOpt según la recompensa que deseas ofrecer.

USE MuOnline;
GO

CREATE PROCEDURE SP_DeliverVoteReward
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @AccountID  VARCHAR(10)
    DECLARE @CharName   VARCHAR(10)
    DECLARE @VoteID     INT

    -- El cursor recorre todos los votos pendientes
    DECLARE cur CURSOR FOR
        SELECT VoteID, AccountID
        FROM VoteLog
        WHERE VoteStatus = 0
        ORDER BY VoteDate ASC

    OPEN cur
    FETCH NEXT FROM cur INTO @VoteID, @AccountID

    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- Obtiene el personaje más reciente de la cuenta
        SELECT TOP 1 @CharName = Name
        FROM Character
        WHERE AccountID = @AccountID
        ORDER BY ConnectStat DESC, ResetCount DESC

        IF @CharName IS NOT NULL
        BEGIN
            -- Entrega 200 W-Coins (ajusta WCoinP según tu columna de coins)
            UPDATE MEMB_INFO
            SET WCoinP = ISNULL(WCoinP, 0) + 200
            WHERE memb___id = @AccountID

            -- Opcional: entregar ítem físico vía tabla de depósito
            -- INSERT INTO ITEM_STORE (AccountID, ItemCode, ItemLevel, ItemDur, ItemOpt)
            -- VALUES (@AccountID, 7936, 0, 255, 0)  -- ejemplo: Box of Kundun+5

            -- Marca el voto como entregado
            UPDATE VoteLog
            SET VoteStatus = 1
            WHERE VoteID = @VoteID
        END

        FETCH NEXT FROM cur INTO @VoteID, @AccountID
    END

    CLOSE cur
    DEALLOCATE cur
END
GO
Atenção: El nombre de la columna de W-Coins varía según la versión del MuServer. En algunas versiones es WCoinP, en otras wcoin_p o GoblinPoint. Ejecuta SELECT TOP 1 * FROM MEMB_INFO en tu base de datos para confirmar el nombre exacto de la columna antes de ejecutar la procedure.

Paso 3: Configurar el Script PHP de Recepción (Pingback)

Crea el archivo vote_callback.php en el directorio raíz de tu sitio (ej.: C:\AppServ\www\tusitio\vote_callback.php o C:\xampp\htdocs\tusitio\vote_callback.php):

<?php
// vote_callback.php — Receptor de pingback de votos
// Coloca este archivo en: /public_html/vote_callback.php o equivalente

define('DB_SERVER', 'localhost');    // IP del SQL Server
define('DB_USER',   'sa');           // usuario del SQL Server
define('DB_PASS',   'TuClave123');   // contraseña del SQL Server
define('DB_NAME',   'MuOnline');

// Clave secreta configurada en el panel de GTOP100 / Xtremetop100
define('VOTE_SECRET', 'mi_clave_secreta_aqui');

// Conexión a SQL Server vía PDO con driver SQLSRV
try {
    $dsn = "sqlsrv:Server=" . DB_SERVER . ";Database=" . DB_NAME;
    $pdo = new PDO($dsn, DB_USER, DB_PASS);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    http_response_code(500);
    exit('DB error');
}

// --- GTOP100 ---
if (isset($_GET['pingback']) && $_GET['pingback'] === VOTE_SECRET) {
    $accountID = preg_replace('/[^a-zA-Z0-9]/', '', $_GET['pingbackkey'] ?? '');
    $voteKey   = md5($_GET['pingbackkey'] . time());
    $ip        = $_SERVER['REMOTE_ADDR'];

    if (empty($accountID)) {
        http_response_code(400);
        exit('Invalid account');
    }

    // Verifica voto duplicado en las últimas 12 horas
    $stmt = $pdo->prepare("
        SELECT COUNT(*) FROM VoteLog
        WHERE AccountID = ? AND SiteID = 'gtop100'
          AND VoteDate > DATEADD(HOUR, -12, GETDATE())
    ");
    $stmt->execute([$accountID]);
    if ($stmt->fetchColumn() > 0) {
        http_response_code(200);
        exit('Already voted');
    }

    // Registra el voto
    $stmt = $pdo->prepare("
        INSERT INTO VoteLog (AccountID, SiteID, VoteKey, IPAddress)
        VALUES (?, 'gtop100', ?, ?)
    ");
    $stmt->execute([$accountID, $voteKey, $ip]);

    http_response_code(200);
    exit('OK');
}

// --- Xtremetop100 ---
if (isset($_POST['pingbackkey'])) {
    $accountID = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['pingbackkey']);
    $voteKey   = $_POST['pingbackkey'] . '_xt_' . time();
    $ip        = $_SERVER['REMOTE_ADDR'];

    $stmt = $pdo->prepare("
        SELECT COUNT(*) FROM VoteLog
        WHERE AccountID = ? AND SiteID = 'xtremetop100'
          AND VoteDate > DATEADD(HOUR, -12, GETDATE())
    ");
    $stmt->execute([$accountID]);
    if ($stmt->fetchColumn() > 0) { exit('1'); }

    $stmt = $pdo->prepare("
        INSERT INTO VoteLog (AccountID, SiteID, VoteKey, IPAddress)
        VALUES (?, 'xtremetop100', ?, ?)
    ");
    $stmt->execute([$accountID, $voteKey, $ip]);
    exit('1'); // Xtremetop100 espera '1' como confirmación
}

http_response_code(400);
exit('Bad request');
?>
Dica: Para instalar el driver PDO de SQL Server para PHP, descarga php_pdo_sqlsrv_XX_ts.dll compatible con tu versión de PHP desde microsoft.com/sqlsrv y agrega al php.ini: extension=php_pdo_sqlsrv_XX_ts.dll. Reinicia Apache/IIS después de modificar el archivo.

Paso 4: Configurar el SQL Server Agent para Ejecutar la Recompensa

La stored procedure debe ejecutarse automáticamente cada pocos minutos. En SQL Server Management Studio:

  1. Expande SQL Server AgentJobs → clic derecho → New Job
  2. En General: Nombre = EntregarRecompensaVoto
  3. En StepsNew Step:
  • Step name: Ejecutar SP
  • Type: Transact-SQL script (T-SQL)
  • Database: MuOnline
  • Command:

``sql EXEC SP_DeliverVoteReward; ``

  1. En SchedulesNew Schedule:
  • Name: Cada 5 minutos
  • Frequency: Daily, Every 5 minutes
  1. Haz clic en OK para guardar el job.
Nota: El SQL Server Agent solo funciona si el servicio está iniciado. Verifica en Servicios (services.msc) que SQL Server Agent (MSSQLSERVER) esté como En ejecución con inicio Automático.

Paso 5: Configurar los Paneles de los Sitios de Votación

GTOP100

  1. Accede a tu panel en gtop100.com → tu servidor → Edit Listing
  2. En Pingback URL ingresa: http://tudominio.com/vote_callback.php?pingback=mi_clave_secreta_aqui
  3. En Pingback Variable Name coloca: pingbackkey
  4. El jugador vota vía: http://gtop100.com/vote/TUID?pingbackkey=nombrecuenta

Xtremetop100

  1. Accede a tu panel → Edit ServerIn-game Reward Voting
  2. En Reward URL: http://tudominio.com/vote_callback.php
  3. En Variable name: pingbackkey
Atenção: La IP de tu servidor (donde corre PHP) debe estar habilitada en el firewall para recibir conexiones HTTP en el puerto 80 o 443. Para Windows Firewall, crea una regla de entrada: netsh advfirewall firewall add rule name="HTTP Vote" protocol=TCP dir=in localport=80 action=allow

Paso 6: Crear la Página de Voto en el Sitio del Servidor

Agrega al sitio un formulario donde el jugador ingresa su nombre de cuenta antes de ser redirigido a votar:

<!-- vote.php — Página de voto en el sitio del servidor -->
<form action="vote.php" method="post">
    <label>Nombre de Cuenta (Login):</label>
    <input type="text" name="account" maxlength="10" required>
    <button type="submit">Votar y Ganar Recompensa</button>
</form>

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $account = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['account'] ?? '');
    if (!empty($account)) {
        $gtop_url = "https://gtop100.com/topsites/MuOnline/TUID/vote?pingbackkey=" . urlencode($account);
        $xt_url   = "https://www.xtremetop100.com/in.php?site=TUID&postback=" . urlencode($account);
        echo "<p><a href='$gtop_url' target='_blank'>Votar en GTOP100 (+200 W-Coins)</a></p>";
        echo "<p><a href='$xt_url' target='_blank'>Votar en Xtremetop100 (+200 W-Coins)</a></p>";
    }
}
?>

Paso 7: Verificar y Monitorear el Sistema

Para revisar votos recientes y estado de entrega directamente en SSMS:

-- Votos de las últimas 24 horas
SELECT VoteID, AccountID, SiteID, VoteDate,
       CASE VoteStatus WHEN 0 THEN 'Pendiente' ELSE 'Entregado' END AS Estado
FROM VoteLog
WHERE VoteDate > DATEADD(HOUR, -24, GETDATE())
ORDER BY VoteDate DESC;

-- Total de votos por cuenta (ranking de votación)
SELECT AccountID, COUNT(*) AS TotalVotos
FROM VoteLog
WHERE VoteStatus = 1
GROUP BY AccountID
ORDER BY TotalVotos DESC;

-- Forzar entrega manual de votos pendientes
EXEC SP_DeliverVoteReward;
Dica: Crea una página /admin/votos.php protegida por contraseña que muestre esta consulta como tabla HTML — facilita atender reclamos de jugadores sin necesidad de abrir SSMS cada vez.

Solución Rápida de Problemas

ProblemaCausa probableSolución
Pingback no llegaFirewall bloqueando puerto 80Verificar regla en Windows Firewall y en el panel de hosting
AccountID no encontradoNombre de cuenta escrito incorrectamenteValidar contra tabla MEMB_INFO antes de aceptar el voto
W-Coins no sumadosNombre de columna incorrecto en MEMB_INFOSELECT TOP 1 * FROM MEMB_INFO para verificar columnas reales
Job de SQL Agent no ejecutaServicio Agent detenidoservices.msc → iniciar SQL Server Agent
Voto duplicado aceptadoFalta verificación de ventana de tiempoAgregar WHERE VoteDate > DATEADD(HOUR, -12, GETDATE())
Nota: Muchos sitios de votación envían el pingback desde una IP diferente a la IP del jugador. Nunca uses la IP del pingback para validar la identidad del votante — usa exclusivamente el nombre de cuenta enviado como parámetro.

Perguntas frequentes

El jugador votó pero no recibió la recompensa. ¿Qué debo verificar?

Primero confirma que el VoteKey devuelto por el pingback se está guardando correctamente en la tabla VoteLog. Verifica que el AccountID coincida exactamente con el nombre de cuenta del jugador (sensible a mayúsculas en GTOP100). Luego ejecuta: SELECT * FROM MuOnline.dbo.VoteLog WHERE AccountID = 'nombrecuenta' ORDER BY VoteDate DESC — si el registro existe pero la recompensa no llegó, el problema está en el script PHP de entrega o en la programación de la stored procedure.

¿Puedo recompensar con más de un ítem por voto?

Sí. Solo agrega múltiples sentencias INSERT dentro de la stored procedure SP_DeliverVoteReward, una por ítem. Usa el mismo CharacterName y ajusta los campos ItemCode, ItemLevel, ItemDur, ItemOpt según los ítems deseados. Ten en cuenta que la tabla de ítems (generalmente MEMB_ITEMS o equivalente) tiene límite de slots — verifica que el inventario del personaje no esté lleno.

¿Cuál es la diferencia entre recompensar vía base de datos y vía comando en juego?

Vía base de datos (INSERT directo o stored procedure) el ítem se entrega incluso con el servidor offline, pero requiere que el personaje esté desconectado en el momento de la entrega o que uses la tabla de entrega de ítems del MuServer. Vía comando en juego (/add_item o similar) el GameServer ejecuta inmediatamente, pero el personaje debe estar online y el servidor en funcionamiento.

¿Cómo evito que un jugador vote varias veces antes de recibir la recompensa?

Agrega un campo VoteStatus en la tabla VoteLog (0=pendiente, 1=entregado). Antes de aceptar un nuevo voto, ejecuta: SELECT COUNT(*) FROM VoteLog WHERE AccountID = @AccountID AND VoteStatus = 0 — si devuelve > 0, rechaza el nuevo registro. Esto evita la acumulación de recompensas no cobradas y detecta posibles intentos de explotación del sistema.

EQ

Equipo ViciadosMU

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

Sigue leyendo

Artículos relacionados