Como integrar o site e o jogo no servidor de MU Online
Integre o site PHP com o banco SQL Server do servidor de MU Online — criar conta no site que registra no jogo, ranking real de personagens, painel de controle de conta e sistema de VIP pelo site.
A integração entre o site e o jogo no MU Online é feita através de conexão direta ao banco SQL Server que o MuServer usa. O site PHP lê e escreve diretamente nas tabelas do jogo — não há API intermediária.
A integração entre o site e o jogo no MU Online é feita através de conexão direta ao banco SQL Server que o MuServer usa. O site PHP lê e escreve diretamente nas tabelas do jogo — não há API intermediária.
Arquitetura de integração
SITE (PHP/Apache) JOGO (MuServer)
│ │
│ PDO + sqlsrv driver │
└─────────────────────────┐ │
▼ ▼
SQL SERVER (banco MuOnline)
├── MEMB_INFO (contas)
├── Character (personagens)
├── Character_Items (inventário)
└── MEMB_VIP (VIP)
O site e o GameServer compartilham o mesmo banco. Quando o site cria uma conta na MEMB_INFO, ela imediatamente fica disponível para login no jogo.
Configurar o driver sqlsrv no PHP
No Linux (Ubuntu/Debian) com PHP 8.x
# Instalar ODBC driver da Microsoft para SQL Server
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list \
> /etc/apt/sources.list.d/mssql-release.list
sudo apt update
sudo ACCEPT_EULA=Y apt install -y msodbcsql17 unixodbc-dev
# Instalar extensão PHP sqlsrv
sudo pecl install sqlsrv pdo_sqlsrv
# Habilitar no PHP
echo "extension=sqlsrv.so" | sudo tee -a /etc/php/8.1/apache2/php.ini
echo "extension=pdo_sqlsrv.so" | sudo tee -a /etc/php/8.1/apache2/php.ini
# Reiniciar Apache
sudo systemctl restart apache2
# Verificar
php -m | grep sqlsrv
No Windows (AppServ/XAMPP)
- Baixe o driver em
github.com/microsoft/msphpsql/releases— escolha a versão correta para seu PHP (ex:php_sqlsrv_83_ts_x64.dllpara PHP 8.3 Thread-Safe 64-bit); - Copie para a pasta
ext/do PHP (ex:C:\AppServ\php\ext\); - Edite o
php.ini:
``ini extension=php_sqlsrv_83_ts_x64.dll extension=php_pdo_sqlsrv_83_ts_x64.dll ``
- Reinicie o Apache.
Configuração de conexão com o banco
<?php
// config/db-muserver.php
define('MU_SQL_SERVER', '(local)'); // IP ou nome do SQL Server
define('MU_SQL_DATABASE', 'MuOnline');
define('MU_SQL_USER', 'mu_app'); // usuário SQL dedicado
define('MU_SQL_PASSWORD', 'SenhaMuApp@2026!');
function getMuDB() {
static $conn = null;
if ($conn === null) {
$conn = sqlsrv_connect(MU_SQL_SERVER, [
'Database' => MU_SQL_DATABASE,
'UID' => MU_SQL_USER,
'PWD' => MU_SQL_PASSWORD,
'CharacterSet' => 'UTF-8',
'ReturnDatesAsStrings' => true,
'MultipleActiveResultSets' => true,
]);
if ($conn === false) {
error_log("Erro SQL Server: " . print_r(sqlsrv_errors(), true));
throw new RuntimeException('Erro ao conectar ao banco do servidor MU');
}
}
return $conn;
}
// Alternativa com PDO (sqlsrv driver)
function getMuPDO(): PDO {
static $pdo = null;
if ($pdo === null) {
$dsn = "sqlsrv:Server=" . MU_SQL_SERVER . ";Database=" . MU_SQL_DATABASE;
$pdo = new PDO($dsn, MU_SQL_USER, MU_SQL_PASSWORD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
return $pdo;
}
Criar conta no site com registro no jogo
<?php
// registro.php — criar conta no site e no jogo simultaneamente
require 'config/db-muserver.php';
function registrarConta(string $login, string $senha, string $email): array {
// Validação básica
if (strlen($login) < 4 || strlen($login) > 10) {
return ['sucesso' => false, 'erro' => 'Login deve ter 4-10 caracteres'];
}
if (!preg_match('/^[a-zA-Z0-9]+$/', $login)) {
return ['sucesso' => false, 'erro' => 'Login: apenas letras e números'];
}
if (strlen($senha) < 6) {
return ['sucesso' => false, 'erro' => 'Senha deve ter no mínimo 6 caracteres'];
}
$conn = getMuDB();
// Verificar se login já existe
$checkStmt = sqlsrv_prepare($conn,
"SELECT COUNT(*) FROM MEMB_INFO WHERE memb_id = ?",
[[$login, SQLSRV_PARAM_IN]]
);
sqlsrv_execute($checkStmt);
sqlsrv_fetch($checkStmt);
$existe = sqlsrv_get_field($checkStmt, 0);
if ($existe > 0) {
return ['sucesso' => false, 'erro' => 'Este login já está em uso'];
}
// Criptografar senha (SQL Server usa SHA2_256 internamente)
// O MuServer valida a senha de formas diferentes por versão
// Versão mais comum S6: senha em texto claro na MEMB_INFO, comparada em MD5
$senhaCript = strtoupper(md5($senha)); // MD5 maiúsculo
// Inserir na MEMB_INFO (tabela de contas do MuServer)
$insertStmt = sqlsrv_prepare($conn, "
INSERT INTO MEMB_INFO (
memb_id,
memb_pw,
memb_name,
sno_u_stat,
memb_stat,
mail_addr,
bloc_code,
appl_days
) VALUES (?, ?, ?, 1, 1, ?, 0, GETDATE())
", [
[$login, SQLSRV_PARAM_IN],
[$senhaCript, SQLSRV_PARAM_IN],
[$login, SQLSRV_PARAM_IN], // nome (usar login como nome)
[$email, SQLSRV_PARAM_IN],
]);
if (!sqlsrv_execute($insertStmt)) {
error_log("Erro ao criar conta: " . print_r(sqlsrv_errors(), true));
return ['sucesso' => false, 'erro' => 'Erro interno ao criar conta'];
}
return ['sucesso' => true, 'login' => $login];
}
// Processar formulário
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$resultado = registrarConta(
trim($_POST['login'] ?? ''),
$_POST['senha'] ?? '',
trim($_POST['email'] ?? '')
);
if ($resultado['sucesso']) {
echo "Conta '{$resultado['login']}' criada com sucesso! Já pode fazer login no jogo.";
} else {
echo "Erro: " . htmlspecialchars($resultado['erro']);
}
}
?>
strtoupper(md5($senha))), outras usam HashBytes('SHA2_256', ...) do SQL Server, outras texto claro. Sempre verifique como as senhas estão armazenadas na sua MEMB_INFO antes de implementar.Ranking de personagens no site
<?php
// ranking.php — ranking de level e resets
require 'config/db-muserver.php';
function getRankingLevel(int $limit = 50): array {
$conn = getMuDB();
$sql = "
SELECT TOP $limit
ROW_NUMBER() OVER (ORDER BY c.Resets DESC, c.cLevel DESC) AS Posicao,
c.Name,
c.Class,
c.cLevel AS Level,
c.Resets,
c.ConnectStat AS Online,
m.mail_addr AS Email
FROM Character c
INNER JOIN MEMB_INFO m ON m.memb_id = c.AccountID
WHERE c.CtlCode = 0 -- excluir GMs do ranking
ORDER BY c.Resets DESC, c.cLevel DESC
";
$stmt = sqlsrv_query($conn, $sql);
$ranking = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
$row['Classe'] = nomeDaClasse($row['Class']);
$ranking[] = $row;
}
return $ranking;
}
function nomeDaClasse(int $classCode): string {
// Classes do S6 (classe base = múltiplos de 16)
$classes = [
0 => 'Dark Wizard',
1 => 'Soul Master',
2 => 'Grand Master',
16 => 'Dark Knight',
17 => 'Blade Knight',
18 => 'Blade Master',
32 => 'Elf',
33 => 'Muse Elf',
34 => 'High Elf',
48 => 'Magic Gladiator',
49 => 'Duel Master',
64 => 'Dark Lord',
65 => 'Lord Emperor',
80 => 'Summoner',
81 => 'Bloody Summoner',
82 => 'Dimension Master',
96 => 'Rage Fighter',
97 => 'Fist Master',
];
// Código da classe pode ter modificadores de EVO
$baseClass = (int)($classCode / 16) * 16 + ($classCode % 16);
return $classes[$classCode] ?? $classes[$baseClass] ?? "Classe $classCode";
}
// Exibir ranking em tabela HTML
$ranking = getRankingLevel();
?>
<table class="ranking">
<tr>
<th>#</th><th>Personagem</th><th>Classe</th>
<th>Level</th><th>Resets</th><th>Status</th>
</tr>
<?php foreach ($ranking as $row): ?>
<tr class="<?= $row['Online'] ? 'online' : 'offline' ?>">
<td><?= $row['Posicao'] ?></td>
<td><?= htmlspecialchars($row['Name']) ?></td>
<td><?= htmlspecialchars($row['Classe']) ?></td>
<td><?= $row['Level'] ?></td>
<td><?= $row['Resets'] ?></td>
<td><?= $row['Online'] ? '🟢 Online' : '⚫ Offline' ?></td>
</tr>
<?php endforeach; ?>
</table>
Painel de controle de conta no site
<?php
// painel.php — o jogador gerencia sua conta pelo site
require 'config/db-muserver.php';
session_start();
// Verificar se está logado no site
if (!isset($_SESSION['conta_mu'])) {
header('Location: /login');
exit;
}
$conta = $_SESSION['conta_mu'];
$conn = getMuDB();
// Buscar dados da conta e personagens
$stmt = sqlsrv_query($conn, "
SELECT m.memb_id, m.mail_addr, m.bloc_code,
v.vip_type, v.start_date, v.end_date
FROM MEMB_INFO m
LEFT JOIN MEMB_VIP v ON v.memb_id = m.memb_id
WHERE m.memb_id = ?
", [[$conta, SQLSRV_PARAM_IN]]);
$dadosConta = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
// Buscar personagens da conta
$stmtChars = sqlsrv_query($conn, "
SELECT Name, Class, cLevel, Resets, ConnectStat
FROM Character
WHERE AccountID = ?
ORDER BY cLevel DESC
", [[$conta, SQLSRV_PARAM_IN]]);
$personagens = [];
while ($row = sqlsrv_fetch_array($stmtChars, SQLSRV_FETCH_ASSOC)) {
$personagens[] = $row;
}
// Alterar senha
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['nova_senha'])) {
$novaSenha = strtoupper(md5($_POST['nova_senha']));
sqlsrv_query($conn,
"UPDATE MEMB_INFO SET memb_pw = ? WHERE memb_id = ?",
[[$novaSenha, SQLSRV_PARAM_IN], [$conta, SQLSRV_PARAM_IN]]
);
$mensagem = "Senha alterada com sucesso!";
}
?>
<h1>Minha Conta: <?= htmlspecialchars($conta) ?></h1>
<p>Email: <?= htmlspecialchars($dadosConta['mail_addr']) ?></p>
<?php if ($dadosConta['vip_type']): ?>
<p>VIP Ativo: Tipo <?= $dadosConta['vip_type'] ?> até <?= $dadosConta['end_date'] ?></p>
<?php endif; ?>
<h2>Personagens</h2>
<ul>
<?php foreach ($personagens as $char): ?>
<li><?= htmlspecialchars($char['Name']) ?> — Level <?= $char['cLevel'] ?> — Resets: <?= $char['Resets'] ?></li>
<?php endforeach; ?>
</ul>
Adicionar VIP via site (integração com pagamento)
<?php
// webhook-pagamento.php — chamado após confirmação de pagamento
require 'config/db-muserver.php';
function ativarVIP(string $conta, int $tipovip, int $dias): bool {
$conn = getMuDB();
// MERGE para criar ou estender VIP
$sql = "
MERGE MEMB_VIP AS target
USING (SELECT ? AS memb_id) AS source ON target.memb_id = source.memb_id
WHEN MATCHED THEN
UPDATE SET
vip_type = ?,
end_date = CASE
WHEN end_date > GETDATE()
THEN DATEADD(DAY, ?, end_date) -- estender se ainda ativo
ELSE DATEADD(DAY, ?, GETDATE()) -- novo prazo se expirado
END
WHEN NOT MATCHED THEN
INSERT (memb_id, vip_type, start_date, end_date)
VALUES (?, ?, GETDATE(), DATEADD(DAY, ?, GETDATE()));
";
$stmt = sqlsrv_query($conn, $sql, [
[$conta, SQLSRV_PARAM_IN],
[$tipovip, SQLSRV_PARAM_IN],
[$dias, SQLSRV_PARAM_IN],
[$dias, SQLSRV_PARAM_IN],
[$conta, SQLSRV_PARAM_IN],
[$tipovip, SQLSRV_PARAM_IN],
[$dias, SQLSRV_PARAM_IN],
]);
return $stmt !== false;
}
// Exemplo: ativar VIP tipo 1 por 30 dias
ativarVIP('minhaConta', 1, 30);
Checklist de integração site-jogo
DRIVER PHP
[ ] sqlsrv instalado e funcionando (php -m | grep sqlsrv)
[ ] Driver ODBC Microsoft instalado (Linux)
[ ] Conexão testada com script simples
BANCO
[ ] Usuário SQL dedicado para o site criado
[ ] Permissões: SELECT/INSERT/UPDATE em MEMB_INFO, Character, MEMB_VIP
[ ] Porta 1433 acessível do servidor do site
FUNCIONALIDADES
[ ] Registro de conta funcionando
[ ] Login no site funcionando
[ ] Ranking exibindo dados reais do banco
[ ] Painel de conta exibindo personagens
[ ] Troca de senha funcionando
SEGURANÇA
[ ] Prepared statements em todas as queries (sem SQL injection)
[ ] Senha criptografada antes de salvar
[ ] Rate limiting no endpoint de registro
[ ] Proteção CSRF nos formulários
Perguntas frequentes
O site do servidor pode criar contas no jogo?
Sim — o site PHP se conecta diretamente ao banco SQL Server do MuServer e insere na tabela MEMB_INFO. É a forma mais comum de cadastro em servidores privados.
Como o site se conecta ao SQL Server?
Via PHP usando o driver sqlsrv (para Linux/Windows) ou PDO com driver ODBC. O driver sqlsrv é o mais indicado para SQL Server — instala-se separadamente no PHP.
Posso ter ranking em tempo real no site?
Sim — o site lê as tabelas Character e Account do SQL Server. Ranking de level, resets e tempo online são todos possíveis com queries SQL simples.
O site precisa estar no mesmo servidor que o MuServer?
Não — pode ser em servidores separados. O site só precisa de acesso ao SQL Server (porta 1433). Por segurança, configure o SQL Server para aceitar conexões apenas do IP do site.