O maior portal de MU Online do Brasil — desde 2003
Tutorial Intermediário Website

Como criar troca de nick e classe via site no MU Online

Implemente troca de nick e mudança de classe via site no MU Online com validação de nome, atualização segura de chaves estrangeiras e cobrança opcional em WCoin.

BR Bruno · Atualizado em 5 out 2025 · ⏱ 20 min de leitura
Resposta rápida

Trocar o nome (nick) ou a classe de um personagem são dois dos serviços pagos mais procurados em servidores privados de MU Online. Um jogador quer renomear o personagem que criou às pressas; outro se cansou do Dark Wizard e quer virar Dark Knight sem recomeçar do zero. Oferecer essas trocas pelo sit

Trocar o nome (nick) ou a classe de um personagem são dois dos serviços pagos mais procurados em servidores privados de MU Online. Um jogador quer renomear o personagem que criou às pressas; outro se cansou do Dark Wizard e quer virar Dark Knight sem recomeçar do zero. Oferecer essas trocas pelo site é conveniente e rentável — mas também é uma das operações mais perigosas de implementar. Diferente de reset e PK Clear, que mexem em poucas colunas de uma linha, a troca de nick toca em relacionamentos: guild, lista de amigos, mensagens, rankings. Fazer errado gera personagens órfãos de guild, itens perdidos e inconsistências difíceis de rastrear.

Este tutorial mostra como construir troca de nick e troca de classe via site de forma segura e atômica, usando stored procedures no SQL Server e uma camada PHP enxuta. Os nomes de campos e tabelas são um EXEMPLO de Season 6; a estrutura exata varia por versão e por emulador. Sempre mapeie as dependências do nome no seu banco antes de aplicar. Se você ainda não tem servidor e site rodando, comece por como criar servidor de MU Online.

Pré-requisitos

  • Servidor de MU Online funcional com banco MuOnline acessível.
  • SQL Server (2008/2014/2017/2019) com SSMS e permissão db_owner.
  • Site em PHP 7.4+ conectado via PDO (sqlsrv ou dblib).
  • Painel de conta com login/sessão funcionando.
  • Backup recente e ambiente de homologação para testes.

Passo 1 — Mapear todas as dependências do nome do personagem

Antes de trocar um único caractere, descubra onde o nome do personagem é referenciado. O nome geralmente é a chave lógica usada em várias tabelas. Rode:

SELECT t.name AS Tabela, c.name AS Coluna
FROM sys.columns c
JOIN sys.tables t ON t.object_id = c.object_id
WHERE c.name IN ('Name', 'Char', 'CharName', 'Member', 'GameID')
ORDER BY t.name;

Num EXEMPLO comum de Season 6, o nome aparece em:

TabelaColunaPapel
CharacterNameRegistro principal do personagem
GuildG_MasterNome do mestre da guild
GuildMemberNameMembros da guild
FriendName / FriendNameLista de amigos
MEMB_INFO (via AccountCharacter)Slots de personagem por conta
warehouse/RankingdependenteReferências diversas

Ignorar qualquer uma dessas gera inconsistência: por exemplo, renomear em Character mas não em GuildMember faz o personagem "sumir" da guild. A lista varia por versão — mapeie a sua com a query acima.

Passo 2 — Regras de validação do novo nome

Um nome inválido pode travar o cliente do jogo ou abrir espaço para exploits. Aplique validação rigorosa tanto no PHP quanto na procedure:

  • Apenas letras e números; sem espaços, acentos ou símbolos.
  • Comprimento típico de 1 a 10 caracteres (varia por versão).
  • Verificação de duplicidade case-insensitive (evita "Player" e "player" coexistindo).
  • Lista de palavras proibidas (nomes reservados, ofensas, "GM", "Admin").
<?php
function nomeValido(string $nome): bool {
    if (!preg_match('/^[A-Za-z0-9]{1,10}$/', $nome)) return false;
    $proibidos = ['gm', 'admin', 'gamemaster', 'moderador'];
    return !in_array(strtolower($nome), $proibidos, true);
}

Passo 3 — Stored procedure de troca de nick

A troca de nome precisa ser atômica: ou todas as tabelas são atualizadas, ou nenhuma. Uma transação com XACT_ABORT ON garante isso. A procedure também valida propriedade, status online, duplicidade e cobrança.

USE MuOnline;
GO

