Brazil's biggest MU Online portal — since 2003
Tutorial Intermediate Tutoriais

How to Create Vote and Reward System on MU Online Server

Learn how to implement an automatic vote and reward system on your MU Online server using SQL Server, PHP, and voting sites like GTOP100 and Xtremetop100.

VI ViciadosMU Team · Updated on 3 jul 2026 · ⏱ 12 min read

System Overview

A vote and reward system encourages players to promote your server on major private MU Online ranking websites such as GTOP100, Xtremetop100, and MuOnline.com.br. Each vote earns ranking points and, in return, the player receives items, Zen, W-Coins, or automatic resets.

The full flow works as follows:

Voting site → HTTP Pingback to your server → PHP script receives and validates → Record in SQL Server → Stored Procedure delivers the reward

Nota: This tutorial covers Season 6 servers running MuServer on SQL Server 2008/2012/2014. For Season 13+, table names may vary — confirm by inspecting your database schema before running any command.

Step 1: Create the Vote Control Table in SQL Server

Open SQL Server Management Studio (SSMS) and connect to the MuOnline database. Execute:

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,  -- unique key returned by the voting site
    VoteDate    DATETIME     NOT NULL DEFAULT GETDATE(),
    VoteStatus  TINYINT      NOT NULL DEFAULT 0,  -- 0=pending, 1=delivered
    IPAddress   VARCHAR(45)  NOT NULL DEFAULT ''
);
GO

-- Index for fast account-based queries
CREATE INDEX IX_VoteLog_AccountID ON VoteLog (AccountID, VoteStatus);
GO
Dica: The VoteKey field stores the unique token the voting site sends in the pingback. It serves as proof of a legitimate vote and prevents duplicate inserts — add a UNIQUE constraint for extra protection: ALTER TABLE VoteLog ADD CONSTRAINT UQ_VoteKey UNIQUE (VoteKey);

Step 2: Create the Reward Delivery Stored Procedure

The stored procedure reads pending votes and inserts reward items into the character table. Adjust ItemCode, ItemLevel, and ItemOpt to match the reward you want to offer.

USE MuOnline;
GO

CREATE PROCEDURE SP_DeliverVoteReward
AS
BEGIN
    SET NOCOUNT ON;

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

    -- Cursor iterates over all pending votes
    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
        -- Get the most recently active character from the account
        SELECT TOP 1 @CharName = Name
        FROM Character
        WHERE AccountID = @AccountID
        ORDER BY ConnectStat DESC, ResetCount DESC

        IF @CharName IS NOT NULL
        BEGIN
            -- Deliver 200 W-Coins (adjust WCoinP to match your coins column)
            UPDATE MEMB_INFO
            SET WCoinP = ISNULL(WCoinP, 0) + 200
            WHERE memb___id = @AccountID

            -- Optional: deliver a physical item via deposit table
            -- INSERT INTO ITEM_STORE (AccountID, ItemCode, ItemLevel, ItemDur, ItemOpt)
            -- VALUES (@AccountID, 7936, 0, 255, 0)  -- example: Box of Kundun+5

            -- Mark vote as delivered
            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: The W-Coins column name varies by MuServer version. In some versions it is WCoinP, in others wcoin_p or GoblinPoint. Run SELECT TOP 1 * FROM MEMB_INFO on your database to confirm the exact column name before executing the procedure.

Step 3: Set Up the PHP Pingback Receiver Script

Create the file vote_callback.php in your site's root directory (e.g., C:\AppServ\www\yoursite\vote_callback.php or C:\xampp\htdocs\yoursite\vote_callback.php):

<?php
// vote_callback.php — Vote pingback receiver
// Place this file at: /public_html/vote_callback.php or equivalent

define('DB_SERVER', 'localhost');   // SQL Server IP
define('DB_USER',   'sa');          // SQL Server user
define('DB_PASS',   'YourPass123'); // SQL Server password
define('DB_NAME',   'MuOnline');

// Secret key configured in the GTOP100 / Xtremetop100 panel
define('VOTE_SECRET', 'my_secret_key_here');

