Files
Radicade/Server/Rooms/RoomProgressionService.cs
T
2026-10-16 23:33:13 -07:00

204 lines
7.3 KiB
C#

using Microsoft.EntityFrameworkCore;
using RadicadeServer.API.Notifications;
using RadicadeServer.API.Storefront;
using RadicadeServer.DB;
using RadicadeServer.Extensions;
using RadicadeServer.RecNet.Enums.Messages;
using RadicadeServer.RecNet.Enums.Store;
using RadicadeServer.RecNet.Notifications;
using RadicadeServer.RecNet.Progression;
namespace RadicadeServer.Utils
{
public static class RoomProgressionService
{
public const int XpPerInterval = 10;
public static readonly TimeSpan Interval = TimeSpan.FromMinutes(5);
public const int RoomXpCap = 60;
public const int DailyXpCap = 150;
public static readonly TimeSpan DailyCapCooldown = TimeSpan.FromHours(24);
public const string ProgressionLevelUpdateNotificationId = "PlayerProgressionLevelUpdate";
public static async Task<int> AwardRoomXpAsync(RecDBContext db, long playerId, long roomId, bool isPublicRoom)
{
if (playerId <= 0 || roomId <= 0 || !isPublicRoom) return 0;
var progression = await db.Progression.FirstOrDefaultAsync(x => x.PlayerId == playerId);
if (progression == null)
{
progression = new ProgressionData { PlayerId = playerId };
await db.Progression.AddAsync(progression);
}
var json = progression.GetProgressionJson() ?? new ProgressionJson();
var now = DateTime.UtcNow;
if (json.XpEarnedTodayDate != now.Date)
{
json.XpEarnedToday = 0;
json.XpEarnedTodayDate = now.Date;
json.XpDailyCapReachedAt = null;
}
if (json.XpEarnedToday >= DailyXpCap)
{
if (json.XpDailyCapReachedAt is { } reachedAt && now >= reachedAt.Add(DailyCapCooldown))
{
json.XpEarnedToday = 0;
json.XpEarnedTodayDate = now.Date;
json.XpDailyCapReachedAt = null;
}
else
{
json.XpDailyCapReachedAt ??= now;
progression.Progress = json.ToJson();
await db.SaveChangesAsync();
return 0;
}
}
if (json.RoomXpRoomId != roomId)
{
json.RoomXpRoomId = roomId;
json.RoomXpEarned = 0;
json.RoomXpLastGrantAt = now;
progression.Progress = json.ToJson();
await db.SaveChangesAsync();
return 0;
}
if (json.RoomXpEarned >= RoomXpCap)
{
progression.Progress = json.ToJson();
await db.SaveChangesAsync();
return 0;
}
json.RoomXpLastGrantAt ??= now;
int granted = 0;
while (now - json.RoomXpLastGrantAt.Value >= Interval)
{
int roomHeadroom = RoomXpCap - (json.RoomXpEarned + granted);
int dailyHeadroom = DailyXpCap - (json.XpEarnedToday + granted);
int instalment = Math.Min(XpPerInterval, Math.Min(roomHeadroom, dailyHeadroom));
if (instalment <= 0) break;
granted += instalment;
json.RoomXpLastGrantAt = json.RoomXpLastGrantAt.Value.Add(Interval);
}
if (granted == 0 && now - json.RoomXpLastGrantAt.Value >= Interval)
json.RoomXpLastGrantAt = now;
if (granted == 0)
{
progression.Progress = json.ToJson();
await db.SaveChangesAsync();
return 0;
}
int applied = (int)Math.Min(granted, int.MaxValue);
json.RoomXpEarned += applied;
json.XpEarnedToday += applied;
json.XpEarnedTodayDate = now.Date;
json.LastXpEarnedAt = now;
if (json.XpEarnedToday >= DailyXpCap)
json.XpDailyCapReachedAt ??= now;
int oldLevel = progression.Level;
progression.XP = (int)Math.Min(progression.XP + applied, int.MaxValue);
progression.Progress = json.ToJson();
await db.SaveChangesAsync();
await StorefrontShared.SendXpBalanceUpdateAsync(playerId,
LevelUpGiftService.XpIntoCurrentLevel(progression), applied,
StorefrontBalanceAddTypes.DirectBalanceWithMultiplier);
int computedLevel = LevelUpGiftService.ComputeLevel(LevelUpGiftService.ParseMap(), progression.XP);
if (computedLevel > progression.Level) progression.Level = computedLevel;
await LevelUpGiftService.DeliverLevelUpGiftsAsync(db, playerId,
recomputeFromXp: true, fromLevel: oldLevel, notify: true);
await SendProgressionLevelUpdateAsync(db, playerId, progression);
if (progression.Level > oldLevel)
{
await PushProgressionUpdateAsync(db, playerId);
}
return applied;
}
public static async Task SendProgressionLevelUpdateAsync(RecDBContext db, long playerId,
ProgressionData? progression = null)
{
if (playerId <= 0) return;
try
{
progression ??= await db.Progression.AsNoTracking()
.FirstOrDefaultAsync(x => x.PlayerId == playerId);
if (progression == null) return;
var notification = new Notification
{
Id = ProgressionLevelUpdateNotificationId,
Msg = new PlayerProgressionLevelUpdateNotification(progression)
};
await Notify.SendNotify(playerId, notification);
await Notify.SendToSubscribers(playerId, notification);
}
catch (Exception)
{
}
}
public static async Task PushProgressionUpdateAsync(RecDBContext db, long playerId)
{
await SendProgressionLevelUpdateAsync(db, playerId);
try
{
var profile = await db.Profiles.AsNoTracking().FirstOrDefaultAsync(x => x.accountId == playerId);
if (profile == null) return;
var plrData = await db.Players.AsNoTracking().FirstOrDefaultAsync(x => x.AccountId == playerId);
var progression = await db.Progression.AsNoTracking().FirstOrDefaultAsync(x => x.PlayerId == playerId);
if (progression != null)
await StorefrontShared.SendXpBalanceUpdateAsync(playerId,
LevelUpGiftService.XpIntoCurrentLevel(progression), 0,
StorefrontBalanceAddTypes.LevelUp);
await Notify.SendNotify(playerId, new Notification
{
Id = "AccountUpdate",
Msg = profile
});
await Notify.UpdateSubscriptions(playerId, Notifs.SubscriptionUpdateProfile, profile);
if (plrData != null)
await Notify.SendNotify(playerId, new Notification
{
Id = "SelfAccountUpdate",
Msg = PlayerUtils.BuildSelfAccount(profile, plrData)
});
}
catch (Exception)
{
}
}
}
}