IF OBJECT_ID('dbo.WZ_TrocaNickViaSite', 'P') IS NOT NULL
    DROP PROCEDURE dbo.WZ_TrocaNickViaSite;
GO

CREATE PROCEDURE dbo.WZ_TrocaNickViaSite
    @AccountID  VARCHAR(10),
    @OldName    VARCHAR(10),
    @NewName    VARCHAR(10),
    @WCoinCost  INT = 0
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    DECLARE @Owner VARCHAR(10), @Online TINYINT, @WCoin INT;

    BEGIN TRANSACTION;

    SELECT @Owner = AccountID, @Online = ConnectStat
    FROM dbo.Character WITH (UPDLOCK, ROWLOCK)
    WHERE Name = @OldName;

    IF @Owner IS NULL OR @Owner <> @AccountID
    BEGIN
        ROLLBACK TRANSACTION;
        SELECT -10 AS Result, 'Personagem nao pertence a esta conta.' AS Message;
        RETURN;
    END

    IF @Online <> 0
    BEGIN
        ROLLBACK TRANSACTION;
        SELECT -1 AS Result, 'Saia do jogo antes de trocar o nick.' AS Message;
        RETURN;
    END

    -- Duplicidade case-insensitive
    IF EXISTS (SELECT 1 FROM dbo.Character WHERE Name = @NewName)
    BEGIN
        ROLLBACK TRANSACTION;
        SELECT -2 AS Result, 'Este nome ja esta em uso.' AS Message;
        RETURN;
    END

    -- Cobranca em WCoin (EXEMPLO: campo em MEMB_INFO)
    IF @WCoinCost > 0
    BEGIN
        SELECT @WCoin = WCoin FROM dbo.MEMB_INFO WHERE memb___id = @AccountID;
        IF @WCoin < @WCoinCost
        BEGIN
            ROLLBACK TRANSACTION;
            SELECT -3 AS Result, 'WCoin insuficiente.' AS Message;
            RETURN;
        END
        UPDATE dbo.MEMB_INFO SET WCoin = WCoin - @WCoinCost WHERE memb___id = @AccountID;
    END

    -- Atualiza TODAS as referencias (ajuste conforme sua versao)
    UPDATE dbo.Character   SET Name  = @NewName WHERE Name = @OldName;
    UPDATE dbo.GuildMember SET Name  = @NewName WHERE Name = @OldName;
    UPDATE dbo.Guild       SET G_Master = @NewName WHERE G_Master = @OldName;
    UPDATE dbo.Friend      SET Name  = @NewName WHERE Name = @OldName;
    UPDATE dbo.Friend      SET FriendName = @NewName WHERE FriendName = @OldName;

    INSERT INTO dbo.NickChangeLog (AccountID, OldName, NewName, ChangeDate)
    VALUES (@AccountID, @OldName, @NewName, GETDATE());

    COMMIT TRANSACTION;

    SELECT 1 AS Result, 'Nick trocado com sucesso.' AS Message;
END
GO

Crie a tabela de log:

CREATE TABLE dbo.NickChangeLog (
    ID         INT IDENTITY(1,1) PRIMARY KEY,
    AccountID  VARCHAR(10),
    OldName    VARCHAR(10),
    NewName    VARCHAR(10),
    ChangeDate DATETIME
);

> Alguns bancos definem as chaves estrangeiras com ON UPDATE CASCADE. Se for o caso da sua versão, atualizar apenas Character.Name propaga automaticamente. Confirme com sp_fkeys 'Character' antes de decidir se precisa dos UPDATEs manuais.

Passo 4 — Stored procedure de troca de classe

A troca de classe é conceitualmente mais simples (mexe em uma linha) porém tem efeitos colaterais: atributos e itens equipados podem ficar incompatíveis com a nova classe. A abordagem segura é trocar o Class, resetar os atributos base e devolver os pontos para redistribuição.

USE MuOnline;
GO

IF OBJECT_ID('dbo.WZ_TrocaClasseViaSite', 'P') IS NOT NULL
    DROP PROCEDURE dbo.WZ_TrocaClasseViaSite;
GO

