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

Sistema de premiação por tempo online no servidor de MU Online

Implemente um sistema de recompensas por tempo online no servidor de MU Online — tabelas SQL, stored procedures para rastrear horas jogadas e dar prêmios automáticos.

EQ Equipe ViciadosMU · Atualizado em 1 jul 2026 · ⏱ 20 min de leitura
Resposta rápida

Um sistema de premiação por tempo online recompensa jogadores que ficam conectados ao servidor — incentivando retenção e lealdade. A implementação usa tabelas SQL e stored procedures que calculam o tempo jogado e entregam recompensas automaticamente.

Um sistema de premiação por tempo online recompensa jogadores que ficam conectados ao servidor — incentivando retenção e lealdade. A implementação usa tabelas SQL e stored procedures que calculam o tempo jogado e entregam recompensas automaticamente.

Como funciona o sistema

1. Jogador faz login
       │ INSERT ou UPDATE em OnlineTrack (hora_inicio)
       ▼
2. Jogador está online
       │ O GameServer faz UPDATE periódico do tempo total
       ▼
3. Jogador faz logout
       │ Calcula delta (hora_atual - hora_inicio)
       │ Adiciona ao TotalMinutes na tabela
       ▼
4. Scheduled task (a cada 5-30 min)
       │ Verifica quem atingiu milestones (1h, 5h, 10h...)
       │ Insere recompensas na tabela de pending rewards
       ▼
5. Jogador resgata ou recebe automaticamente na próxima sessão

Passo 1 — Criar as tabelas no banco

USE MuOnline;
GO

-- Tabela de rastreamento de tempo online
CREATE TABLE OnlineTrack (
  CharName      VARCHAR(16) NOT NULL,
  AccountID     VARCHAR(10) NOT NULL,
  SessionStart  DATETIME NOT NULL DEFAULT GETDATE(),
  SessionEnd    DATETIME NULL,
  TotalMinutes  BIGINT NOT NULL DEFAULT 0,    -- tempo total acumulado em minutos
  LastSaved     DATETIME NOT NULL DEFAULT GETDATE(),
  CONSTRAINT PK_OnlineTrack PRIMARY KEY (CharName)
);
GO

-- Tabela de marcos de tempo (quando dar recompensa)
CREATE TABLE TimeRewardMilestone (
  MilestoneID   INT IDENTITY(1,1) PRIMARY KEY,
  MinutesRequired BIGINT NOT NULL,           -- horas convertidas em minutos
  RewardType    TINYINT NOT NULL,            -- 1=zen, 2=item, 3=vip, 4=reset
  RewardValue   INT NOT NULL,               -- quantidade (zen) ou item ID
  RewardGroup   TINYINT NOT NULL DEFAULT 0,  -- grupo do item (se tipo=2)
  Description   VARCHAR(128) NOT NULL,
  Repeatable    BIT NOT NULL DEFAULT 0       -- 0=uma vez, 1=repetível
);
GO

-- Inserir marcos de tempo
INSERT INTO TimeRewardMilestone (MinutesRequired, RewardType, RewardValue, Description, Repeatable)
VALUES
  (60,    1, 500000,    '1 hora online: 500.000 zen',  1),  -- a cada 1h, 500k zen
  (300,   1, 2500000,   '5 horas: 2.5M zen',           0),  -- 5h: uma vez
  (600,   3, 1,         '10 horas: VIP Bronze 1 dia',  0),  -- 10h: VIP 1 dia
  (1200,  3, 3,         '20 horas: VIP Ouro 1 dia',    0),  -- 20h: VIP Ouro
  (3000,  4, 1,         '50 horas: 1 Reset grátis',    0),  -- 50h: 1 reset
  (6000,  3, 7,         '100 horas: VIP Ouro 1 semana',0);  -- 100h: VIP 7 dias
GO