// Connect to SQL Server via PDO with SQLSRV driver
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');
    }

    // Check for duplicate vote within the last 12 hours
    $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');
    }

    // Register the vote
    $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 expects '1' as confirmation
}

http_response_code(400);
exit('Bad request');
?>
Dica: To install the SQL Server PDO driver for PHP, download php_pdo_sqlsrv_XX_ts.dll compatible with your PHP version from microsoft.com/sqlsrv and add to php.ini: extension=php_pdo_sqlsrv_XX_ts.dll. Restart Apache/IIS after editing the file.

Step 4: Configure SQL Server Agent to Execute the Reward Procedure

The stored procedure needs to run automatically every few minutes. In SQL Server Management Studio:

  1. Expand SQL Server AgentJobs → right-click → New Job
  2. Under General: Name = DeliverVoteReward
  3. Under StepsNew Step:
  • Step name: Execute SP
  • Type: Transact-SQL script (T-SQL)
  • Database: MuOnline
  • Command:

``sql EXEC SP_DeliverVoteReward; ``

  1. Under SchedulesNew Schedule:
  • Name: Every 5 minutes
  • Frequency: Daily, Every 5 minutes
  1. Click OK to save the job.
Nota: SQL Server Agent only works if the service is started. Check in Services (services.msc) that SQL Server Agent (MSSQLSERVER) is Running with startup set to Automatic.

Step 5: Configure the Voting Site Panels

GTOP100

  1. Log in to your panel at gtop100.com → your server → Edit Listing
  2. In Pingback URL enter: http://yourdomain.com/vote_callback.php?pingback=my_secret_key_here
  3. In Pingback Variable Name set: pingbackkey
  4. Players vote via: http://gtop100.com/vote/YOURID?pingbackkey=accountname

Xtremetop100

  1. Log in to your panel → Edit ServerIn-game Reward Voting
  2. In Reward URL: http://yourdomain.com/vote_callback.php
  3. In Variable name: pingbackkey
Atenção: Your server's IP (where PHP runs) must be allowed in the firewall to receive HTTP connections on port 80 or 443. For Windows Firewall, create an inbound rule: netsh advfirewall firewall add rule name="HTTP Vote" protocol=TCP dir=in localport=80 action=allow

Step 6: Create the Vote Page on Your Server Website

Add a form to your site where the player enters their account name before being redirected to vote:

<!-- vote.php — Vote page on the server website -->
<form action="vote.php" method="post">
    <label>Account Name (Login):</label>
    <input type="text" name="account" maxlength="10" required>
    <button type="submit">Vote and Earn Reward</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/YOURID/vote?pingbackkey=" . urlencode($account);
        $xt_url   = "https://www.xtremetop100.com/in.php?site=YOURID&postback=" . urlencode($account);
        echo "<p><a href='$gtop_url' target='_blank'>Vote on GTOP100 (+200 W-Coins)</a></p>";
        echo "<p><a href='$xt_url' target='_blank'>Vote on Xtremetop100 (+200 W-Coins)</a></p>";
    }
}
?>

Step 7: Verify and Monitor the System

To check recent votes and delivery status directly in SSMS:

-- Votes in the last 24 hours
SELECT VoteID, AccountID, SiteID, VoteDate,
       CASE VoteStatus WHEN 0 THEN 'Pending' ELSE 'Delivered' END AS Status
FROM VoteLog
WHERE VoteDate > DATEADD(HOUR, -24, GETDATE())
ORDER BY VoteDate DESC;

-- Total votes per account (vote ranking)
SELECT AccountID, COUNT(*) AS TotalVotes
FROM VoteLog
WHERE VoteStatus = 1
GROUP BY AccountID
ORDER BY TotalVotes DESC;

-- Force manual delivery of pending votes
EXEC SP_DeliverVoteReward;
Dica: Build a password-protected /admin/votes.php page that displays this query as an HTML table — it makes handling player complaints much faster without needing to open SSMS every time.

