O maior portal de MU Online do Brasil — desde 2003
Tutorial Avançado Web

Como criar um plugin para o WebEngine do seu servidor de MU Online

Aprenda a estruturar, codificar e publicar um plugin próprio para o WebEngine (painel web) do seu servidor de MU Online, integrando rankings, votação, loja e comandos com o banco de dados do jogo.

RO Rodrigo · Atualizado em 31 jan 2014 · ⏱ 17 min de leitura
Resposta rápida

O WebEngine é a espinha dorsal da presença online do seu servidor de MU Online: é ele que mostra o ranking de resets, processa o registro de contas, integra a votação em toparama, exibe a loja de cash e, em muitos casos, permite comandos remotos como resetar personagem ou trocar de classe. Quando o

O WebEngine é a espinha dorsal da presença online do seu servidor de MU Online: é ele que mostra o ranking de resets, processa o registro de contas, integra a votação em toparama, exibe a loja de cash e, em muitos casos, permite comandos remotos como resetar personagem ou trocar de classe. Quando o painel padrão não cobre uma necessidade específica — um evento sazonal, uma integração com Discord, um sistema de cupons — a solução correta não é remendar o core, e sim criar um plugin. Este tutorial mostra como estruturar, codificar, testar e publicar um plugin do zero, cobrindo a arquitetura típica, a conexão segura com o banco e os cuidados de produção.

O que é um plugin de WebEngine e quando criar um

Um plugin é um módulo isolado que se integra ao painel principal sem alterar seus arquivos-núcleo. Ele expõe rotas próprias, views próprias e, se necessário, tabelas próprias no banco — tudo isolado para que uma atualização do WebEngine base não sobrescreva seu trabalho. Crie um plugin quando a funcionalidade é opcional, específica do seu servidor (não genérica o suficiente para virar core) ou precisa de ciclo de vida próprio (ativar/desativar, versionar, distribuir para outros admins).

Arquitetura típica de um WebEngine baseado em PHP

A maioria dos painéis de MU usa uma estrutura MVC (Model-View-Controller). Entender essa estrutura é o primeiro passo antes de escrever qualquer linha de plugin.

CamadaResponsabilidadeLocal típico
ControllerRecebe requisição HTTP, chama a lógicaapplication/controllers/
ModelConsultas ao banco (MSSQL/MySQL)application/models/
ViewHTML/Twig/Blade renderizado ao usuárioapplication/views/
ConfigStrings de conexão, chaves, rotasapplication/config/
PluginsMódulos isolados de terceirosapplication/plugins/<nome>/

Pré-requisitos antes de começar

  • WebEngine já instalado e funcionando, conectado ao banco do servidor (veja o tutorial de criação de servidor).
  • Acesso de escrita ao código-fonte do painel (FTP/SSH) e ambiente de staging separado da produção.
  • Credenciais de leitura no banco de dados do jogo (idealmente um usuário com permissões restritas, não o sa).
  • Editor de código com suporte a PHP e SQL, e um cliente de banco (SSMS ou HeidiSQL) para inspecionar tabelas.

Passo 1 — Definir o escopo e o contrato do plugin

Antes de codificar, escreva em uma frase o que o plugin faz: "Exibe um painel de resgate de código promocional que credita Cash Points na conta." Esse contrato define as tabelas que você vai tocar (MEMB_STAT, CashShop_Log ou equivalentes), as rotas que vai expor (/plugins/promocode/resgatar) e as permissões necessárias (usuário logado, não banido).

Passo 2 — Criar a estrutura de pastas do plugin

Um plugin isolado normalmente segue este esqueleto:

application/plugins/promocode/
├── PromocodeController.php
├── PromocodeModel.php
├── views/
│   └── resgatar.php
├── config.php
└── install.sql

O arquivo install.sql cria as tabelas próprias do plugin (ex.: plugin_promocode_codes), evitando que ele dependa de alterar tabelas do core do jogo.

