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

Como criar um sistema de PK Clear (limpar PK) via site no MU Online

Crie um sistema de PK Clear via site no MU Online para limpar o status de assassino dos personagens, com stored procedure segura, cobrança opcional e proteção contra abuso.

GA Gabriel · Atualizado em 12 ago 2025 · ⏱ 17 min de leitura
Resposta rápida

No MU Online, matar outros jogadores fora de eventos transforma seu personagem em um assassino (PK — Player Killer). Esse status traz penalidades desagradáveis: chance de dropar itens ao morrer, bloqueio de acesso a NPCs em cidades e, dependendo do grau, guardas que atacam automaticamente. Tradicion

No MU Online, matar outros jogadores fora de eventos transforma seu personagem em um assassino (PK — Player Killer). Esse status traz penalidades desagradáveis: chance de dropar itens ao morrer, bloqueio de acesso a NPCs em cidades e, dependendo do grau, guardas que atacam automaticamente. Tradicionalmente o jogador precisa esperar o tempo de penalidade passar ou pagar um NPC específico dentro do jogo para limpar o PK. Oferecer o PK Clear via site é uma comodidade valorizada — e uma fonte de monetização — que permite ao jogador limpar o status pelo painel de conta em segundos.

Este tutorial mostra como construir esse sistema de forma segura, com a lógica dentro de uma stored procedure no SQL Server e uma camada PHP enxuta. Assim como no reset, a chave está em validar tudo no banco, tratar cada dado do formulário como hostil e impedir que a operação rode com o personagem online. Os nomes de campos apresentados são um EXEMPLO de Season 6; a estrutura exata varia por versão — confirme sempre o esquema do seu banco antes de aplicar. Se você ainda não montou a base do servidor e do site, veja primeiro como criar servidor de MU Online.

Pré-requisitos

  • Servidor de MU Online funcional com GameServer e banco MuOnline acessível.
  • SQL Server (2008/2014/2017/2019) com SSMS e permissão db_owner.
  • Site em PHP 7.4+ conectado ao banco via PDO (driver sqlsrv ou dblib).
  • Sistema de login/sessão do painel de conta funcionando.
  • Backup recente do banco antes de qualquer teste de UPDATE.

Passo 1 — Entender o status de PK no banco

O status de assassino é armazenado em colunas da tabela Character. Antes de escrever qualquer código, descubra os nomes exatos na sua versão:

SELECT COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Character'
  AND (COLUMN_NAME LIKE '%Pk%' OR COLUMN_NAME LIKE '%Kill%');

Num EXEMPLO típico de Season 6, os campos relevantes são:

CampoFunçãoValor "limpo"
PkLevelGrau de penalidade3 (neutro) na maioria das builds
PkCountNúmero de kills acumuladas0
PkTimeTempo de penalidade restante0
ConnectStatStatus online (0 = offline)usado para bloqueio

Atenção ao valor "neutro" de PkLevel: em muitos emuladores de Season 6, 3 significa personagem comum/sem PK, enquanto valores maiores indicam graus crescentes de assassino. Em outras versões o neutro é 0. Isso varia por versão — teste com um personagem de homologação antes de definir a constante.

Passo 2 — Criar a stored procedure de PK Clear

Toda a regra de negócio (propriedade do personagem, status online, cobrança, limite) fica na procedure, dentro de uma transação. O PHP só chama e interpreta o retorno.

USE MuOnline;
GO

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

CREATE PROCEDURE dbo.WZ_PkClearViaSite
    @AccountID   VARCHAR(10),
    @CharName    VARCHAR(10),
    @NeutralPk   INT = 3,       -- EXEMPLO: valor neutro varia por versao
    @ZenCost     BIGINT = 0
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    DECLARE @Owner VARCHAR(10), @Online TINYINT, @PkLevel INT, @Money BIGINT;

    BEGIN TRANSACTION;

    SELECT @Owner = AccountID, @Online = ConnectStat,
           @PkLevel = PkLevel, @Money = Money
    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 limpar o PK.' AS Message;
        RETURN;
    END

    -- Se ja esta neutro, nao cobra nem altera
    IF @PkLevel <= @NeutralPk
    BEGIN
        ROLLBACK TRANSACTION;
        SELECT -2 AS Result, 'Este personagem nao esta com PK.' AS Message;
        RETURN;
    END

    IF @ZenCost > 0 AND @Money < @ZenCost
    BEGIN
        ROLLBACK TRANSACTION;
        SELECT -3 AS Result, 'Zen insuficiente.' AS Message;
        RETURN;
    END

    -- Limpa o status de PK
    UPDATE dbo.Character
    SET PkLevel = @NeutralPk,
        PkCount = 0,
        PkTime  = 0,
        Money   = Money - @ZenCost
    WHERE Name = @CharName;

    INSERT INTO dbo.PkClearLog (AccountID, CharName, PkLevelAntes, ClearDate, Origem)
    VALUES (@AccountID, @CharName, @PkLevel, GETDATE(), 'SITE');

    COMMIT TRANSACTION;

    SELECT 1 AS Result, 'PK limpo com sucesso.' AS Message;