Quick Troubleshooting

ProblemLikely CauseSolution
Pingback not arrivingFirewall blocking port 80Check Windows Firewall rule and hosting panel
AccountID not foundAccount name typed incorrectlyValidate against MEMB_INFO table before accepting the vote
W-Coins not addedWrong column name in MEMB_INFORun SELECT TOP 1 * FROM MEMB_INFO to check real column names
SQL Agent job not runningAgent service stoppedservices.msc → start SQL Server Agent
Duplicate vote acceptedMissing time-window checkAdd WHERE VoteDate > DATEADD(HOUR, -12, GETDATE())
Nota: Many voting sites send the pingback from a different IP than the player's IP. Never use the pingback IP to validate the voter's identity — use only the account name sent as a parameter.

Perguntas frequentes

The player voted but did not receive the reward. What should I check?

First confirm that the VoteKey returned by the pingback is being correctly saved to the VoteLog table. Verify that the AccountID matches the player's account name exactly (case-sensitive on GTOP100). Then run: SELECT * FROM MuOnline.dbo.VoteLog WHERE AccountID = 'accountname' ORDER BY VoteDate DESC — if the record exists but the reward was not delivered, the issue is in the PHP delivery script or the stored procedure scheduling.

Can I reward more than one item per vote?

Yes. Simply add multiple INSERT statements inside the stored procedure SP_DeliverVoteReward, one per item. Use the same CharacterName and adjust ItemCode, ItemLevel, ItemDur, ItemOpt fields for each desired item. Keep in mind that the item table (usually MEMB_ITEMS or equivalent) has a slot limit — check that the character's inventory is not full.

What is the difference between rewarding via database and via in-game command?

Via database (direct INSERT or stored procedure), the item is delivered even when the server is offline, but requires the character to be disconnected at the time of delivery or that you use MuServer's delivery item table. Via in-game command (/add_item or similar), the GameServer executes immediately, but the character must be online and the server must be running.

How do I prevent a player from voting multiple times before receiving the reward?

Add a VoteStatus field to the VoteLog table (0=pending, 1=delivered). Before accepting a new vote, run: SELECT COUNT(*) FROM VoteLog WHERE AccountID = @AccountID AND VoteStatus = 0 — if it returns > 0, reject the new record. This prevents reward accumulation and also detects potential exploitation attempts.

VI

ViciadosMU Team

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

Keep reading

Related articles

🛡️
Tutorial

How to Implement Referral System on MU Server

Learn to build a complete referral system for your MU Online server, including SQL tables, reward logic, and website integration with anti-abuse controls.

12 min · Advanced
🐬
Tutorial

Install MySQL and phpMyAdmin for the MU Online server website

Complete guide to installing MySQL and phpMyAdmin for a MU Online server website: the critical difference between MySQL (for the website) and SQL Server (for the game), when to install MySQL separately vs using XAMPP or AppServ, the full MySQL Community Server installation walkthrough (7 steps including the authentication method choice for PHP compatibility), how to install phpMyAdmin manually and configure config.inc.php, how to create the website database and import the web system's SQL file, how to create a dedicated MySQL user (never use root in the site config), common MySQL connection errors and how to fix them, and security best practices (no port 3306 exposed, strong root password, dedicated user).

12 min · Beginner
🧰
Tutorial

How to install XAMPP for a MU Online server website

Complete guide to installing XAMPP for running a MU Online server's website: what XAMPP includes (Apache, PHP, MySQL/MariaDB, phpMyAdmin), the critical difference between XAMPP's MySQL and SQL Server (the game database), how to choose the right PHP version for your web system, the full XAMPP installation and control panel walkthrough, where to put the website files (htdocs folder), how to create and import the MySQL database for the web system via phpMyAdmin, how to install IonCube Loader in XAMPP for PHP-encoded MU web systems, the four most common XAMPP problems and fixes (port 80 conflict, MySQL not starting, wrong PHP version, blank page), and how to use XAMPP on a VPS vs a local development machine.

12 min · Beginner