-- Tabela de recompensas pendentes (a serem entregues)
CREATE TABLE PendingRewards (
  RewardID      INT IDENTITY(1,1) PRIMARY KEY,
  AccountID     VARCHAR(10) NOT NULL,
  CharName      VARCHAR(16) NOT NULL,
  RewardType    TINYINT NOT NULL,
  RewardValue   INT NOT NULL,
  RewardGroup   TINYINT NOT NULL DEFAULT 0,
  MilestoneID   INT,
  Created       DATETIME NOT NULL DEFAULT GETDATE(),
  Delivered     BIT NOT NULL DEFAULT 0,
  DeliveredAt   DATETIME NULL
);
GO

-- Tabela de histórico de recompensas entregues
CREATE TABLE RewardHistory (
  HistoryID   INT IDENTITY(1,1) PRIMARY KEY,
  AccountID   VARCHAR(10) NOT NULL,
  CharName    VARCHAR(16) NOT NULL,
  RewardDesc  VARCHAR(128),
  AwardedAt   DATETIME NOT NULL DEFAULT GETDATE()
);
GO

Passo 2 — Stored Procedures para o sistema

Registrar login de personagem

CREATE PROCEDURE sp_CharLogin
  @charName  VARCHAR(16),
  @accountID VARCHAR(10)
AS
BEGIN
  SET NOCOUNT ON;

  -- Se já tem registro, atualizar sessão
  IF EXISTS (SELECT 1 FROM OnlineTrack WHERE CharName = @charName)
  BEGIN
    UPDATE OnlineTrack
    SET SessionStart = GETDATE(),
        AccountID    = @accountID,
        LastSaved    = GETDATE()
    WHERE CharName = @charName;
  END
  ELSE
  BEGIN
    -- Primeiro login: criar registro
    INSERT INTO OnlineTrack (CharName, AccountID, SessionStart)
    VALUES (@charName, @accountID, GETDATE());
  END;
END;
GO

Registrar logout e calcular tempo

CREATE PROCEDURE sp_CharLogout
  @charName VARCHAR(16)
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @sessionStart DATETIME, @minutesThis INT;

  SELECT @sessionStart = SessionStart
  FROM OnlineTrack WHERE CharName = @charName;

  IF @sessionStart IS NULL RETURN;  -- sem sessão registrada

  -- Calcular minutos desta sessão
  SET @minutesThis = DATEDIFF(MINUTE, @sessionStart, GETDATE());

  -- Atualizar total acumulado
  UPDATE OnlineTrack
  SET TotalMinutes = TotalMinutes + @minutesThis,
      SessionEnd   = GETDATE(),
      LastSaved    = GETDATE()
  WHERE CharName = @charName;

  -- Verificar e dar recompensas
  EXEC sp_CheckTimeRewards @charName;
END;
GO

Verificar e distribuir recompensas

CREATE PROCEDURE sp_CheckTimeRewards
  @charName VARCHAR(16)
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @totalMin BIGINT, @accountID VARCHAR(10);

  SELECT @totalMin = TotalMinutes, @accountID = AccountID
  FROM OnlineTrack WHERE CharName = @charName;

  IF @totalMin IS NULL RETURN;

  -- Para cada marco que o jogador atingiu e que ainda não foi dado
  INSERT INTO PendingRewards (AccountID, CharName, RewardType, RewardValue, RewardGroup, MilestoneID)
  SELECT
    @accountID,
    @charName,
    m.RewardType,
    m.RewardValue,
    m.RewardGroup,
    m.MilestoneID
  FROM TimeRewardMilestone m
  WHERE
    m.MinutesRequired <= @totalMin
    AND (
      -- Marco não-repetível: verificar se ainda não foi dado
      (m.Repeatable = 0 AND NOT EXISTS (
        SELECT 1 FROM RewardHistory
        WHERE CharName = @charName AND RewardDesc LIKE '%MID:' + CAST(m.MilestoneID AS VARCHAR) + '%'
      ))
      OR
      -- Marco repetível: dar a cada X minutos
      (m.Repeatable = 1 AND NOT EXISTS (
        SELECT 1 FROM PendingRewards
        WHERE CharName = @charName AND MilestoneID = m.MilestoneID AND Delivered = 0
      ))
    );
END;
GO

Atualização periódica (chamar a cada 5 minutos)

