namespace GameServer.Contents.GameMode.Helper; using Nettention.Proud; public class PingLatencyPolicy { public int PingKickThreshold { get; set; } = 600; public TimeSpan PingKickDuration { get; set; } = TimeSpan.FromSeconds(30); } //===================================================================== // LatencyChecker - 플레이가 불가능한 핑 지연을 체크한다. super peer와의 핑을 체크 //===================================================================== public class PingLatencyKicker { public class ClientLatency { public HostID HostId { get; set; } public int PingSum { get; set; } public int PingCheckCount { get; set; } public DateTime PingCheckBeginTime { get; set; } = DateTime.UtcNow; public int PingAverage => PingCheckCount > 0 ? PingSum / PingCheckCount: 0; } private readonly PingLatencyPolicy m_ping_latency_policy; private readonly SortedList m_host_latencies = new(); private HostID m_group_id; private HostID m_super_peer_id; private readonly NetServer m_server; public PingLatencyKicker(NetServer server, PingLatencyPolicy pingLatencyPolicy) { m_ping_latency_policy = pingLatencyPolicy; m_server = server; } public void setGroupHostId(HostID groupId) { m_group_id = groupId; } public void setSuperPeerId(HostID superPeerId) { m_super_peer_id = superPeerId; } //================================================================= // 호스트 그룹에 속한 클라이언트의 핑 지연을 체크하여, 게임이 불가한 지연이 발생한 경우 해당 호스트를 반환 //============================================================== public IEnumerable shouldKick() { if (!isValidGroup()) { return []; } checkAndValidateSuperPeer(); if (!isValidSuperPeer()) { return []; } var kick_host_ids = getGroupMemberHostIds().Where(x => checkLatency(x) == true); return kick_host_ids; } public async Task> shouldKickAsync() { return await Task.Run(shouldKick); } //================================================================= // 핑 지연 체크 //================================================================= private bool checkLatency(HostID hostId) { var ping_to_super_peer = getP2PRecentPingMs(hostId); m_host_latencies.TryGetValue(hostId, out var client_latency); if (ping_to_super_peer > m_ping_latency_policy.PingKickThreshold) { if (client_latency == null) { client_latency = new ClientLatency { HostId = hostId, PingSum = 0, PingCheckCount = 0, PingCheckBeginTime = DateTime.UtcNow }; m_host_latencies.Add(hostId, client_latency); } } if (client_latency != null) { if (client_latency.PingCheckBeginTime + m_ping_latency_policy.PingKickDuration > DateTime.UtcNow) { if (client_latency.PingAverage > m_ping_latency_policy.PingKickThreshold) { return true; } m_host_latencies.Remove(hostId); } } return false; } private int getP2PRecentPingMs(HostID hostId) { return m_server.GetP2PRecentPingMs(hostId, m_super_peer_id); } private IEnumerable getGroupMemberHostIds() { var host_ids = new List(); var group_info = m_server.GetP2PGroupInfo(m_group_id); for (int i = 0; i < group_info.members.GetCount(); i++) { host_ids.Add(group_info.members.At(i)); } return host_ids; } //================================================================= // 호스트 그룹이 삭제됐는 지 확인 //================================================================= private bool isValidGroup() { if (m_group_id == HostID.HostID_None) return false; var group_info = m_server.GetP2PGroupInfo(m_group_id); if (group_info == null || group_info.members.GetCount() == 0) { m_group_id = HostID.HostID_None; return false; } return true; } //================================================================= // 수퍼 피어가 퇴장한 경우 등을 체크 //================================================================= private void checkAndValidateSuperPeer() { // 수퍼 피어가 퇴장한 경우 등을 체크 if (m_super_peer_id != HostID.HostID_None && m_server.GetClientInfo(m_super_peer_id) == null) { m_super_peer_id = HostID.HostID_None; } } //================================================================= // 수퍼 피어가 설정되어 있는지 체크 //================================================================= private bool isValidSuperPeer() { return m_super_peer_id != HostID.HostID_None; } }