mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 15:08:47 -07:00
Refactor date handling: replace year 9999 with WireDates.FarFuture to prevent timezone-related parsing issues in player responses
aussie fix
This commit is contained in:
@@ -174,13 +174,21 @@ public class PlayerService(DorkNetDbContext db, RoomService rooms, ILogger<Playe
|
||||
// Self-heal: if the row exists but the bookkeeping fields
|
||||
// drift (e.g. someone manually edited Username), restore them
|
||||
// so admin tools always show "Coach" as the owner.
|
||||
if (existing.Username != SystemAccountUsername || existing.DisplayName != SystemAccountUsername)
|
||||
var nameDrift = existing.Username != SystemAccountUsername
|
||||
|| existing.DisplayName != SystemAccountUsername;
|
||||
// Heal a legacy year-9999 BannedUntil written before WireDates:
|
||||
// such a value corrupts the watch's local-timezone date parse if it
|
||||
// ever reaches a client (e.g. via /api/playersbanned). See WireDates.
|
||||
var banDrift = existing.BannedUntil.HasValue
|
||||
&& existing.BannedUntil.Value > WireDates.FarFuture;
|
||||
if (nameDrift || banDrift)
|
||||
{
|
||||
await db.Players
|
||||
.Where(p => p.Id == SystemAccountId)
|
||||
.ExecuteUpdateAsync(s => s
|
||||
.SetProperty(p => p.Username, SystemAccountUsername)
|
||||
.SetProperty(p => p.DisplayName, SystemAccountUsername));
|
||||
.SetProperty(p => p.DisplayName, SystemAccountUsername)
|
||||
.SetProperty(p => p.BannedUntil, WireDates.FarFuture));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -204,7 +212,10 @@ public class PlayerService(DorkNetDbContext db, RoomService rooms, ILogger<Playe
|
||||
// Far-future ban as a hard authorization backstop in case a
|
||||
// forged JWT ever names player 1 as the subject. BanCheckMiddleware
|
||||
// refuses any authenticated request when BannedUntil > now.
|
||||
BannedUntil = new DateTime(9999, 12, 31, 0, 0, 0, DateTimeKind.Utc),
|
||||
// WireDates.FarFuture (2099), NOT year 9999 — a 9999 BannedUntil
|
||||
// serialised to the watch overflows its local-timezone date parse
|
||||
// in some DST timezones and corrupts the response. See WireDates.
|
||||
BannedUntil = WireDates.FarFuture,
|
||||
PasswordHash = null,
|
||||
CreatedAt = new DateTime(2020, 3, 6, 0, 0, 0, DateTimeKind.Utc),
|
||||
LastSeenAt = new DateTime(2020, 3, 6, 0, 0, 0, DateTimeKind.Utc),
|
||||
|
||||
@@ -867,8 +867,11 @@ public class StoreService(DorkNetDbContext db, LevelService level, IConfiguratio
|
||||
{
|
||||
StorefrontType = storefrontType,
|
||||
StorefrontName = name,
|
||||
NextUpdate = "9999-12-31T00:00:00Z",
|
||||
NewUntil = "9999-12-31T00:00:00Z",
|
||||
// NOT year 9999: that overflows the watch's local-timezone date
|
||||
// parse in some DST timezones → "malformed RecNet response" and a
|
||||
// broken store for that player only. See WireDates.
|
||||
NextUpdate = WireDates.FarFutureIso,
|
||||
NewUntil = WireDates.FarFutureIso,
|
||||
StoreItems = storeItems,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace DorkNet.Server;
|
||||
|
||||
/// <summary>
|
||||
/// Canonical "far future / never expires" sentinel dates for anything we
|
||||
/// serialise back to the watch.
|
||||
///
|
||||
/// DO NOT use <c>DateTime.MaxValue</c> or year 9999 in client-facing wire
|
||||
/// data. The 2020.12 watch parses ISO-8601 timestamps and adjusts them to the
|
||||
/// player's LOCAL timezone (DateTimeParse.ParseISO8601 → AdjustTimeZoneToLocal
|
||||
/// → TimeZoneInfo DST resolution). For a player whose timezone has DST
|
||||
/// adjustment rules, resolving the DST transition for a year-9999 instant
|
||||
/// pushes the computed transition point past <c>DateTime.MaxValue</c> (year
|
||||
/// 10000) and throws <c>ArgumentOutOfRangeException: Year, Month, and Day
|
||||
/// parameters describe an un-representable DateTime</c>. That blows up the
|
||||
/// WHOLE response deserialization → "Received malformed RecNet response" → the
|
||||
/// store / gift drops / promo board silently break, but ONLY for players in
|
||||
/// the affected timezones (everyone else parses the same payload fine, which
|
||||
/// makes it look like a per-player gremlin).
|
||||
///
|
||||
/// 2099 is effectively "forever" for a 2020-era client and is nowhere near the
|
||||
/// overflow boundary in any timezone.
|
||||
/// </summary>
|
||||
public static class WireDates
|
||||
{
|
||||
/// <summary>Far-future sentinel as a <see cref="DateTime"/> (UTC).</summary>
|
||||
public static readonly DateTime FarFuture =
|
||||
new(2099, 12, 31, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <summary>Far-future sentinel pre-rendered as the ISO-8601 Z string the
|
||||
/// watch expects (used where the DTO field is a raw string).</summary>
|
||||
public const string FarFutureIso = "2099-12-31T00:00:00Z";
|
||||
}
|
||||
Reference in New Issue
Block a user