-- Stored procedure chamada a cada 5 minutos pelo SQL Agent
CREATE PROCEDURE sp_UpdateOnlineTimes
AS
BEGIN
  SET NOCOUNT ON;

  -- Atualizar o tempo de todos que estão conectados agora
  -- (baseado no ConnectStat da tabela Character)
  UPDATE ot
  SET ot.TotalMinutes = ot.TotalMinutes + 5,   -- adiciona 5 minutos
      ot.LastSaved    = GETDATE()
  FROM OnlineTrack ot
  JOIN Character c ON c.Name = ot.CharName
  WHERE c.ConnectStat = 1;   -- apenas personagens conectados

  -- Verificar recompensas para todos online
  DECLARE @charName VARCHAR(16);
  DECLARE cur CURSOR FOR
    SELECT c.Name
    FROM Character c
    WHERE c.ConnectStat = 1;

  OPEN cur;
  FETCH NEXT FROM cur INTO @charName;
  WHILE @@FETCH_STATUS = 0
  BEGIN
    EXEC sp_CheckTimeRewards @charName;
    FETCH NEXT FROM cur INTO @charName;
  END;
  CLOSE cur;
  DEALLOCATE cur;
END;
GO

Passo 3 — Agendar a execução periódica

Via SQL Server Agent (recomendado)

-- Criar job para rodar a cada 5 minutos
USE msdb;
GO

EXEC sp_add_job
  @job_name = N'MU Online - Update Tempo Online';

EXEC sp_add_jobstep
  @job_name = N'MU Online - Update Tempo Online',
  @step_name = N'Atualizar tempo',
  @command = N'EXEC MuOnline.dbo.sp_UpdateOnlineTimes',
  @database_name = N'MuOnline';

EXEC sp_add_schedule
  @schedule_name = N'A cada 5 minutos',
  @freq_type = 4,           -- diário
  @freq_interval = 1,
  @freq_subday_type = 4,   -- minutos
  @freq_subday_interval = 5;

EXEC sp_attach_schedule
  @job_name = N'MU Online - Update Tempo Online',
  @schedule_name = N'A cada 5 minutos';

EXEC sp_add_jobserver @job_name = N'MU Online - Update Tempo Online';
GO

Via Windows Task Scheduler (para SQL Express sem Agent)

:: update-online-time.bat
sqlcmd -S (local) -U mu_app -P "SenhaMuApp!" -d MuOnline -Q "EXEC sp_UpdateOnlineTimes" -t 30

Configure no Agendador de Tarefas para rodar a cada 5 minutos.

Passo 4 — Exibir ranking de tempo no site

<?php
// ranking-tempo.php — PHP com driver sqlsrv para SQL Server

$conn = sqlsrv_connect('(local)', [
    'Database' => 'MuOnline',
    'UID'      => 'mu_app',
    'PWD'      => 'SenhaMuApp!'
]);

if (!$conn) die("Erro ao conectar");

$sql = "
    SELECT TOP 20
        ot.CharName,
        c.Class,
        c.cLevel,
        ot.TotalMinutes,
        ot.TotalMinutes / 60 AS TotalHoras,
        (SELECT COUNT(*) FROM PendingRewards pr 
         WHERE pr.CharName = ot.CharName AND pr.Delivered = 0) AS PremiosPendentes
    FROM OnlineTrack ot
    JOIN Character c ON c.Name = ot.CharName
    ORDER BY ot.TotalMinutes DESC
";

$result = sqlsrv_query($conn, $sql);
$rank = 1;

echo "<table><tr><th>#</th><th>Personagem</th><th>Classe</th><th>Nível</th><th>Horas Online</th></tr>";

while ($row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC)) {
    printf(
        "<tr><td>%d</td><td>%s</td><td>%d</td><td>%d</td><td>%d horas</td></tr>",
        $rank++,
        htmlspecialchars($row['CharName']),
        $row['Class'],
        $row['cLevel'],
        $row['TotalHoras']
    );
}

echo "</table>";
sqlsrv_close($conn);
?>

Passo 5 — Entrega de recompensas pendentes

Via GM Command

O GM executa o command para dar a recompensa manualmente:

/item [grupo] [id] [level]     ← para dar item ao personagem
/zen [valor]                   ← para dar zen

Via processamento automático no GameServer