END
GO

E a tabela de log:

CREATE TABLE dbo.PkClearLog (
    ID           INT IDENTITY(1,1) PRIMARY KEY,
    AccountID    VARCHAR(10),
    CharName     VARCHAR(10),
    PkLevelAntes INT,
    ClearDate    DATETIME,
    Origem       VARCHAR(10)
);

> O log não é opcional. PK Clear barato e sem registro incentiva "ganking" — jogadores que matam outros à vontade porque sabem que limpam a penalidade em um clique. O log permite detectar contas que limpam PK repetidamente.

Passo 3 — Conexão PDO reutilizável

Mantenha a conexão num arquivo único, com credenciais fora do controle de versão.

<?php
// db.php
function getDB(): PDO {
    $dsn = 'sqlsrv:Server=' . (getenv('MU_DB_HOST') ?: '127.0.0.1') . ';Database=MuOnline';
    return new PDO($dsn, getenv('MU_DB_USER'), getenv('MU_DB_PASS'), [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]);
}

Passo 4 — Formulário de PK Clear no painel

Liste apenas os personagens da conta logada, marcando quais estão com PK. Isso melhora a experiência (o jogador vê onde precisa agir) e evita cliques inúteis.

<?php
session_start();
require 'db.php';
if (empty($_SESSION['account_id'])) { header('Location: /login.php'); exit; }

if (empty($_SESSION['csrf'])) { $_SESSION['csrf'] = bin2hex(random_bytes(32)); }

$pdo = getDB();
$stmt = $pdo->prepare(
    'SELECT Name, PkLevel, PkCount FROM Character WHERE AccountID = ? ORDER BY Name'
);
$stmt->execute([$_SESSION['account_id']]);
$chars = $stmt->fetchAll();
$NEUTRO = 3; // EXEMPLO: ajuste ao valor neutro da sua versao
?>
<form method="post" action="/pkclear_processar.php">
  <input type="hidden" name="csrf" value="<?= htmlspecialchars($_SESSION['csrf']) ?>">
  <select name="char" required>
    <?php foreach ($chars as $c):
        $temPk = ((int)$c['PkLevel'] > $NEUTRO); ?>
      <option value="<?= htmlspecialchars($c['Name']) ?>" <?= $temPk ? '' : 'disabled' ?>>
        <?= htmlspecialchars($c['Name']) ?>
        <?= $temPk ? '— PK nível ' . (int)$c['PkLevel'] . ' (' . (int)$c['PkCount'] . ' kills)' : '— sem PK' ?>
      </option>
    <?php endforeach; ?>
  </select>
  <button type="submit">Limpar PK</button>
</form>

Passo 5 — Processar a limpeza com segurança

Valide o token CSRF, chame a procedure com parâmetros vinculados e trate o retorno. Nenhuma regra de negócio é decidida no PHP.

<?php
session_start();
require 'db.php';
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. Recarregue a pagina.');
}
unset($_SESSION['csrf']);

$char = trim($_POST['char'] ?? '');
if ($char === '' || strlen($char) > 10) { exit('Personagem invalido.'); }

$pdo = getDB();
$stmt = $pdo->prepare(
    'EXEC dbo.WZ_PkClearViaSite @AccountID = :acc, @CharName = :char,
                               @NeutralPk = :neutro, @ZenCost = :zen'
);
$stmt->execute([
    ':acc'    => $_SESSION['account_id'],
    ':char'   => $char,
    ':neutro' => 3,          // EXEMPLO
    ':zen'    => 3000000,    // EXEMPLO de custo
]);
$res = $stmt->fetch();

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

Passo 6 — Impedir PK Clear com personagem online

