Como Criar Sistema de Voto e Recompensa no Servidor de MU Online
Aprenda a implementar um sistema de voto com recompensa automática no seu servidor de MU Online usando SQL Server, PHP e sites de votação como GTOP100 e Xtremetop100.
Visão Geral do Sistema
Um sistema de voto e recompensa incentiva os jogadores a divulgarem seu servidor nos principais sites de ranking de servidores privados de MU Online, como GTOP100, Xtremetop100 e MuOnline.com.br. Cada voto contabiliza pontos no ranking e, em contrapartida, o jogador recebe itens, Zen, W-Coins ou resets automáticos.
O fluxo completo funciona assim:
Site de votação → Pingback HTTP para seu servidor → Script PHP recebe e valida → Registro no SQL Server → Stored Procedure entrega a recompensa
Passo 1: Criar a Tabela de Controle de Votos no SQL Server
Abra o SQL Server Management Studio (SSMS) e conecte ao banco MuOnline. 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, -- chave única retornada pelo site
VoteDate DATETIME NOT NULL DEFAULT GETDATE(),
VoteStatus TINYINT NOT NULL DEFAULT 0, -- 0=pendente, 1=entregue
IPAddress VARCHAR(45) NOT NULL DEFAULT ''
);
GO
-- Índice para consultas rápidas por conta
CREATE INDEX IX_VoteLog_AccountID ON VoteLog (AccountID, VoteStatus);
GO
VoteKey armazena o token único que o site de votação envia no pingback. Ele serve como prova de voto legítimo e evita inserções duplicadas — crie uma constraint UNIQUE nele se quiser proteção extra: ALTER TABLE VoteLog ADD CONSTRAINT UQ_VoteKey UNIQUE (VoteKey);Passo 2: Criar a Stored Procedure de Entrega de Recompensa
A stored procedure lê votos pendentes e insere os itens de recompensa na tabela de personagem. Ajuste ItemCode, ItemLevel e ItemOpt conforme a recompensa que deseja oferecer.
USE MuOnline;
GO
CREATE PROCEDURE SP_DeliverVoteReward
AS
BEGIN
SET NOCOUNT ON;
DECLARE @AccountID VARCHAR(10)
DECLARE @CharName VARCHAR(10)
DECLARE @VoteID INT
-- Cursor percorre todos os votos pendentes
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
-- Pega o personagem mais recente da conta
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 (ajuste WCoinP conforme seu sistema de coins)
UPDATE MEMB_INFO
SET WCoinP = ISNULL(WCoinP, 0) + 200
WHERE memb___id = @AccountID
-- Opcional: entregar item físico via tabela de depósito
-- INSERT INTO ITEM_STORE (AccountID, ItemCode, ItemLevel, ItemDur, ItemOpt)
-- VALUES (@AccountID, 7936, 0, 255, 0) -- exemplo: Box of Kundun+5
-- Marca voto como entregue
UPDATE VoteLog
SET VoteStatus = 1
WHERE VoteID = @VoteID
END
FETCH NEXT FROM cur INTO @VoteID, @AccountID
END
CLOSE cur
DEALLOCATE cur
END
GO
WCoinP, em outras é wcoin_p ou GoblinPoint. Execute SELECT TOP 1 * FROM MEMB_INFO no seu banco para confirmar o nome exato da coluna antes de rodar a procedure.Passo 3: Configurar o Script PHP de Recebimento (Pingback)
Crie o arquivo vote_callback.php no diretório raiz do seu site (ex.: C:\AppServ\www\seusite\vote_callback.php ou C:\xampp\htdocs\seusite\vote_callback.php):
<?php
// vote_callback.php — Receptor de pingback de votos
// Coloque este arquivo em: /public_html/vote_callback.php ou equivalente
define('DB_SERVER', 'localhost'); // IP do SQL Server
define('DB_USER', 'sa'); // usuário do SQL Server
define('DB_PASS', 'SuaSenha123'); // senha do SQL Server
define('DB_NAME', 'MuOnline');
// Chave secreta configurada no painel do GTOP100 / Xtremetop100
define('VOTE_SECRET', 'minha_chave_secreta_aqui');
// Conecta ao SQL Server via PDO com 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 nas ú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 o 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 confirmação
}
http_response_code(400);
exit('Bad request');
?>
php_pdo_sqlsrv_XX_ts.dll compatível com sua versão do PHP em microsoft.com/sqlsrv e adicione ao php.ini: extension=php_pdo_sqlsrv_XX_ts.dll. Reinicie o Apache/IIS após alterar o arquivo.Passo 4: Configurar o SQL Server Agent para Executar a Recompensa
A stored procedure precisa ser executada automaticamente a cada poucos minutos. No SQL Server Management Studio:
- Expanda SQL Server Agent → Jobs → clique direito → New Job
- Em General: Nome =
EntregarRecompensaVoto - Em Steps → New Step:
- Step name:
Executar SP - Type:
Transact-SQL script (T-SQL) - Database:
MuOnline - Command:
``sql EXEC SP_DeliverVoteReward; ``
- Em Schedules → New Schedule:
- Name:
A cada 5 minutos - Frequency:
Daily, Every5minutes
- Clique em OK para salvar o job.
SQL Server Agent (MSSQLSERVER) está como Running e com startup Automatic.Passo 5: Configurar o Painel dos Sites de Votação
GTOP100
- Acesse seu painel em gtop100.com → seu servidor → Edit Listing
- Em Pingback URL insira:
http://seudominio.com/vote_callback.php?pingback=minha_chave_secreta_aqui - Em Pingback Variable Name coloque:
pingbackkey - O jogador acessa a URL de voto:
http://gtop100.com/vote/SEUID?pingbackkey=nomeconta
Xtremetop100
- Acesse seu painel → Edit Server → In-game Reward Voting
- Em Reward URL:
http://seudominio.com/vote_callback.php - Em Variable name:
pingbackkey
netsh advfirewall firewall add rule name="HTTP Vote" protocol=TCP dir=in localport=80 action=allowPasso 6: Criar a Página de Voto no Site do Servidor
Adicione ao site um formulário onde o jogador informa o nome de conta antes de ser redirecionado:
<!-- vote.php — Página de voto no site do servidor -->
<form action="vote.php" method="post">
<label>Nome de Conta (Login):</label>
<input type="text" name="account" maxlength="10" required>
<button type="submit">Votar e Ganhar 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/SEUID/vote?pingbackkey=" . urlencode($account);
$xt_url = "https://www.xtremetop100.com/in.php?site=SEUID&postback=" . urlencode($account);
echo "<p><a href='$gtop_url' target='_blank'>Votar no GTOP100 (+200 W-Coins)</a></p>";
echo "<p><a href='$xt_url' target='_blank'>Votar no Xtremetop100 (+200 W-Coins)</a></p>";
}
}
?>
Passo 7: Verificar e Monitorar o Sistema
Para checar votos recentes e status de entrega diretamente no SSMS:
-- Votos das últimas 24 horas
SELECT VoteID, AccountID, SiteID, VoteDate,
CASE VoteStatus WHEN 0 THEN 'Pendente' ELSE 'Entregue' END AS Status
FROM VoteLog
WHERE VoteDate > DATEADD(HOUR, -24, GETDATE())
ORDER BY VoteDate DESC;
-- Total de votos por conta (ranking de votação)
SELECT AccountID, COUNT(*) AS TotalVotos
FROM VoteLog
WHERE VoteStatus = 1
GROUP BY AccountID
ORDER BY TotalVotos DESC;
-- Forçar entrega manual de um voto específico
EXEC SP_DeliverVoteReward;
/admin/votos.php protegida por senha que exibe essa consulta em tabela HTML — facilita monitorar reclamações de jogadores sem precisar abrir o SSMS toda vez.Troubleshooting Rápido
| Problema | Causa provável | Solução |
|---|---|---|
| Pingback não chega | Firewall bloqueando porta 80 | Verificar regra no Windows Firewall e no painel do hosting |
AccountID não encontrado | Nome de conta digitado errado | Validar contra tabela MEMB_INFO antes de aceitar o voto |
| W-Coins não somados | Coluna errada na MEMB_INFO | SELECT TOP 1 * FROM MEMB_INFO para checar colunas reais |
| Job do SQL Agent não roda | Serviço Agent parado | services.msc → iniciar SQL Server Agent |
| Voto duplicado aceito | Falta de verificação de tempo | Adicionar WHERE VoteDate > DATEADD(HOUR, -12, GETDATE()) |
Perguntas frequentes
O jogador votou mas não recebeu a recompensa. O que verificar?
Primeiro confirme que o VoteKey retornou pelo pingback está sendo gravado corretamente na tabela VoteLog. Verifique se o campo AccountID bate exatamente com o nome de conta do jogador (case-sensitive no GTOP100). Em seguida rode: SELECT * FROM MuOnline.dbo.VoteLog WHERE AccountID = 'nomeconta' ORDER BY VoteDate DESC — se o registro existe mas a recompensa não chegou, o problema está no script PHP de delivery ou no agendamento da stored procedure.
Posso recompensar com mais de um item por voto?
Sim. Basta adicionar múltiplas linhas INSERT na stored procedure SP_DeliverVoteReward, uma para cada item. Use o mesmo CharacterName e ajuste os campos ItemCode, ItemLevel, ItemDur, ItemOpt conforme os itens desejados. Lembre que a tabela de itens no banco (geralmente MEMB_ITEMS ou equivalente) tem limite de slots — verifique se o personagem não está com inventário cheio.
Qual a diferença entre recompensar via banco de dados e via comando in-game?
Via banco de dados (INSERT direto ou stored procedure) o item é entregue mesmo com o servidor offline, mas exige que o personagem esteja desconectado no momento da entrega ou que você use a tabela de itens de 'delivery' do MuServer. Via comando in-game (/add_item ou similar) o GameServer executa imediatamente, mas o personagem precisa estar online e o servidor operacional.
Como evitar que o jogador vote várias vezes antes de receber a recompensa?
Adicione um campo VoteStatus na tabela VoteLog (0=pendente, 1=entregue). Antes de aceitar novo voto, faça: SELECT COUNT(*) FROM VoteLog WHERE AccountID = @AccountID AND VoteStatus = 0 — se retornar > 0, recuse o novo registro. Isso impede acúmulo de recompensas não coletadas e também detecta possíveis tentativas de explorar o sistema.