Como Implementar Sistema de Ranking Automático no Website do MU
Aprenda a criar um sistema de ranking automático integrado ao banco de dados do seu servidor MU Online, com atualização em tempo real via SQL e PHP.
Exibir um ranking atualizado automaticamente é um dos recursos mais valorizados pelos jogadores de servidores MU Online. Este guia explica como integrar o banco de dados SQL Server do seu servidor MuServer (Season 6 em diante) com um website PHP para gerar rankings de personagens, resets e castles — sem intervenção manual.
Pré-requisitos
Antes de começar, confirme que você possui:
- Acesso ao SQL Server Management Studio (SSMS) com permissão de leitura/escrita no banco
MuOnline - PHP 7.4+ com extensão
sqlsrvoupdo_sqlsrvinstalada (XAMPP ou AppServ no Windows Server) - Acesso ao SQL Server Agent para agendar Jobs
- Permissão para criar tabelas e stored procedures no banco de dados
MuOnline do MuServer Season 6. Versões anteriores (S2/S3) usam estruturas de tabela ligeiramente diferentes — adapte os nomes de colunas conforme necessário consultando sua tabela Character.Passo 1: Entender a Estrutura das Tabelas Relevantes
O MuServer armazena os dados de personagem na tabela Character dentro do banco MuOnline. As colunas mais usadas para ranking são:
-- Verificar estrutura da tabela Character
USE MuOnline;
SELECT COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Character'
ORDER BY ORDINAL_POSITION;
Colunas essenciais para ranking:
| Coluna | Descrição |
|---|---|
Name | Nome do personagem |
AccountID | Conta vinculada |
cLevel | Level atual |
Class | Classe do personagem (byte) |
Resets | Número de resets (se configurado) |
PkCount | Contagem de PK |
Money | Zen do personagem |
MapNumber | Mapa atual (para verificar se está online) |
Passo 2: Criar a Tabela de Cache de Ranking
Em vez de consultar a tabela Character diretamente a cada requisição do website, crie uma tabela de cache dedicada. Isso evita lentidão no banco de dados durante picos de acesso.
USE MuOnline;
GO
CREATE TABLE RankingCache (
RankPosition INT IDENTITY(1,1) PRIMARY KEY,
CharacterName VARCHAR(10) NOT NULL,
AccountID VARCHAR(10) NOT NULL,
CharClass TINYINT NOT NULL,
CharLevel SMALLINT NOT NULL,
CharResets INT NOT NULL DEFAULT 0,
CharZen BIGINT NOT NULL DEFAULT 0,
GuildName VARCHAR(8) NULL,
LastUpdate DATETIME NOT NULL DEFAULT GETDATE(),
RankType VARCHAR(20) NOT NULL DEFAULT 'level'
);
CREATE INDEX IX_RankingCache_Type ON RankingCache (RankType, RankPosition);
GO
Passo 3: Criar a Stored Procedure de Atualização
A stored procedure abaixo apaga e recria os dados de cache para cada tipo de ranking. Execute-a no SSMS para criá-la:
USE MuOnline;
GO
CREATE PROCEDURE sp_UpdateRankingCache
AS
BEGIN
SET NOCOUNT ON;
-- Limpar cache anterior
DELETE FROM RankingCache;
-- Ranking por Level e Resets
INSERT INTO RankingCache (CharacterName, AccountID, CharClass, CharLevel, CharResets, CharZen, GuildName, RankType)
SELECT TOP 200
C.Name,
C.AccountID,
C.Class,
C.cLevel,
ISNULL(C.Resets, 0),
C.Money,
G.G_Name,
'level'
FROM Character C
LEFT JOIN GuildMember GM ON GM.Name = C.Name
LEFT JOIN Guild G ON G.G_Name = GM.G_Name
WHERE C.cLevel > 0
AND C.AccountID NOT IN (
SELECT memb___id FROM MEMB_INFO WHERE memb_stat = 1
)
ORDER BY ISNULL(C.Resets, 0) DESC, C.cLevel DESC, C.Money DESC;
-- Ranking por Zen (riqueza)
INSERT INTO RankingCache (CharacterName, AccountID, CharClass, CharLevel, CharResets, CharZen, GuildName, RankType)
SELECT TOP 100
C.Name,
C.AccountID,
C.Class,
C.cLevel,
ISNULL(C.Resets, 0),
C.Money,
G.G_Name,
'zen'
FROM Character C
LEFT JOIN GuildMember GM ON GM.Name = C.Name
LEFT JOIN Guild G ON G.G_Name = GM.G_Name
WHERE C.cLevel > 0
AND C.AccountID NOT IN (
SELECT memb___id FROM MEMB_INFO WHERE memb_stat = 1
)
ORDER BY C.Money DESC;
-- Atualizar timestamp
UPDATE RankingCache SET LastUpdate = GETDATE();
PRINT 'RankingCache atualizado com sucesso.';
END;
GO
memb_stat = 1 na exclusão para filtrar contas de GM. Se seu servidor usar uma coluna diferente para marcar administradores, ajuste o filtro conforme a estrutura do seu MEMB_INFO.Passo 4: Agendar Atualização Automática via SQL Server Agent
Crie um Job no SQL Server Agent para executar a procedure automaticamente a cada 10 minutos:
- Abra o SSMS → expanda SQL Server Agent → clique com o botão direito em Jobs → New Job
- General tab: Name =
Atualizar Ranking MU - Steps tab → New Step:
- Step Name:
Executar sp_UpdateRankingCache - Type:
Transact-SQL script (T-SQL) - Database:
MuOnline - Command:
EXEC sp_UpdateRankingCache
- Schedules tab → New Schedule:
- Name:
A cada 10 minutos - Frequency:
Daily - Daily frequency:
Occurs every 10 minutes - Start/End:
00:00:00a23:59:59
- Clique em OK para salvar.
Para testar imediatamente, clique com o botão direito no Job → Start Job at Step.
services.msc). Se estiver parado, os Jobs não serão executados e o ranking ficará desatualizado.Passo 5: Configurar a Conexão PHP ao SQL Server
No seu website, crie o arquivo de configuração de banco de dados. Salve em /web/includes/db_config.php:
<?php
// /web/includes/db_config.php
define('DB_SERVER', '127.0.0.1'); // IP do SQL Server
define('DB_PORT', '1433');
define('DB_USER', 'mu_web_user'); // usuário com permissão SELECT
define('DB_PASS', 'SuaSenhaAqui');
define('DB_NAME', 'MuOnline');
function getDbConnection() {
$connectionOptions = [
'Database' => DB_NAME,
'Uid' => DB_USER,
'PWD' => DB_PASS,
'CharacterSet' => 'UTF-8',
'TrustServerCertificate' => true,
];
$conn = sqlsrv_connect(DB_SERVER . ', ' . DB_PORT, $connectionOptions);
if ($conn === false) {
error_log('Falha na conexão DB: ' . print_r(sqlsrv_errors(), true));
return null;
}
return $conn;
}
?>
SELECT na tabela RankingCache. Nunca use sa ou o usuário do GameServer para conexões web.Passo 6: Criar a Página de Ranking em PHP
Salve o arquivo em /web/ranking.php:
<?php
require_once 'includes/db_config.php';
$type = isset($_GET['type']) ? $_GET['type'] : 'level';
$type = in_array($type, ['level', 'zen']) ? $type : 'level';
$page = max(1, (int)($_GET['page'] ?? 1));
$limit = 25;
$offset = ($page - 1) * $limit;
// Mapa de classes Season 6
$classNames = [
0 => 'Dark Wizard', 1 => 'Soul Master', 2 => 'Grand Master',
16 => 'Dark Knight', 17 => 'Blade Knight', 18 => 'Blade Master',
32 => 'Fairy Elf', 33 => 'Muse Elf', 34 => 'High Elf',
48 => 'Magic Gladiator', 64 => 'Dark Lord',
80 => 'Summoner', 96 => 'Rage Fighter',
];
$conn = getDbConnection();
$sql = "SELECT CharacterName, CharClass, CharLevel, CharResets, CharZen, GuildName,
ROW_NUMBER() OVER (PARTITION BY RankType ORDER BY RankPosition) AS Pos
FROM RankingCache
WHERE RankType = ?
ORDER BY RankPosition
OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
$params = [$type, $offset, $limit];
$result = sqlsrv_query($conn, $sql, $params);
$lastUpdate = '';
$luSql = "SELECT TOP 1 LastUpdate FROM RankingCache WHERE RankType = ?";
$luRes = sqlsrv_query($conn, $luSql, [$type]);
if ($luRow = sqlsrv_fetch_array($luRes, SQLSRV_FETCH_ASSOC)) {
$lastUpdate = $luRow['LastUpdate']->format('d/m/Y H:i');
}
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head><meta charset="UTF-8"><title>Ranking - ViciadosMU</title></head>
<body>
<h1>Ranking <?= $type === 'zen' ? 'Zen' : 'Level/Resets' ?></h1>
<p>Última atualização: <?= htmlspecialchars($lastUpdate) ?></p>
<table border="1" cellpadding="6">
<tr><th>#</th><th>Personagem</th><th>Classe</th><th>Level</th><th>Resets</th><th>Guild</th></tr>
<?php
$rank = $offset + 1;
while ($row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC)):
$cls = $classNames[$row['CharClass']] ?? 'Desconhecido';
?>
<tr>
<td><?= $rank++ ?></td>
<td><?= htmlspecialchars($row['CharacterName']) ?></td>
<td><?= htmlspecialchars($cls) ?></td>
<td><?= (int)$row['CharLevel'] ?></td>
<td><?= (int)$row['CharResets'] ?></td>
<td><?= htmlspecialchars($row['GuildName'] ?? '-') ?></td>
</tr>
<?php endwhile; ?>
</table>
</body>
</html>
Passo 7: Resolver Problemas Comuns
Ranking não atualiza
- Verifique o serviço SQL Server Agent →
services.msc→ SQL Server Agent (MSSQLSERVER) - Execute manualmente no SSMS:
EXEC MuOnline..sp_UpdateRankingCache - Consulte erros em:
SSMS → SQL Server Agent → Error Logs
Página PHP retorna tela em branco
// Adicione no topo do arquivo para depuração (remova em produção)
ini_set('display_errors', 1);
error_reporting(E_ALL);
Verifique se a extensão sqlsrv está habilitada no php.ini:
; No php.ini do XAMPP (C:\xampp\php\php.ini)
extension=php_sqlsrv_74_ts_x64.dll
extension=php_pdo_sqlsrv_74_ts_x64.dll
Erro de conexão "Cannot open database"
Execute no SSMS:
-- Verificar se o usuário web tem acesso
USE MuOnline;
GRANT SELECT ON RankingCache TO mu_web_user;
GRANT EXECUTE ON sp_UpdateRankingCache TO mu_web_user;
Resets da tabela Character: CREATE INDEX IX_Char_Resets ON Character (Resets DESC, cLevel DESC). Isso acelera a stored procedure de atualização.Expansões Possíveis
Com a estrutura base funcionando, você pode adicionar:
- Ranking de Guild: agregue pontos pela soma de resets dos membros via
GROUP BY G_Name - Ranking de PvP: ordene por
PkCount DESCcom filtro de contas ativas - Histórico de posições: adicione uma tabela
RankingHistorye insira snapshots diários via Job separado agendado à meia-noite - API JSON: retorne os dados como
header('Content-Type: application/json'); echo json_encode($rows);para integrar com Discord bots ou apps mobile
Este sistema escala bem para servidores de pequeno a médio porte. Para servidores com mais de 1.000 jogadores simultâneos, considere migrar o cache para Redis ou memcached e consultar o SQL Server apenas durante a atualização agendada.
Perguntas frequentes
Com que frequência devo atualizar o ranking?
Para servidores ativos, um intervalo de 5 a 15 minutos é suficiente. Use o SQL Server Agent com um Job agendado apontando para a stored procedure de atualização. Intervalos menores que 5 minutos podem sobrecarregar o banco de dados em servidores com muitos jogadores.
O ranking mostra dados desatualizados, o que verificar?
Confirme que o SQL Server Agent está rodando em Serviços do Windows, que o Job está ativo (não pausado) e que a tabela RankingCache possui a coluna LastUpdate sendo atualizada. Execute manualmente: EXEC sp_UpdateRankingCache e verifique se há erros no SQL Server Management Studio.
Como evitar que personagens GM apareçam no ranking?
Adicione um filtro na query principal: WHERE AccountID NOT IN (SELECT memb___id FROM MEMB_INFO WHERE memb_stat = 1) e adicione também uma coluna IsAdmin na tabela de cache para filtrar via PHP, ou mantenha uma tabela exclusion_list com os AccountIDs dos administradores.
Posso exibir rankings por classe de personagem?
Sim. Adicione um filtro na query: AND Class BETWEEN 0 AND 1 para Dark Wizard, BETWEEN 16 AND 17 para Dark Knight, BETWEEN 32 AND 33 para Elf, BETWEEN 48 AND 49 para Magic Gladiator e BETWEEN 64 AND 64 para Dark Lord. Crie abas separadas no website para cada filtro.