Como criar servidor de MU Online Mobile — guia completo
Guia completo sobre servidores privados de MU Online Mobile — estrutura técnica, diferenças do PC, backend API, banco de dados, e conectividade de clientes Android/iOS.
O MU Online Mobile é uma versão completamente diferente do MU PC — usa arquitetura web moderna em vez do protocolo binário TCP do MuServer clássico. Criar um servidor privado de MU Mobile envolve desenvolvimento de backend API, muito mais do que configurar arquivos .ini.
Diferenças fundamentais: MU PC vs MU Mobile
| Aspecto | MU PC (MuServer) | MU Mobile |
|---|---|---|
| Protocolo | TCP binário (pacotes customizados) | HTTP/HTTPS + WebSocket |
| Backend | DataServer + GameServer + JoinServer | API REST + Gateway WebSocket |
| Banco de dados | SQL Server (ODBC 32-bit) | MySQL/PostgreSQL/MongoDB |
| Arquivos de config | .ini (texto simples) | JSON + banco de dados |
| Cliente | .exe executável Windows | .apk Android / App iOS |
| Autenticação | Tabela MEMB_INFO no SQL | JWT tokens / OAuth |
| Comunicação | Pacotes binários custom | JSON sobre WebSocket |
| Segurança | Mínima por padrão | SSL/TLS obrigatório |
Arquitetura de um servidor MU Mobile
CLIENTE (APK modificado)
│
│ HTTPS API calls (REST)
│ WSS WebSocket (tempo real)
▼
LOAD BALANCER / NGINX
│
├──► API SERVER (Node.js / Go / C#)
│ │ autenticação, inventário, chat
│ │
│ ┌────▼──────────────────┐
│ │ BANCO DE DADOS │
│ │ MySQL / PostgreSQL │
│ │ (contas, personagens, │
│ │ itens, guildas) │
│ └───────────────────────┘
│
└──► GAME SERVER (engine)
│ lógica de combate
│ sincronização de posição
│ eventos do mundo
▼
BANCO CACHE (Redis)
(estado em tempo real)
Pré-requisitos técnicos
Para desenvolver um servidor MU Mobile é necessário:
| Área | Conhecimento / Tecnologia |
|---|---|
| Backend web | Node.js + Express, Go, ou C# ASP.NET |
| Banco de dados | MySQL 8.x ou PostgreSQL 14+ |
| Cache/real-time | Redis (para estado de jogo) |
| Protocolo | WebSocket (socket.io ou ws) |
| Segurança | SSL/TLS, JWT |
| Reverse engineering | APK tools (apktool, jadx) |
| Mobile | Android Studio (para modificar APK) |
| Linux | Nginx, systemd, configuração de servidor |
Passo 1 — Entender o protocolo do cliente
O primeiro desafio é descobrir como o cliente mobile se comunica com o servidor:
Ferramenta de análise de tráfego
Para analisar o protocolo sem modificar código:
# Captura de tráfego com proxy (mitmproxy)
pip install mitmproxy
mitmproxy --listen-port 8888
# No Android, configure o proxy para 192.168.1.X:8888
# Abra o MU Mobile → o tráfego aparece no mitmproxy
O que observar
Tipicamente você vai ver:
1. POST /api/auth/login → autenticação da conta
2. GET /api/character → dados do personagem
3. WSS wss://game.server.com/game → canal de jogo em tempo real
Payload de login (JSON exemplo):
{
"username": "usuario123",
"password": "hash_da_senha",
"device_id": "uuid-do-dispositivo",
"version": "2.3.1"
}
Passo 2 — Banco de dados MySQL para MU Mobile
O MU Mobile usa MySQL (ou PostgreSQL) em vez do SQL Server:
-- Criar banco de dados
CREATE DATABASE mu_mobile CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'mu_mobile_app'@'localhost' IDENTIFIED BY 'SenhaMobileApp!';
GRANT ALL PRIVILEGES ON mu_mobile.* TO 'mu_mobile_app'@'localhost';
FLUSH PRIVILEGES;
-- Estrutura básica de tabelas
USE mu_mobile;
-- Tabela de contas
CREATE TABLE accounts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(32) NOT NULL UNIQUE,
password VARCHAR(128) NOT NULL, -- bcrypt hash
email VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP,
ban_until TIMESTAMP NULL,
INDEX idx_username (username)
);
-- Tabela de personagens
CREATE TABLE characters (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL REFERENCES accounts(id),
name VARCHAR(16) NOT NULL UNIQUE,
class TINYINT NOT NULL, -- 0=DK, 1=DW, 2=Elf...
level SMALLINT DEFAULT 1,
experience BIGINT DEFAULT 0,
strength SMALLINT DEFAULT 26,
agility SMALLINT DEFAULT 20,
vitality SMALLINT DEFAULT 24,
energy SMALLINT DEFAULT 16,
hp INT DEFAULT 100,
mp INT DEFAULT 50,
map_id SMALLINT DEFAULT 0,
pos_x SMALLINT DEFAULT 125,
pos_y SMALLINT DEFAULT 125,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_saved TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_account (account_id)
);
-- Tabela de itens do inventário
CREATE TABLE inventory (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
character_id INT UNSIGNED NOT NULL REFERENCES characters(id),
slot TINYINT NOT NULL,
item_group TINYINT NOT NULL,
item_id SMALLINT NOT NULL,
level TINYINT DEFAULT 0,
durability TINYINT DEFAULT 100,
options VARBINARY(16) DEFAULT NULL, -- opções binárias do item
INDEX idx_character (character_id)
);
-- Tabela de guildas
CREATE TABLE guilds (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(16) NOT NULL UNIQUE,
score INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Passo 3 — API Backend (Node.js + Express)
Exemplo de estrutura básica de API para MU Mobile:
// server.js — exemplo de backend Node.js
const express = require('express');
const ws = require('ws');
const mysql = require('mysql2/promise');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const app = express();
app.use(express.json());
const db = await mysql.createPool({
host: 'localhost',
database: 'mu_mobile',
user: 'mu_mobile_app',
password: 'SenhaMobileApp!'
});
// Endpoint de autenticação
app.post('/api/auth/login', async (req, res) => {
const { username, password } = req.body;
const [rows] = await db.query(
'SELECT id, password, ban_until FROM accounts WHERE username = ?',
[username]
);
if (!rows.length) {
return res.status(401).json({ error: 'Conta não encontrada' });
}
const account = rows[0];
// Verificar ban
if (account.ban_until && new Date(account.ban_until) > new Date()) {
return res.status(403).json({ error: 'Conta banida' });
}
// Verificar senha
const validPassword = await bcrypt.compare(password, account.password);
if (!validPassword) {
return res.status(401).json({ error: 'Senha incorreta' });
}
// Gerar token JWT
const token = jwt.sign(
{ account_id: account.id, username },
process.env.JWT_SECRET,
{ expiresIn: '24h' }
);
// Atualizar last_login
await db.query('UPDATE accounts SET last_login = NOW() WHERE id = ?', [account.id]);
res.json({ token, account_id: account.id });
});
// Endpoint de lista de personagens
app.get('/api/characters', authenticateToken, async (req, res) => {
const [chars] = await db.query(
'SELECT id, name, class, level, map_id FROM characters WHERE account_id = ?',
[req.user.account_id]
);
res.json({ characters: chars });
});
// Middleware de autenticação JWT
function authenticateToken(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
app.listen(3000, () => console.log('API rodando na porta 3000'));
WebSocket para comunicação em tempo real
// game-server.js — servidor WebSocket para o jogo
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3001 });
// Mapa de jogadores conectados
const players = new Map();
wss.on('connection', (socket, request) => {
let playerId = null;
socket.on('message', (data) => {
const message = JSON.parse(data);
switch (message.type) {
case 'ENTER_GAME':
// Validar token e carregar personagem
playerId = message.character_id;
players.set(playerId, { socket, position: { x: 125, y: 125 } });
break;
case 'MOVE':
// Processar movimento
if (players.has(playerId)) {
players.get(playerId).position = message.position;
// Broadcast da posição para jogadores próximos
broadcastToNearby(playerId, { type: 'PLAYER_MOVED', id: playerId, position: message.position });
}
break;
case 'ATTACK':
// Processar ataque
processAttack(playerId, message.target_id, message.skill_id);
break;
}
});
socket.on('close', () => {
players.delete(playerId);
// Salvar personagem no banco
saveCharacter(playerId);
});
});
function broadcastToNearby(fromId, message) {
const from = players.get(fromId);
if (!from) return;
// Calcular raio de 15 tiles
for (const [id, player] of players) {
if (id === fromId) continue;
const dist = Math.hypot(
player.position.x - from.position.x,
player.position.y - from.position.y
);
if (dist <= 15) {
player.socket.send(JSON.stringify(message));
}
}
}
Passo 4 — Modificar o APK para servidor privado
Ferramentas necessárias
# Instalar apktool (descompila/recompila APK)
# Baixar apktool.jar do repositório oficial
# Instalar jadx (descompila .dex para Java legível)
# Disponível em github.com/skylot/jadx
# Instalar zipalign e apksigner (do Android SDK)
Processo de modificação
# 1. Descompilar o APK
java -jar apktool.jar d MUMobile.apk -o mu_mobile_decompiled/
# 2. Procurar URLs do servidor na pasta decompilada
grep -r "webzen.com" mu_mobile_decompiled/
grep -r "muweb.com" mu_mobile_decompiled/
grep -r "api.mu" mu_mobile_decompiled/
# 3. As URLs geralmente estão em:
# - smali/com/webzen/... → código decompilado
# - assets/config.json → configuração
# - res/raw/ → arquivos raw
# - res/values/strings.xml
# 4. Substituir a URL do servidor oficial
# Exemplo:
# "https://api.mu-mobile.webzen.com" → "https://meuservidor.com"
sed -i 's/api.mu-mobile.webzen.com/meuservidor.com/g' mu_mobile_decompiled/assets/config.json
# 5. Recompilar o APK modificado
java -jar apktool.jar b mu_mobile_decompiled/ -o MUMobile_private.apk
# 6. Assinar o APK (necessário para instalar no Android)
# Criar keystore
keytool -genkeypair -v -keystore mu_private.keystore -alias mu -keyalg RSA -keysize 2048 -validity 10000
# Assinar
apksigner sign --ks mu_private.keystore --out MUMobile_private_signed.apk MUMobile_private.apk
Passo 5 — Nginx como proxy reverso (SSL)
# /etc/nginx/sites-available/mu-mobile
server {
listen 443 ssl;
server_name meuservidor.com;
ssl_certificate /etc/ssl/certs/meuservidor.crt;
ssl_certificate_key /etc/ssl/private/meuservidor.key;
# API REST
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# WebSocket do jogo
location /game {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
Passo 6 — Diferenças de gameplay para configurar
Ao contrário do MU PC, no MU Mobile a maioria das configurações fica no banco de dados:
-- Configuração de taxas (exemplo)
CREATE TABLE server_config (
config_key VARCHAR(64) PRIMARY KEY,
config_value VARCHAR(255)
);
INSERT INTO server_config VALUES
('exp_rate', '5'),
('drop_rate', '2'),
('exc_drop_rate', '10'),
('max_level', '400'),
('max_resets', '20'),
('reset_level', '400'),
('pvp_enabled', '1'),
('events_enabled', '1');
-- Ler configuração no backend:
-- SELECT config_value FROM server_config WHERE config_key = 'exp_rate';
Diferenças chave para quem vem do MU PC
| MU PC | MU Mobile |
|---|---|
Arquivos .ini de configuração | Configuração no banco ou JSON |
| SQL Server (ODBC 32-bit) | MySQL/PostgreSQL |
| DataServer.exe | API REST backend |
| GameServer.exe | Game Engine (Node.js/Go/C#) |
| ConnectServer.exe | Load balancer (Nginx) |
| JoinServer.exe | Autenticação JWT |
| Porta TCP 44405 | Portas 80/443 (HTTP/HTTPS) |
| Protocolo binário | JSON sobre WebSocket |
| Runtime VC++ | Node.js, Go ou .NET runtime |
Realidade do desenvolvimento
Criar um servidor privado de MU Mobile completo requer:
- Meses de desenvolvimento (vs dias para MU PC com distribuição pronta);
- Conhecimento de desenvolvimento web full-stack;
- Habilidade em engenharia reversa do protocolo;
- Infraestrutura de VPS Linux com SSL;
- Entendimento de segurança de APIs (autenticação, rate limiting, etc.);
- Manutenção contínua quando o cliente oficial recebe updates.
Para aprendizado de programação de servidores de jogos, o MU PC (MuServer) é muito mais acessível como ponto de partida. O MU Mobile é um projeto avançado que requer experiência prévia em desenvolvimento backend.
Perguntas frequentes
É possível criar um servidor privado de MU Mobile?
Tecnicamente sim, mas é significativamente mais complexo do que o MU PC. O cliente mobile usa API REST/WebSocket em vez de protocolo binário direto, e requer backend web completo.
MU Mobile usa o mesmo servidor que MU PC?
Não. São arquiteturas completamente diferentes. MU Mobile usa API web (HTTP/HTTPS + WebSocket), enquanto MU PC usa protocolo binário TCP direto.
Qual linguagem de programação o backend do MU Mobile usa?
Depende da implementação, mas em geral usa Node.js, Go, ou C# com ASP.NET para a API. Algumas implementações comunitárias usam PHP com WebSocket.
É necessário modificar o APK para conectar a um servidor privado?
Sim — o APK oficial aponta para os servidores da Webzen. Para conectar a um servidor privado, é necessário modificar o APK para apontar para o IP/URL do servidor privado.