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.
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).