From 0fd9dd802bca6b894878be3b3e1036de32a8be5d Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Tue, 12 Mar 2024 19:01:01 +1100 Subject: [PATCH] Try to fix issue where time component of dates saved to the DB was being saved/loaded with culture specific time separator instead of : (Thanks Fam0r) --- MCGalaxy/Commands/Chat/CmdSend.cs | 2 +- MCGalaxy/Commands/Information/CmdOpStats.cs | 2 +- .../Commands/Maintenance/CmdPlayerEdit.cs | 6 +++--- MCGalaxy/Database/Backends/MySQL.cs | 2 +- MCGalaxy/Database/Database.cs | 7 ++++--- MCGalaxy/Database/PlayerData.cs | 20 +++++++++---------- .../Modules/Relay/Discord/DiscordWebsocket.cs | 6 +++--- MCGalaxy/Player/Player.Handlers.cs | 2 +- MCGalaxy/Player/Player.cs | 2 +- MCGalaxy/Player/PlayerActions.cs | 12 +++++------ MCGalaxy/util/Extensions/DateExts.cs | 13 ++++++++++++ 11 files changed, 44 insertions(+), 30 deletions(-) diff --git a/MCGalaxy/Commands/Chat/CmdSend.cs b/MCGalaxy/Commands/Chat/CmdSend.cs index eaa89881b..01e2143ca 100644 --- a/MCGalaxy/Commands/Chat/CmdSend.cs +++ b/MCGalaxy/Commands/Chat/CmdSend.cs @@ -54,7 +54,7 @@ namespace MCGalaxy.Commands.Chatting } Database.AddRow("Inbox" + name, "PlayerFrom, TimeSent, Contents", - p.name, DateTime.Now.ToString(Database.DateFormat), message); + p.name, DateTime.Now.ToInvariantDateString(), message); p.CheckForMessageSpam(); Player target = PlayerInfo.FindExact(name); diff --git a/MCGalaxy/Commands/Information/CmdOpStats.cs b/MCGalaxy/Commands/Information/CmdOpStats.cs index 47ef59c84..30b1acb1c 100644 --- a/MCGalaxy/Commands/Information/CmdOpStats.cs +++ b/MCGalaxy/Commands/Information/CmdOpStats.cs @@ -30,7 +30,7 @@ namespace MCGalaxy.Commands.Info public override bool UseableWhenFrozen { get { return true; } } public override void Use(Player p, string message, CommandData data) { - string end = DateTime.Now.ToString(Database.DateFormat); + string end = DateTime.Now.ToInvariantDateString(); string start = "thismonth", name = null; string[] args = message.SplitSpaces(); diff --git a/MCGalaxy/Commands/Maintenance/CmdPlayerEdit.cs b/MCGalaxy/Commands/Maintenance/CmdPlayerEdit.cs index f06f518da..e713a156c 100644 --- a/MCGalaxy/Commands/Maintenance/CmdPlayerEdit.cs +++ b/MCGalaxy/Commands/Maintenance/CmdPlayerEdit.cs @@ -146,13 +146,13 @@ namespace MCGalaxy.Commands.Maintenance { return; } - DateTime date; - if (!DateTime.TryParseExact(args[2], Database.DateFormat, null, 0, out date)) { + DateTime dt; + if (!args[2].TryParseInvariantDateString(out dt)) { p.Message("Invalid date. It must be in format: " + Database.DateFormat); return; } - if (who != null) setter(date); + if (who != null) setter(dt); PlayerDB.Update(args[0], column, args[2]); MessageDataChanged(p, args[0], args[1], args[2]); } diff --git a/MCGalaxy/Database/Backends/MySQL.cs b/MCGalaxy/Database/Backends/MySQL.cs index 15b5d28ce..0873ee62c 100644 --- a/MCGalaxy/Database/Backends/MySQL.cs +++ b/MCGalaxy/Database/Backends/MySQL.cs @@ -241,7 +241,7 @@ namespace MCGalaxy.SQL string RawGetDateTime(int col) { DateTime date = GetDateTime(col); - return date.ToString(Database.DateFormat); + return date.ToInvariantDateString(); } public override string GetStringValue(int col) { diff --git a/MCGalaxy/Database/Database.cs b/MCGalaxy/Database/Database.cs index f6d3b0f6c..5ab0a3eb5 100644 --- a/MCGalaxy/Database/Database.cs +++ b/MCGalaxy/Database/Database.cs @@ -27,7 +27,7 @@ namespace MCGalaxy.SQL public static class Database { public static IDatabaseBackend Backend; - public const string DateFormat = "yyyy-MM-dd HH:mm:ss"; + public const string DateFormat = "yyyy-MM-dd HH:mm:ss"; /// Counts rows in the given table. /// Optional SQL to filter which rows are counted. @@ -244,9 +244,10 @@ namespace MCGalaxy.SQL } public static DateTime ParseDBDate(string value) { - DateTime date; + DateTime dt; // prefer the exact format - if (DateTime.TryParseExact(value, DateFormat, null, 0, out date)) return date; + if (value.TryParseInvariantDateString(out dt)) return dt; + return DateTime.Parse(value); } } diff --git a/MCGalaxy/Database/PlayerData.cs b/MCGalaxy/Database/PlayerData.cs index 0ee90a28f..7b68bce79 100644 --- a/MCGalaxy/Database/PlayerData.cs +++ b/MCGalaxy/Database/PlayerData.cs @@ -56,7 +56,7 @@ namespace MCGalaxy.DB p.FirstLogin = DateTime.Now; p.TimesVisited = 1; - string now = DateTime.Now.ToString(Database.DateFormat); + string now = DateTime.Now.ToInvariantDateString(); Database.AddRow("Players", "Name, IP, FirstLogin, LastLogin, totalLogin, Title, " + "totalDeaths, Money, totalBlocks, totalKicked, Messages, TimeSpent", p.name, p.ip, now, now, 1, "", 0, 0, 0, 0, 0, (long)p.TotalTime.TotalSeconds); @@ -155,17 +155,17 @@ namespace MCGalaxy.DB static DateTime ParseDateTime(ISqlRecord record, string name) { int i = record.GetOrdinal(name); + DateTime dt; + // dates are a major pain + string raw = record.GetStringValue(i); + if (raw.TryParseInvariantDateString(out dt)) return dt; + try { - string raw = record.GetStringValue(i); - return DateTime.ParseExact(raw, Database.DateFormat, null); - } catch { - try { - return record.GetDateTime(i); - } catch (Exception ex) { - Logger.LogError("Error parsing date", ex); - return DateTime.MinValue; - } + return record.GetDateTime(i); + } catch (Exception ex) { + Logger.LogError("Error parsing date", ex); + return DateTime.MinValue; } } diff --git a/MCGalaxy/Modules/Relay/Discord/DiscordWebsocket.cs b/MCGalaxy/Modules/Relay/Discord/DiscordWebsocket.cs index 6919b8704..461cdacf9 100644 --- a/MCGalaxy/Modules/Relay/Discord/DiscordWebsocket.cs +++ b/MCGalaxy/Modules/Relay/Discord/DiscordWebsocket.cs @@ -202,11 +202,11 @@ namespace MCGalaxy.Modules.Relay.Discord if (obj.TryGetValue("s", out sequence)) Session.LastSeq = (string)sequence; - string eventName = (string)obj["t"]; - object rawData; + string eventName = (string)obj["t"]; + object rawData; obj.TryGetValue("d", out rawData); - JsonObject data = (JsonObject)rawData; + JsonObject data = rawData as JsonObject; if (eventName == "READY") { HandleReady(data); diff --git a/MCGalaxy/Player/Player.Handlers.cs b/MCGalaxy/Player/Player.Handlers.cs index 52317ff5d..3fd40f639 100644 --- a/MCGalaxy/Player/Player.Handlers.cs +++ b/MCGalaxy/Player/Player.Handlers.cs @@ -665,7 +665,7 @@ namespace MCGalaxy try { //opstats patch (since 5.5.11) if (Server.Opstats.CaselessContains(cmd) || (cmd.CaselessEq("review") && args.CaselessEq("next") && Server.reviewlist.Count > 0)) { Database.AddRow("Opstats", "Time, Name, Cmd, Cmdmsg", - DateTime.Now.ToString(Database.DateFormat), name, cmd, args); + DateTime.Now.ToInvariantDateString(), name, cmd, args); } } catch { } diff --git a/MCGalaxy/Player/Player.cs b/MCGalaxy/Player/Player.cs index d392b47f8..1ef01e244 100644 --- a/MCGalaxy/Player/Player.cs +++ b/MCGalaxy/Player/Player.cs @@ -182,7 +182,7 @@ namespace MCGalaxy { long drawn = PlayerData.Pack(TotalDeleted, TotalDrawn); Database.UpdateRows("Players", "IP=@0, LastLogin=@1, totalLogin=@2, totalDeaths=@3, Money=@4, " + "totalBlocks=@5, totalCuboided=@6, totalKicked=@7, TimeSpent=@8, Messages=@9", "WHERE Name=@10", - ip, LastLogin.ToString(Database.DateFormat), + ip, LastLogin.ToInvariantDateString(), TimesVisited, TimesDied, money, blocks, drawn, TimesBeenKicked, (long)TotalTime.TotalSeconds, TotalMessagesSent, name); } diff --git a/MCGalaxy/Player/PlayerActions.cs b/MCGalaxy/Player/PlayerActions.cs index 4b67b3969..8b4c1404f 100644 --- a/MCGalaxy/Player/PlayerActions.cs +++ b/MCGalaxy/Player/PlayerActions.cs @@ -36,7 +36,7 @@ namespace MCGalaxy bool didJoin = false; try { - didJoin = name == null ? GotoLevel(p, lvl) : GotoMap(p, name); + didJoin = name == null ? GotoLevel(p, lvl, false) : GotoMap(p, name); } finally { Interlocked.Exchange(ref p.UsingGoto, 0); Server.DoGC(); @@ -50,14 +50,14 @@ namespace MCGalaxy static bool GotoMap(Player p, string name) { Level lvl = LevelInfo.FindExact(name); - if (lvl != null) return GotoLevel(p, lvl); + if (lvl != null) return GotoLevel(p, lvl, false); if (Server.Config.AutoLoadMaps) { string map = Matcher.FindMaps(p, name); if (map == null) return false; lvl = LevelInfo.FindExact(map); - if (lvl != null) return GotoLevel(p, lvl); + if (lvl != null) return GotoLevel(p, lvl, false); return LoadOfflineLevel(p, map); } else { lvl = Matcher.FindLevels(p, name); @@ -66,7 +66,7 @@ namespace MCGalaxy Command.Find("Search").Use(p, "levels " + name); return false; } - return GotoLevel(p, lvl); + return GotoLevel(p, lvl, false); } } @@ -87,13 +87,13 @@ namespace MCGalaxy LevelActions.Load(p, map, false); Level lvl = LevelInfo.FindExact(map); - if (lvl != null) return GotoLevel(p, lvl); + if (lvl != null) return GotoLevel(p, lvl, true); p.Message("Level \"{0}\" failed to be auto-loaded.", map); return false; } - static bool GotoLevel(Player p, Level lvl) { + static bool GotoLevel(Player p, Level lvl, bool autoloaded) { if (p.level == lvl) { p.Message("You are already in {0}&S.", lvl.ColoredName); return false; } bool canJoin = lvl.CanJoin(p); diff --git a/MCGalaxy/util/Extensions/DateExts.cs b/MCGalaxy/util/Extensions/DateExts.cs index 654eb4178..4eb6664fe 100644 --- a/MCGalaxy/util/Extensions/DateExts.cs +++ b/MCGalaxy/util/Extensions/DateExts.cs @@ -46,5 +46,18 @@ namespace MCGalaxy TimeSpan oldestDelta = now - log[0]; return oldestDelta > checkInterval; } + + + // Can't just use HH:mm:ss, as e.g. Finnish converts : to . + // https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings#timeSeparator + const string INVARIANT_DATE_FORMAT = "yyyy-MM-dd HH':'mm':'ss"; + + public static string ToInvariantDateString(this DateTime time) { + return time.ToString(INVARIANT_DATE_FORMAT); + } + + public static bool TryParseInvariantDateString(this string str, out DateTime dt) { + return DateTime.TryParseExact(str, INVARIANT_DATE_FORMAT, null, 0, out dt); + } } }