Alguns sistemas de GameServer leem a tabela PendingRewards automaticamente na sessão do jogador. Verifique se a sua distribuição suporta isso — consulte a documentação.

Via site (jogador resgata)

<?php
// resgatar-premio.php

session_start();
$accountID = $_SESSION['account_id']; // conta logada no site

$conn = sqlsrv_connect('(local)', ['Database' => 'MuOnline', 'UID' => 'mu_app', 'PWD' => 'SenhaMuApp!']);

// Buscar prêmios pendentes desta conta
$sql = "SELECT RewardID, CharName, RewardType, RewardValue, Created
        FROM PendingRewards
        WHERE AccountID = ? AND Delivered = 0
        ORDER BY Created";
$stmt = sqlsrv_query($conn, $sql, [$accountID]);

$rewards = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
    $rewards[] = $row;
}

// Se solicitou resgate
if ($_POST['action'] === 'redeem' && isset($_POST['reward_id'])) {
    $rewardId = (int)$_POST['reward_id'];
    
    // Verificar que pertence a esta conta
    $check = sqlsrv_query($conn,
        "SELECT RewardID, RewardType, RewardValue FROM PendingRewards WHERE RewardID = ? AND AccountID = ?",
        [$rewardId, $accountID]);
    
    if ($reward = sqlsrv_fetch_array($check, SQLSRV_FETCH_ASSOC)) {
        // Processar recompensa baseada no tipo
        if ($reward['RewardType'] == 1) {
            // Zen — adicionar ao personagem (requer personagem offline)
            // ... implementação específica da distribuição
        }
        
        // Marcar como entregue
        sqlsrv_query($conn,
            "UPDATE PendingRewards SET Delivered = 1, DeliveredAt = GETDATE() WHERE RewardID = ?",
            [$rewardId]);
    }
}
?>

Relatórios úteis

-- Total de horas por conta
SELECT AccountID, SUM(TotalMinutes)/60 AS TotalHoras
FROM OnlineTrack
GROUP BY AccountID
ORDER BY TotalHoras DESC;

-- Jogadores online agora
SELECT c.Name, c.Class, c.cLevel, ot.TotalMinutes/60 AS HorasTotal
FROM Character c
JOIN OnlineTrack ot ON ot.CharName = c.Name
WHERE c.ConnectStat = 1
ORDER BY ot.TotalMinutes DESC;

-- Recompensas ainda não entregues
SELECT CharName, RewardType, RewardValue, Created
FROM PendingRewards
WHERE Delivered = 0
ORDER BY Created;

-- Quantas horas cada personagem jogou esta semana
SELECT CharName,
       SUM(DATEDIFF(MINUTE, SessionStart, ISNULL(SessionEnd, GETDATE())))/60 AS HorasEstaSemana
FROM (
    SELECT CharName, SessionStart, SessionEnd
    FROM OnlineTrack
    WHERE SessionStart >= DATEADD(WEEK, -1, GETDATE())
) sessoes
GROUP BY CharName
ORDER BY HorasEstaSemana DESC;

Perguntas frequentes

O MuServer rastreia o tempo online automaticamente?

Depende da distribuição. Algumas já têm tabelas de login/logout. Para rastreamento preciso, você implementa via SQL trigger ou stored procedure que monitora a tabela de sessão.

Como dar itens automaticamente por tempo online?

O sistema mais simples usa uma tabela 'pending_rewards' — quando o jogador atinge X horas, uma linha é inserida. Na próxima sessão, o GameServer lê e entrega o item, ou o jogador resgata no site.

Posso integrar ranking de tempo online no site?

Sim — a tabela de tempo online fica no banco do servidor. O site PHP conecta via driver sqlsrv e lê as horas de cada jogador para exibir o ranking.

O sistema funciona mesmo se o servidor reiniciar?

Sim, desde que você salve o tempo online no banco regularmente. O GameServer deve fazer UPDATE do tempo ao fechar a sessão ou periodicamente (a cada 5 minutos, por exemplo).

EQ

Equipe editorial do ViciadosMU — portal de MU Online no ar desde 2003.

Continue lendo

Artigos relacionados