namespace Supercell.Laser.Logic.Util { using Supercell.Laser.Logic.Message; using System.Collections.Concurrent; public static class ServerDiagnostics { private static readonly DateTime StartedAtUtc = DateTime.UtcNow; private static readonly ConcurrentDictionary IncomingPacketCounts = new ConcurrentDictionary(); private static readonly ConcurrentDictionary OutgoingPacketCounts = new ConcurrentDictionary(); public static TimeSpan Uptime => DateTime.UtcNow - StartedAtUtc; public static string DescribeMessage(GameMessage message) { return message == null ? "UnknownMessage(?)" : $"{message.GetType().Name}({message.GetMessageType()})"; } public static void RecordIncomingPacket(GameMessage message) { RecordPacket(IncomingPacketCounts, message); } public static void RecordOutgoingPacket(GameMessage message) { RecordPacket(OutgoingPacketCounts, message); } public static string BuildRuntimeSummary(int tcpOnline, int online) { return $"uptime={Uptime:dd\\.hh\\:mm\\:ss} tcp_online={tcpOnline} online={online}"; } public static string BuildPacketSummary(int top = 5) { string incoming = string.Join(", ", GetTopPackets(IncomingPacketCounts, top)); string outgoing = string.Join(", ", GetTopPackets(OutgoingPacketCounts, top)); if (string.IsNullOrEmpty(incoming)) incoming = "none"; if (string.IsNullOrEmpty(outgoing)) outgoing = "none"; return $"incoming[{incoming}] outgoing[{outgoing}]"; } private static void RecordPacket(ConcurrentDictionary packetCounts, GameMessage message) { string key = DescribeMessage(message); packetCounts.AddOrUpdate(key, 1, (_, count) => count + 1); } private static IEnumerable GetTopPackets(ConcurrentDictionary packetCounts, int top) { return packetCounts .OrderByDescending(entry => entry.Value) .Take(top) .Select(entry => $"{entry.Key}={entry.Value}"); } public static bool ShouldLogPacket(int messageType) { return messageType != 10108 && messageType != 10555 && messageType != 14102 && messageType != 20108; } } }