using System.Collections.Concurrent; using Google.Protobuf; using NeoSmart.AsyncLock; using Nettention.Proud; using ServerCore; using ServerBase; namespace ServerBase; public class LargePacketHandler { //private ConcurrentDictionary<(Player, string), SortedSet> m_large_packets = new(); private ConcurrentDictionary> m_large_packets = new(); // 하나의 패킷만 처리 private static AsyncLock _lock = new(); private SessionBase? m_network_session { get; set; } public void onInit(SessionBase networkSession) { m_network_session = networkSession; var func = checkLargePacket; _ = PeriodicTaskHelper.runTask(func, TimeSpan.FromMilliseconds(Constant.LARGE_PACKET_CHECK_INTERVAL_MSEC)); } public Result collectPacket(IEntityWithSession ssesion, string packetUid, Int32 totalPacketCount, Int32 packetIdx, ByteString datas) { Log.getLogger().debug($"LargePacketCollect packetUid : {packetUid}, totalPacketCount : {totalPacketCount}, packetIdx : {packetIdx}, datasize : {datas.Length}"); var collected_packet = new CollectedPacketInfo(packetUid, totalPacketCount, packetIdx, datas); //var user_guid = player.getUserGuid(); var result = addCollectedPacket(ssesion, collected_packet); return result; } private Result addCollectedPacket(IEntityWithSession ssesion, CollectedPacketInfo packet) { using (_lock.Lock()) { if (false == m_large_packets.TryGetValue(ssesion, out var collected_packets)) { collected_packets = new SortedSet(); collected_packets.Add(packet); m_large_packets.TryAdd(ssesion, collected_packets); } else { collected_packets.Add(packet); } } Log.getLogger().debug($"addCollectedPacket done"); return new Result(); } private Result removeCollectedPacket(List sessionIds) { if (sessionIds.Count == 0) return new(); var result = new Result(); using (_lock.Lock()) { foreach (var session_id in sessionIds) { m_large_packets.TryRemove(session_id, out var _); } } return result; } public Task checkLargePacket() { var result = new Result(); List delete_users = new(); foreach (var packets in m_large_packets) { var session = packets.Key; var packet_set = packets.Value; var isRecvResult = validCheck(session, packet_set); if (isRecvResult.isSuccess()) { if (false == processPacket(session, packet_set)) { Log.getLogger().error("processPacket error!!"); delete_users.Add(session); break; } delete_users.Add(session); continue; } if (isRecvResult.getErrorCode() == ServerErrorCode.LargePacketNotAllReceived) continue; if (isRecvResult.getErrorCode() != ServerErrorCode.LargePacketRecvTimeOver) { send_GS2C_NTF_LARGE_PACKET_TIMEOUT(session, isRecvResult, packet_set); delete_users.Add(session); continue; } } if (delete_users.Count > 0) { removeCollectedPacket(delete_users); } return Task.FromResult(result); } private void send_GS2C_NTF_LARGE_PACKET_TIMEOUT(IEntityWithSession session, Result result, SortedSet packetSet) { var last_recv_time = packetSet.OrderByDescending(p => p.m_last_recv_time).FirstOrDefault(); NullReferenceCheckHelper.throwIfNull(last_recv_time, () => $"Last Receive time is null !! - player:{session.toBasicString()}"); // var uid = packetSet.Select(p => p.m_packet_uid).FirstOrDefault() ?? string.Empty; ClientToGame msg = new(); msg.Message = new(); msg.Message.NtfLargePacketTimeout = new(); msg.Message.NtfLargePacketTimeout.Uid = last_recv_time.m_packet_uid; msg.Message.NtfLargePacketTimeout.LastCheckTime = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(last_recv_time.m_last_recv_time); msg.Message.NtfLargePacketTimeout.LastCheckTime = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow); } private bool processPacket(IEntityWithSession session, SortedSet packets) { var network_session = getNetworkSession(); using (var stream = new MemoryStream()) { foreach (var packet in packets) { if (packet.m_datas == null) { Log.getLogger().warn("Warning: packet.m_datas is null"); continue; } packet.m_datas.WriteTo(stream); } if (stream.Length == 0) { Log.getLogger().warn("Warning: stream is empty after writing packets."); return false; } try { stream.Position = 0; var req = ClientToGame.Parser.ParseFrom(stream); var func = network_session.onCallProtocolHandler(session, req); return true; } catch (Exception e) { Log.getLogger().error($"Exception !!!, Failed to perform !!!, in LargePacketHandler.processPacket() : exception:{e} - {session.toBasicString()}"); return false; } } } private Result validCheck(IEntityWithSession session, SortedSet packets) { var err_msg = string.Empty; var result = new Result(); var sorted_packet_idxs = packets.OrderBy(p => p.m_packet_idx).ToList(); var total_packet_count = packets.Select(p => p.m_total_packet_count).FirstOrDefault(); //아직 패킷이 다 안들어온거다. if (sorted_packet_idxs.Count < total_packet_count) { err_msg = $"Not all packets were received !!! : recvPacketCount:{sorted_packet_idxs.Count} < totalPacketCount:{total_packet_count} - {session.toBasicString()}"; result.setFail(ServerErrorCode.LargePacketNotAllReceived, err_msg); return result; } var last_recv_time = packets.OrderByDescending(p => p.m_last_recv_time).FirstOrDefault(); NullReferenceCheckHelper.throwIfNull(last_recv_time, () => $"last receive time is null !!! - {session.toBasicString()}"); var diff = DateTimeHelper.Current - last_recv_time.m_last_recv_time; if (diff.TotalSeconds > Constant.LARGE_PACKET_CHECK_WAIT_SEC) { err_msg = $"Packet receive timeout exceeded !!! : waitingSec:{diff.TotalSeconds} > WaitMaxSec:{Constant.LARGE_PACKET_CHECK_WAIT_SEC}, lastRecvTime:{last_recv_time} - {session.toBasicString()}"; result.setFail(ServerErrorCode.LargePacketRecvTimeOver, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return result; } public SessionBase getNetworkSession() { NullReferenceCheckHelper.throwIfNull(m_network_session, () => $"m_network_session is null !!!"); return m_network_session; } } public class CollectedPacketInfo : IComparable { public string m_packet_uid { get; } = string.Empty; public Int32 m_total_packet_count { get; } = 0; public Int32 m_packet_idx { get; } = 0; public ByteString? m_datas { get; } = null; public DateTime m_last_recv_time { get; } = DateTime.Now; public CollectedPacketInfo(string packetUid, Int32 totalPacketCount, Int32 packetIdx, ByteString datas) { m_packet_uid = packetUid; m_total_packet_count = totalPacketCount; m_packet_idx = packetIdx; m_datas = datas; } public Int32 CompareTo(CollectedPacketInfo? other) { if (other == null) return 1; return this.m_packet_idx.CompareTo(other.m_packet_idx); } public override bool Equals(object? obj) { if (obj == null || GetType() != obj.GetType()) { return false; } CollectedPacketInfo other = (CollectedPacketInfo)obj; //return m_packet_uid == other.m_packet_uid && // m_total_packet_count == other.m_total_packet_count && // m_packet_idx == other.m_packet_idx && // m_last_recv_time == other.m_last_recv_time; return m_packet_idx == other.m_packet_idx; } public override int GetHashCode() { return HashCode.Combine(m_packet_uid, m_total_packet_count, m_packet_idx, m_last_recv_time); } }