Passo 3 — Escrever o Model (acesso ao banco)

<?php
class PromocodeModel extends CI_Model {

    public function validarCodigo($codigo, $accountId) {
        $this->db->where('code', $codigo);
        $this->db->where('used', 0);
        $query = $this->db->get('plugin_promocode_codes');
        return $query->row();
    }

    public function creditarCash($accountId, $valor) {
        // Sempre via transação — nunca UPDATE direto sem controle de concorrência
        $this->db->trans_start();
        $this->db->query(
            "UPDATE MEMB_STAT SET CashPoint = CashPoint + ? WHERE memb___id = ?",
            [$valor, $accountId]
        );
        $this->db->trans_complete();
        return $this->db->trans_status();
    }
}

Note o uso de transações e queries parametrizadas — nunca concatene entrada do usuário diretamente em SQL, isso é a porta de entrada mais comum para SQL Injection em painéis de MU mal feitos.

Passo 4 — Escrever o Controller e a rota

<?php
class PromocodeController extends CI_Controller {

    public function resgatar() {
        if (!$this->session->userdata('logged_in')) {
            redirect('login');
        }
        $codigo = $this->input->post('codigo');
        $conta  = $this->session->userdata('account_id');

        $this->load->model('plugins/promocode/PromocodeModel');
        $item = $this->PromocodeModel->validarCodigo($codigo, $conta);

        if (!$item) {
            $this->session->set_flashdata('erro', 'Código inválido ou já utilizado.');
            redirect('plugins/promocode');
        }

        $this->PromocodeModel->creditarCash($conta, $item->valor);
        $this->session->set_flashdata('sucesso', 'Cash creditado com sucesso!');
        redirect('plugins/promocode');
    }
}

Registre a rota no arquivo de rotas do painel (application/config/routes.php) apontando plugins/promocode para este controller.

Passo 5 — Integrar com o esquema do banco do MuServer

Tabelas variam por emulador, mas os pontos de integração mais comuns são:

NecessidadeTabela típica (MSSQL)Cuidado
Dados de contaMEMB_INFO / MEMB_STATNunca altere senha em texto puro
Cash/Points da lojaMEMB_STAT.CashPoint ou tabela própriaUse transação, sempre
Correio in-gamePostMain / PostList (varia)Siga o formato exato que o GameServer espera
Log de açõesTabela própria do pluginSempre logue crédito de valores

Se o seu emulador expõe stored procedures para operações sensíveis (criar item, enviar correio), prefira chamá-las em vez de fazer INSERT/UPDATE manual — elas já tratam concorrência e formatos binários específicos do MU.

Passo 6 — Autenticação, sessão e permissões

Reutilize o sistema de sessão já existente do WebEngine (não crie um paralelo). Verifique sempre se o usuário está logado antes de qualquer ação que grave dados, e adicione uma segunda camada de checagem se a ação envolver valores monetários (ex.: confirmar senha novamente para resgates de alto valor).

Passo 7 — Views e experiência do usuário

Mantenha o layout consistente com o restante do painel (mesmo CSS, mesmo cabeçalho/rodapé), reaproveitando os templates existentes em vez de criar um HTML isolado. Isso evita que o plugin "pareça" um site diferente e quebre a confiança do jogador.

Passo 8 — Testes em ambiente de staging

  1. Restaure uma cópia do banco de produção em um banco de teste.
  2. Aponte o WebEngine de staging para esse banco.
  3. Teste o fluxo completo: código válido, código inválido, código já usado, conta banida, falha de conexão simulada.
  4. Verifique os logs gerados e confirme que nenhum valor foi creditado em duplicidade sob requisições simultâneas (teste de concorrência com duas abas ao mesmo tempo).

Passo 9 — Empacotar e versionar o plugin