CREATE PROCEDURE dbo.WZ_TrocaClasseViaSite
    @AccountID  VARCHAR(10),
    @CharName   VARCHAR(10),
    @NewClass   TINYINT,      -- EXEMPLO: 0=DW, 16=DK, 32=Elf, 48=MG, 64=DL
    @WCoinCost  INT = 0
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    DECLARE @Owner VARCHAR(10), @Online TINYINT, @WCoin INT, @Points INT, @Level INT;

    BEGIN TRANSACTION;

    SELECT @Owner = AccountID, @Online = ConnectStat,
           @Level = cLevel
    FROM dbo.Character WITH (UPDLOCK, ROWLOCK)
    WHERE Name = @CharName;

    IF @Owner IS NULL OR @Owner <> @AccountID
    BEGIN
        ROLLBACK TRANSACTION;
        SELECT -10 AS Result, 'Personagem nao pertence a esta conta.' AS Message;
        RETURN;
    END

    IF @Online <> 0
    BEGIN
        ROLLBACK TRANSACTION;
        SELECT -1 AS Result, 'Saia do jogo antes de trocar de classe.' AS Message;
        RETURN;
    END

    IF @WCoinCost > 0
    BEGIN
        SELECT @WCoin = WCoin FROM dbo.MEMB_INFO WHERE memb___id = @AccountID;
        IF @WCoin < @WCoinCost
        BEGIN
            ROLLBACK TRANSACTION;
            SELECT -3 AS Result, 'WCoin insuficiente.' AS Message;
            RETURN;
        END
        UPDATE dbo.MEMB_INFO SET WCoin = WCoin - @WCoinCost WHERE memb___id = @AccountID;
    END

    -- Pontos totais aproximados para redistribuir (EXEMPLO)
    SET @Points = 300 + (@Level * 4);

    UPDATE dbo.Character
    SET Class        = @NewClass,
        Strength     = 20,
        Dexterity    = 20,
        Vitality     = 20,
        Energy       = 20,
        Leadership   = CASE WHEN @NewClass = 64 THEN 25 ELSE 0 END,
        LevelUpPoint = @Points
    WHERE Name = @CharName;

    INSERT INTO dbo.ClassChangeLog (AccountID, CharName, NewClass, ChangeDate)
    VALUES (@AccountID, @CharName, @NewClass, GETDATE());

    COMMIT TRANSACTION;

    SELECT 1 AS Result, 'Classe trocada com sucesso. Redistribua seus pontos no jogo.' AS Message;
END
GO
Atenção: Itens equipados incompatíveis com a nova classe podem causar crash do cliente ou serem "engolidos". O ideal é, dentro da transação, mover para o inventário (ou desequipar) qualquer item que a nova classe não pode usar. Como o layout do inventário é binário e varia por versão, teste exaustivamente em homologação — ou exija que o jogador desequipe tudo antes de trocar.

Passo 5 — Processador PHP unificado

Um único processador atende às duas trocas, sempre com CSRF, validação e parâmetros vinculados.

<?php
session_start();
require 'db.php';
require 'validacao.php'; // funcao nomeValido()

if (empty($_SESSION['account_id'])) { http_response_code(403); exit('Nao autenticado.'); }
if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) {
    http_response_code(400); exit('Token invalido.');
}
unset($_SESSION['csrf']);

$acc  = $_SESSION['account_id'];
$char = trim($_POST['char'] ?? '');
$acao = $_POST['acao'] ?? '';
$pdo  = getDB();

