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.
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
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
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
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');
?>
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:
- Expand SQL Server Agent → Jobs → right-click → New Job
- Under General: Name =
DeliverVoteReward - Under Steps → New Step:
- Step name:
Execute SP - Type:
Transact-SQL script (T-SQL) - Database:
MuOnline - Command:
``sql EXEC SP_DeliverVoteReward; ``
- Under Schedules → New Schedule:
- Name:
Every 5 minutes - Frequency:
Daily, Every5minutes
- Click OK to save the job.
SQL Server Agent (MSSQLSERVER) is Running with startup set to Automatic.Step 5: Configure the Voting Site Panels
GTOP100
- Log in to your panel at gtop100.com → your server → Edit Listing
- In Pingback URL enter:
http://yourdomain.com/vote_callback.php?pingback=my_secret_key_here - In Pingback Variable Name set:
pingbackkey - Players vote via:
http://gtop100.com/vote/YOURID?pingbackkey=accountname
Xtremetop100
- Log in to your panel → Edit Server → In-game Reward Voting
- In Reward URL:
http://yourdomain.com/vote_callback.php - In Variable name:
pingbackkey
netsh advfirewall firewall add rule name="HTTP Vote" protocol=TCP dir=in localport=80 action=allowStep 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;
/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
| Problem | Likely Cause | Solution |
|---|---|---|
| Pingback not arriving | Firewall blocking port 80 | Check Windows Firewall rule and hosting panel |
AccountID not found | Account name typed incorrectly | Validate against MEMB_INFO table before accepting the vote |
| W-Coins not added | Wrong column name in MEMB_INFO | Run SELECT TOP 1 * FROM MEMB_INFO to check real column names |
| SQL Agent job not running | Agent service stopped | services.msc → start SQL Server Agent |
| Duplicate vote accepted | Missing time-window check | Add WHERE VoteDate > DATEADD(HOUR, -12, GETDATE()) |
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.