Separe o plugin em um pacote distribuível (zip com install.sql, código e um README de instalação), e mantenha um número de versão (1.0.0) no config.php. Isso facilita atualizações futuras e permite reverter caso um deploy quebre algo em produção.

Passo 10 — Publicar em produção com segurança

Publique fora do horário de pico, com backup do banco imediatamente antes do deploy. Rode o install.sql primeiro, valide as tabelas criadas, depois suba o código do plugin. Monitore os logs do WebEngine e o consumo de CPU/queries do banco nas primeiras horas após o lançamento.

Erros comuns e soluções

SintomaCausa provávelSolução
Erro 500 ao acessar a rota do pluginRota não registrada ou model não carregadoConfirme routes.php e o load->model no controller
Cash creditado em duplicidadeFalta de transação/lock na requisiçãoEnvolva a operação em transação e trave o código como usado antes de creditar
SQL Injection detectado em auditoriaConcatenação direta de input em queryTroque por queries parametrizadas (? bind)
Plugin quebra após atualização do corePlugin alterou arquivos do core em vez de ficar isoladoReestruture para não tocar em arquivos fora de plugins/
Sessão do jogador não reconhecida no pluginSistema de sessão paralelo/incompatívelReutilize a sessão nativa do WebEngine

Checklist de publicação do plugin

  • Escopo e contrato do plugin definidos em uma frase clara.
  • Estrutura isolada em plugins/<nome>/, sem tocar arquivos do core.
  • Queries parametrizadas e transações em qualquer escrita monetária.
  • Autenticação reaproveitando a sessão nativa do WebEngine.
  • Testado em staging com casos de erro e concorrência.
  • Backup do banco realizado antes do deploy em produção.
  • Logs e monitoramento ativos nas primeiras horas após o lançamento.

Com o plugin publicado e estável, o próximo passo natural é pensar em como ele se conecta ao restante da experiência do jogador — por exemplo, integrando o resgate de códigos com eventos in-game descritos no tutorial de criação de servidor de MU Online, fechando o ciclo entre web e jogo.

Perguntas frequentes

O que exatamente é o WebEngine de um servidor de MU?

É o painel web (geralmente em PHP ou .NET) que mostra rankings, permite registro de conta, votação, loja de cash e comandos remotos. Ele se conecta ao mesmo banco de dados do MuOnline/GameServer para ler e escrever informações de jogadores.

Preciso saber PHP para criar um plugin?

Depende do WebEngine usado. A maioria dos painéis da comunidade (baseados em CodeIgniter ou Laravel) usa PHP; alguns projetos mais novos usam .NET/C#. Você precisa conhecer a linguagem do painel específico e o esquema do banco (MSSQL na maioria dos emuladores).

Um plugin pode executar comandos direto no personagem do jogador?

Sim, desde que o plugin escreva nas tabelas ou stored procedures que o GameServer já lê, como filas de comando, correio in-game ou tabelas de itens. Nunca edite diretamente colunas sensíveis (like Money, Level) sem passar pelas procedures oficiais, sob risco de duplicar itens ou zerar personagens.

Como testo um plugin sem afetar o servidor de produção?

Sempre use um ambiente de staging com uma cópia do banco de dados e, se possível, um GameServer de teste. Só publique no ambiente real depois de validar consultas, tempos de resposta e casos de erro (conta duplicada, item inexistente, saldo insuficiente).

É seguro expor uma API dentro do plugin para o cliente do jogo consumir?

Sim, mas exige autenticação (token por sessão), rate limiting e validação de entrada rigorosa. Uma API mal protegida vira porta de entrada para bots de votação, duplicação de cash ou ataques de força bruta contra contas.

RO
Fundador e editor-chefe

Rodrigo mantém o ViciadosMU desde os primórdios do portal. Especialista em criação e administração de servidores de MU Online, história do jogo e a evolução das seasons — escreveu boa parte do acervo antes de 2024.

Continue lendo

Artigos relacionados