if ($acao === 'nick') {
    $novo = trim($_POST['novo_nome'] ?? '');
    if (!nomeValido($novo)) { exit('Nome invalido.'); }
    $stmt = $pdo->prepare('EXEC dbo.WZ_TrocaNickViaSite
        @AccountID=:acc, @OldName=:old, @NewName=:new, @WCoinCost=:wc');
    $stmt->execute([':acc'=>$acc, ':old'=>$char, ':new'=>$novo, ':wc'=>200]);
} elseif ($acao === 'classe') {
    $classe = (int)($_POST['nova_classe'] ?? -1);
    $permitidas = [0, 16, 32, 48, 64]; // EXEMPLO
    if (!in_array($classe, $permitidas, true)) { exit('Classe invalida.'); }
    $stmt = $pdo->prepare('EXEC dbo.WZ_TrocaClasseViaSite
        @AccountID=:acc, @CharName=:char, @NewClass=:cls, @WCoinCost=:wc');
    $stmt->execute([':acc'=>$acc, ':char'=>$char, ':cls'=>$classe, ':wc'=>300]);
} else {
    exit('Acao desconhecida.');
}

$res = $stmt->fetch();
echo (($res['Result'] ?? -99) == 1)
    ? 'Sucesso: ' . htmlspecialchars($res['Message'])
    : 'Falha: ' . htmlspecialchars($res['Message'] ?? 'Erro.');

Passo 6 — Interface no painel de conta

Exiba os personagens da conta e ofereça as duas ações. Um pequeno JS ajusta o formulário conforme a ação escolhida.

document.querySelectorAll('input[name="acao"]').forEach(radio => {
  radio.addEventListener('change', e => {
    document.getElementById('campo-nick').hidden   = e.target.value !== 'nick';
    document.getElementById('campo-classe').hidden = e.target.value !== 'classe';
  });
});

Passo 7 — Bloquear operação com personagem online

Como em reset e PK Clear, o personagem online é o maior risco: o GameServer sobrescreve o banco ao salvar, revertendo a troca e podendo cobrar sem entregar. As procedures já bloqueiam com ConnectStat <> 0. Confirme onde esse campo mora na sua versão e oriente o jogador a sair do jogo antes de qualquer troca.

Erros comuns e soluções

ErroCausa provávelSolução
Personagem some da guild após trocar nickSó atualizou Character.NameAtualizar GuildMember/Guild na mesma transação
Nome duplicado aceitoChecagem case-sensitiveValidar duplicidade sem diferenciar maiúsculas
Crash do cliente após trocar classeItens equipados incompatíveisDesequipar/mover itens da nova classe
Troca revertida ao entrar no jogoPersonagem estava onlineBloquear com ConnectStat <> 0
WCoin debitado sem troca concluídaDébito fora da transaçãoDébito e troca na mesma transação
Injeção pelo nome novoSem validação/regexRegex estrito + prepared statements

Checklist de lançamento

  • Backup do banco MuOnline feito antes dos testes.
  • Todas as tabelas que referenciam o nome mapeadas na sua versão.
  • Chaves estrangeiras verificadas (sp_fkeys 'Character').
  • Códigos numéricos de classe confirmados no seu banco.
  • Procedures WZ_TrocaNickViaSite e WZ_TrocaClasseViaSite testadas no SSMS.
  • Tabelas NickChangeLog e ClassChangeLog criadas.
  • Validação de nome (regex + palavras proibidas + duplicidade) ativa.
  • Token CSRF de uso único no formulário e no processador.
  • Bloqueio de troca para personagem online validado.
  • Tratamento de itens incompatíveis na troca de classe testado.
  • Custo em WCoin/Zen definido e validado dentro da transação.
  • Teste ponta a ponta em homologação antes de abrir ao público.

Troca de nick e classe são serviços de alto valor percebido, mas exigem cuidado redobrado por tocarem em relacionamentos entre tabelas. Mantendo toda a lógica dentro de transações atômicas no banco, mapeando corretamente as dependências do nome e tratando os itens incompatíveis na troca de classe, você entrega um serviço confiável e monetizável sem gerar personagens quebrados. Teste sempre em homologação e ajuste os códigos de classe e as tabelas às particularidades da sua versão.

Perguntas frequentes

Trocar o nome do personagem quebra guild, amigos e itens?

Pode quebrar se você atualizar só a tabela Character. O nome do personagem costuma ser referenciado em GuildMember, Friend e outras tabelas. É preciso atualizar todas dentro da mesma transação, ou usar cascata.

O jogador precisa estar offline para trocar nick ou classe?

Sim. Com o personagem online, o GameServer sobrescreve o banco ao salvar. Bloqueie a operação quando ConnectStat for diferente de 0 e peça o logout antes.

Como validar um novo nome de personagem?

Aplique um regex que aceite apenas letras e números (comprimento típico até 10), rejeite nomes já existentes com verificação case-insensitive e bloqueie palavras proibidas. Faça a checagem de duplicidade dentro da transação.

Trocar de classe muda os atributos e habilidades?

A mudança de Class na tabela Character altera a classe base, mas atributos, itens equipados e skills podem ficar incompatíveis. O mais seguro é resetar stats e desequipar itens da nova classe incompatível durante a troca.

Como saber o código numérico de cada classe?

O campo Class é um inteiro. Em Season 6, valores base comuns são 0 (Dark Wizard), 16 (Dark Knight), 32 (Elf), 48 (Magic Gladiator), 64 (Dark Lord). Isso varia por versão, confirme no seu banco.

BR
Editor de eventos, mapas e itens

Bruno é especialista em eventos, mapas, bosses e economia de itens do MU Online. Documenta cada detalhe com base em jogo real.

Continue lendo

Artigos relacionados