Este é o mesmo risco do reset via site: enquanto o personagem está no jogo, o GameServer guarda os dados em memória e sobrescreve o banco no próximo save. Se você limpar o PK pelo site com o char online, a limpeza é revertida — e o jogador pode ter pago por nada. A procedure já bloqueia com ConnectStat <> 0, mas confirme onde esse campo vive na sua versão: pode estar em Character ou em MEMB_STAT (ligado à conta). Ajuste a consulta conforme necessário e, idealmente, oriente o jogador a sair do jogo antes de limpar.

Passo 7 — Cooldown e proteção contra abuso

Um PK Clear muito acessível quebra a mecânica de risco do PvP. Recomendações:

  1. Preço significativo: o custo em Zen ou WCoin deve doer o suficiente para que matar players tenha consequência real.
  2. Cooldown por personagem: registre um LastPkClear e bloqueie nova limpeza antes de X minutos.
  3. Rate limiting por conta: evita scripts que limpam PK em loop.
  4. Auditoria da PkClearLog: monitore contas com muitas limpezas seguidas.
-- Detectar contas que limparam PK mais de 5 vezes nas ultimas 24h
SELECT AccountID, COUNT(*) AS Limpezas
FROM dbo.PkClearLog
WHERE ClearDate > DATEADD(HOUR, -24, GETDATE())
GROUP BY AccountID
HAVING COUNT(*) > 5
ORDER BY Limpezas DESC;

Erros comuns e soluções

ErroCausa provávelSolução
PK volta após entrar no jogoPersonagem estava onlineBloquear com ConnectStat <> 0; pedir logout
PK Clear não muda nada visívelValor neutro errado (0 x 3)Confirmar o neutro da versão com teste
Cobrança feita mas PK não limpoDébito em query separadaDébito e limpeza na mesma transação
Personagem de outra conta limpoNão valida propriedadeComparar AccountID na procedure
Erro de conversão no PHPTipo de parâmetro incompatívelAlinhar tipos PDO x procedure
Injeção pelo nome do charConcatenação na querySempre prepared statements

Checklist de lançamento

  • Backup do banco MuOnline feito antes dos testes.
  • Campos de PK (PkLevel, PkCount, PkTime) confirmados na sua versão.
  • Valor neutro de PkLevel validado com personagem de teste.
  • Stored procedure WZ_PkClearViaSite criada e testada no SSMS.
  • Tabela PkClearLog criada e recebendo registros.
  • Conexão PDO com prepared statements, sem concatenação.
  • Token CSRF de uso único no formulário e no processador.
  • Bloqueio de limpeza para personagem online validado.
  • Validação de propriedade (AccountID) em duas camadas.
  • Custo e cooldown calibrados para não quebrar o PvP.
  • Teste ponta a ponta em homologação antes de abrir ao público.

Com esse sistema, o PK Clear vira um serviço rápido e lucrativo sem comprometer o equilíbrio do servidor. A lógica sensível fica protegida no banco, cada operação é atômica e registrada, e a camada PHP permanece pequena e difícil de explorar. Ajuste os valores neutros, o custo e o cooldown ao perfil do seu servidor — e sempre teste em homologação antes de subir para produção.

Perguntas frequentes

O que exatamente o PK Clear remove do personagem?

Ele zera o status de assassino (PK): o nível de PK, o contador de kills e o tempo de penalidade. Os campos variam por versão, mas geralmente são PkLevel, PkCount e PkTime na tabela Character.

O jogador precisa estar offline para o PK Clear via site funcionar?

Sim, na maioria das versões. Se o personagem estiver online, o GameServer sobrescreve o banco ao salvar e o PK volta. Bloqueie a operação quando ConnectStat for diferente de 0.

Qual a diferença entre PkLevel e PkCount?

PkLevel é o grau de penalidade (comum, assassino, herói), que afeta drop ao morrer e bloqueio em cidades. PkCount é o número de mortes de jogadores acumuladas. O PK Clear normalmente zera ambos e o PkTime.

Posso cobrar WCoin ou Zen pelo PK Clear?

Sim. Faça o débito dentro da mesma stored procedure e da mesma transação que limpa o PK, garantindo que a cobrança só ocorra se a limpeza for concluída.

Como impedir que jogadores abusem do PK Clear para farmar players sem punição?

Cobre um valor significativo, aplique cooldown por personagem e registre cada limpeza numa tabela de log para auditar padrões suspeitos.

GA
Editor de guias e builds

Gabriel cobre gameplay, builds de classes, PvP e progressão. Testa cada estratégia em servidor antes de publicar.

Continue lendo

Artigos relacionados