internal static IntPtr TraceTree(IntPtr handle, IntPtr baseAddr, int[] offsets, int finalOffset) { #if x64 const int size = 8; #elif x86 const int size = 4; #endif // Arch var buffer = new byte[size]; foreach (var offset in offsets) { if (!ReadProcessMemory(handle, IntPtr.Add(baseAddr, offset), buffer, size, out _)) { IpcClient.SendSignal(Signal.InternalException, new[] { "Unable to calculate address: " + Marshal.GetLastWin32Error(), "MemoryCore", "TraceTree" }); throw new Exception("Unable to calculate address: " + Marshal.GetLastWin32Error()); } #if x64 baseAddr = new IntPtr(BitConverter.ToInt64(buffer, 0)); #elif x86 baseAddr = new IntPtr(BitConverter.ToInt32(buffer, 0)); #endif // Arch if (baseAddr == IntPtr.Zero) { return(IntPtr.Zero); } } return(IntPtr.Add(baseAddr, finalOffset)); }
public void Start() { this._isStopping = false; Task.Run(() => { Log.Warning("MessageDispatcher Output Thread Started"); foreach (var data in this._outputQueue.GetConsumingEnumerable()) { try { var output = data.Consumer(data.Message, data.HeaderLength); data.Listener(data.Header, output); Interlocked.Increment(ref this._messagesDispatched); } catch (Exception e) { IpcClient.SendSignal(Signal.InternalExternalException, new[] { e.Message, e.StackTrace, "MessageDispatcher", "Start" }); } } Log.Warning("MessageDispatcher Output Thread Exited"); }); }
public TCPPacket(byte[] buffer, int ignoreLength) { try { GlobalOffset = ignoreLength; SourcePort = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, ignoreLength + 0)); DestinationPort = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, ignoreLength + 2)); var offsetAndFlags = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(buffer, ignoreLength + 12)); DataOffset = (byte)((offsetAndFlags >> 12) * 4); Flags = (TCPFlags)(offsetAndFlags & 511); // 0b111111111 = 511 Payload = buffer.Skip(ignoreLength + DataOffset).ToArray(); IsValid = true; } catch { SourcePort = 0; DestinationPort = 0; DataOffset = 0; Flags = TCPFlags.NONE; Payload = null; IsValid = false; IpcClient.SendSignal(Signal.ClientPacketParseFail, new[] { "unable to tcplize" }, true); } }
/// <summary> /// Initialize the GameScanner instance with a MemoryReader /// </summary> /// <param name="purgeCache">If true, pointer cache will not be used</param> public void Initialize(bool purgeCache) { Log.Debug($"GS Initialize - Start Initialize"); //foreach (ThreadNr threadNr in Enum.GetValues(typeof(ThreadNr))) //{ // if (_trdPtr.ContainsKey(threadNr)) continue; // if (threadNr == ThreadNr.NoThread) continue; // _trdPtr.Add(threadNr, _reader.GetThreadStack((int)threadNr)); // Log.Verbose($"GS Initialize - Scanned Thread Stack {(int)threadNr}: {_trdPtr[threadNr].ToInt64():X}"); //} if (!purgeCache && ReadBaseOffsetCache()) { return; } var unscanned = new List <SigRecord>(); foreach (SignatureType signatureType in Enum.GetValues(typeof(SignatureType))) { if (_sigPtr.ContainsKey(signatureType)) { continue; } if (!Signature.SignatureLib.TryGetValue(signatureType, out var pattern)) { continue; } unscanned.Add(pattern); } var result = Search(ref unscanned); Log.Warning($"GS Initialize - Unscanned Signature: {string.Join(", ", unscanned.Select(x => Enum.GetName(typeof(SignatureType), x.SelfType)))}"); foreach (var kvp in result) { if (kvp.Value != IntPtr.Zero) { _sigPtr.Add(kvp.Key, kvp.Value); } } if (!ValidateSignatures()) { IpcClient.SendSignal(Signal.MilvanethNeedUpdate, new[] { "Memory", "ValidateSignatures" }); throw new InvalidOperationException("Backbone service self validate failed"); } WriteBaseOffsetCache(); Log.Debug($"GS Initialize - Finish Initialize"); }
public int GetMessageId(MessageIdRetriveKey rawId) { if (msgDic.TryGetValue((int)rawId, out var id)) { return(id); } IpcClient.SendSignal(Signal.MilvanethNeedUpdate, new[] { "Network", "MessageIdRetriver" }); throw new FileNotFoundException("Could not read update file"); }
internal static int Write(IntPtr handle, IntPtr address, byte[] buffer) { if (WriteProcessMemory(handle, address, buffer, buffer.Length, out _)) { return(buffer.Length); } IpcClient.SendSignal(Signal.InternalException, new[] { "Could not write process memory: " + Marshal.GetLastWin32Error(), "MemoryCore", "Write" }); throw new Exception("Could not write process memory: " + Marshal.GetLastWin32Error()); }
internal static void CloseProc(IntPtr ptr) { if (CloseHandle(ptr)) { return; } IpcClient.SendSignal(Signal.InternalException, new[] { "Could not close handle: " + Marshal.GetLastWin32Error(), "MemoryCore", "CloseProc" }); throw new Exception("Could not close handle: " + Marshal.GetLastWin32Error()); }
private void Receive(SocketAsyncEventArgs e) { // Start a new receive operation straight away, without waiting StartReceiving(); try { if (e.SocketError != SocketError.Success) { if (!this._isStopping) { IpcClient.SendSignal(Signal.InternalUnmanagedException, new[] { $"Socket error: \n{(int) e.SocketError}", "Network", "SocketSniffer", "Receive" }); } return; } if (e.BytesTransferred <= 0) { return; } Interlocked.Increment(ref this._packetsObserved); // Copy the bytes received into a new buffer var buffer = new byte[e.BytesTransferred]; Buffer.BlockCopy(e.Buffer, e.Offset, buffer, 0, e.BytesTransferred); EnqueueOutput(new TimestampedData(DateTime.UtcNow, buffer)); } catch (SocketException ex) { IpcClient.SendSignal(Signal.InternalUnmanagedException, new[] { $"Socket exception: {ex.Message} / {ex.HResult} / {Marshal.GetLastWin32Error()}", "Network", "SocketSniffer", "Receive" }); } catch (Exception ex) { IpcClient.SendSignal(Signal.InternalException, new[] { $"Error: {ex.Message}", "Network", "SocketSniffer", "Receive" }); } finally { // Put the SocketAsyncEventArgs back into the pool if (!this._isStopping && this._socket != null && this._socket.IsBound) { this._receivePool.Push(e); this._maxReceiveEnforcer.Release(); } } }
internal static byte[] Read(IntPtr handle, IntPtr address, long size) { var buffer = new byte[size]; if (ReadProcessMemory(handle, address, buffer, buffer.Length, out _)) { return(buffer); } IpcClient.SendSignal(Signal.InternalException, new[] { "Could not read process memory: " + Marshal.GetLastWin32Error(), "MemoryCore", "Read" }); throw new Exception("Could not read process memory: " + Marshal.GetLastWin32Error()); }
internal static IntPtr OpenProc(int pid) { var ptr = OpenProcess(ProcessFlags, false, pid); if (ptr != IntPtr.Zero) { return(ptr); } IpcClient.SendSignal(Signal.InternalException, new [] { "Could not open handle: " + Marshal.GetLastWin32Error(), "MemoryCore", "OpenProc" }); throw new Exception("Could not open handle: " + Marshal.GetLastWin32Error()); }
public void AssignSegment(SocketAsyncEventArgs e) { if (this.nextOffset + this.segmentSize > this.buffer.Length) { IpcClient.SendSignal(Signal.InternalException, new[] { "Buffer exhausted" }); IpcClient.SendSignal(Signal.MilvanethComponentExit, new[] { "Network", "Sniffer" }); throw new InsufficientMemoryException("Buffer exhausted"); } e.SetBuffer(this.buffer, this.nextOffset, this.segmentSize); this.nextOffset += this.segmentSize; }
public static IPEndPoint GetLobbyEndPoint(Process process) { string lobbyHost = null; var lobbyPort = 0; try { using (var searcher = new ManagementObjectSearcher( "SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id)) { foreach (var @object in searcher.Get()) { var commandline = @object["CommandLine"].ToString(); var argv = commandline.Split(' '); foreach (var arg in argv) { var parts = arg.Split('='); if (parts.Length != 2) { continue; } if (parts[0].Contains("LobbyHost01")) { lobbyHost = parts[1]; } else if (parts[0].Contains("LobbyPort01")) { lobbyPort = int.Parse(parts[1]); } } } } if (lobbyHost == null || lobbyPort <= 0) { return(null); } } catch (Exception e) { IpcClient.SendSignal(Signal.InternalException, new [] { $"GetLobbyEndPoint Failed in Connection Picker: \n{e.Message}", "Network", "ConnectionPicker", "GetLobbyEndPoint" }); return(null); } var address = Dns.GetHostAddresses(lobbyHost)[0]; return(new IPEndPoint(address, lobbyPort)); }
private void HasProcStop(object sender, EventArrivedEventArgs e) { var exiting = (uint)e.NewEvent.Properties["ProcessID"].Value; if (exiting != _gamePid && exiting != _parentPid) { return; } Log.Information($"Exiting Process: {exiting}"); IpcClient.SendSignal(Signal.ClientProcessDown, new[] { GameProcess?.Id.ToString() ?? "-1" }); Stopping = true; }
private void EnterPromiscuousMode() { try { this._socket.IOControl(IOControlCode.ReceiveAll, BitConverter.GetBytes(1), new byte[4]); } catch (Exception ex) { IpcClient.SendSignal(Signal.InternalUnmanagedException, new[] { $"Unable to enter promiscuous mode: {ex.Message} / {ex.HResult} / {Marshal.GetLastWin32Error()}", "Network", "SocketSniffer", "EnterPromiscuousMode" }); IpcClient.SendSignal(Signal.MilvanethComponentExit, new[] { "Network", "Sniffer" }); throw; } }
private MessageIdRetriver() { var path = Helper.GetMilFilePathRaw("network.pack"); if (!File.Exists(path)) { IpcClient.SendSignal(Signal.MilvanethNeedUpdate, new[] { "Network", "MessageIdRetriver" }); throw new FileNotFoundException("Could not found update file"); } var ser = new Serializer <Dictionary <int, int> >(path, ""); try { msgDic = ser.Load(); } catch (Exception e) { IpcClient.SendSignal(Signal.MilvanethNeedUpdate, new[] { "Network", "MessageIdRetriver" }); IpcClient.SendSignal(Signal.InternalException, new [] { e.Message, "Network", "MessageIdRetriver", ".ctor" }); throw new FileNotFoundException("Could not parse update file"); } }
private MemoryStream GenerateContent(Stream stream, bool isCompressed, ushort length, int offset = -1) { if (offset >= 0) { stream.Seek(offset, SeekOrigin.Begin); } var content = new MemoryStream(); if (isCompressed) { var body = new byte[length - HeaderLength - 2]; stream.Seek(2, SeekOrigin.Current); stream.Read(body, 0, body.Length); // pos = length try { using (var input = new MemoryStream(body)) using (var deflate = new DeflateStream(input, CompressionMode.Decompress)) { deflate.CopyTo(content); } } catch { IpcClient.SendSignal(Signal.ClientPacketParseFail, new[] { "unable to deflate" }, true); } } else { var body = new byte[length - HeaderLength]; stream.Read(body, 0, body.Length); // pos = bundle.Length content = new MemoryStream(body); } return(content); }
private void StartReceiving() { try { // Get SocketAsyncEventArgs from pool this._maxReceiveEnforcer.Wait(); if (!this._receivePool.TryPop(out var socketEventArgs)) { // Because we are controlling access to pooled SocketAsyncEventArgs, this // *should* never happen... IpcClient.SendSignal(Signal.InternalException, new[] { "Connection pool exhausted" }); IpcClient.SendSignal(Signal.MilvanethComponentExit, new[] { "Network", "Sniffer" }); throw new Exception("Connection pool exhausted"); } // Returns true if the operation will complete asynchronously, or false if it completed // synchronously var willRaiseEvent = this._socket.ReceiveAsync(socketEventArgs); if (!willRaiseEvent) { Receive(socketEventArgs); } } catch (Exception ex) { // Exceptions while shutting down are expected if (!this._isStopping) { Log.Error(ex, "Unhandled Error in Sniffer.StartReceiving"); } this._socket.Close(); this._socket = null; } }
public void WaitForProcess() { while (_gamePid < 0) { Thread.Sleep(500); if (Stopping) { throw new OperationCanceledException("Parent exited while hunting"); } } var gp = Process.GetProcessById(_gamePid); if (!Helper.IsChineseVersion(gp)) { Log.Error("Game process not Chinese version"); IpcClient.SendSignal(Signal.ClientProcessDown, new[] { _gamePid.ToString() }); IpcClient.SendSignal(Signal.MilvanethComponentExit, new[] { "GameStatusManager", ".ctor" }); throw new InvalidOperationException("Target process is not a FFXIV DX11 process"); } if (!SystemInformation.IsAdmin()) { Log.Error("Insufficient Privilege"); IpcClient.SendSignal(Signal.ClientInsuffcientPrivilege, new[] { _gamePid.ToString() }); IpcClient.SendSignal(Signal.MilvanethComponentExit, new[] { "GameStatusManager", ".ctor" }); throw new InvalidOperationException("Insufficient Privilege"); } GameProcess = gp; GameConnection = new List <Connection>(); LobbyConnection = new List <Connection>(); _connEqCp = new ConnectionEqualityComparer(); _connSetEqCp = new MultiSetComparer <Connection>(_connEqCp); _lobbyEndPoint = ConnectionPicker.GetLobbyEndPoint(GameProcess); }
static void Main(string[] args) { int counter = 0; try { SetupEnvironment(args); RegisterNotification(); #region Register _gsm = new GameStatusManager(_gamePid, _parentPid); Log.Verbose("Waiting Game Process"); _gsm.WaitForProcess(); Log.Verbose($"Found Game Process {_gsm.GameProcess.Id}"); _gnm = new GameNetworkMonitor(_gsm.GameProcess); _gmm = new GameMemoryMonitor(_gsm.GameProcess); _gdm = new GameDataManager(); _gnm.Subscribe(NetworkCharacterName.GetMessageId(), _gdm.HandleNetworkCharacterName); _gnm.Subscribe(NetworkItemInfo.GetMessageId(), _gdm.HandleNetworkItemInfo); _gnm.Subscribe(NetworkItemPriceInfo.GetMessageId(), _gdm.HandleNetworkItemPriceInfo); _gnm.Subscribe(NetworkItemInfoEnd.GetMessageId(), _gdm.HandleNetworkItemInfoEnd); _gnm.Subscribe(NetworkLogout.GetMessageId(), _gdm.HandleNetworkLogout); _gnm.Subscribe(NetworkMarketHistory.GetMessageId(), _gdm.HandleNetworkMarketHistory); _gnm.Subscribe(NetworkMarketListingCount.GetMessageId(), _gdm.HandleNetworkMarketListingCount); _gnm.Subscribe(NetworkMarketListing.GetMessageId(), _gdm.HandleNetworkMarketListing); _gnm.Subscribe(NetworkMarketResult.GetMessageId(), _gdm.HandleNetworkMarketResult); _gnm.Subscribe(NetworkRetainerHistory.GetMessageId(), _gdm.HandleNetworkRetainerHistory); _gnm.Subscribe(NetworkRetainerSummary.GetMessageId(), _gdm.HandleNetworkRetainerSummary); _gnm.Subscribe(NetworkRetainerSumEnd.GetMessageId(), _gdm.HandleNetworkRetainerSumEnd); _gnm.Subscribe(NetworkLobbyService.GetMessageId(), _gdm.HandleLobbyService); _gnm.Subscribe(NetworkLobbyCharacter.GetMessageId(), _gdm.HandleLobbyCharacter); _gnm.Subscribe(NetworkClientTrigger.GetMessageId(), _gdm.HandleClientTrigger); _gnm.Subscribe(NetworkRequestRetainer.GetMessageId(), _gdm.HandleRequestRetainer); _gnm.Subscribe(NetworkInventoryModify.GetMessageId(), _gdm.HandleInventoryModify); _gnm.Subscribe(NetworkPlayerSpawn.GetMessageId(), _gdm.HandleNetworkPlayerSpawn); _gsm.OnLobbyUpdate += LobbyUpdate; _gsm.OnWorldStart += WorldStart; _gsm.OnWorldStop += WorldStop; _gsm.OnGlobalStart += GlobalStart; _gsm.OnGlobalStop += GlobalStop; _gmm.OnChatlogUpdated += PackLogData; _gdm.OnGameLoggingOut += _gsm.SuspectNetworkStop; _gdm.OnDataReady += IpcClient.SendData; _gdm.OnRequestScan += _gmm.GetInventory; _gsm.GetPacketAmount += () => _gnm.PacketsAnalyzed; #endregion _gsm.StartMonitoringService(); Log.Warning("Main thread started"); while (!_gsm.Stopping) { counter++; if (counter > 60) { counter = 0; Log.Debug( $"SysLoad: {_gnm?.PacketsObserved:x}" + $"/{_gnm?.PacketsCaptured}" + $"/{_gnm?.PacketsAnalyzed}" + $"/{_gnm?.MessagesProcessed}" + $"/{_gnm?.MessagesDispatched}" + $"/{_gmm?.ScanCycles}" + $"/{_gmm?.LinesRead}" + $"/{IpcClient.PacketSent}"); } _gdm.ScheduledTasks(); Thread.Sleep(1000); } Log.Warning("Main thread expected exiting"); } catch (OperationCanceledException) { // Well, this IS expected literally. Log.Warning("Main thread expected exiting"); } catch (Exception e) { Log.Error(e, $"Main thread unexpected exiting"); } finally { try { _gsm.Dispose(); } catch { /* ignored */ } try { _gmm.Dispose(); } catch { /* ignored */ } try { _gnm.Dispose(); } catch { /* ignored */ } try { _gdm.Dispose(); } catch { /* ignored */ } IpcClient.SendSignal(Signal.MilvanethSubprocessExit, new[] { "Graceful Exit", "Milvaneth.Cmd", "Main Thread" }); try { IpcClient.Dispose(); } catch { /* ignored */ } Log.Fatal("Main thread exited in Program.Main"); Environment.Exit(0); } }
public static List <Connection> GetConnections(Process process) { var connections = new List <Connection>(); var tcpTable = IntPtr.Zero; var tcpTableLength = 0; if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, AddressFamily.InterNetwork, NativeMethods.TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_CONNECTIONS) == 0) { return(connections); } try { tcpTable = Marshal.AllocHGlobal(tcpTableLength); if (NativeMethods.GetExtendedTcpTable(tcpTable, ref tcpTableLength, false, AddressFamily.InterNetwork, NativeMethods.TCP_TABLE_CLASS.TCP_TABLE_OWNER_PID_CONNECTIONS) == 0) { var table = (NativeMethods.TcpTable)Marshal.PtrToStructure(tcpTable, typeof(NativeMethods.TcpTable)); var rowPtr = new IntPtr(tcpTable.ToInt64() + Marshal.SizeOf(typeof(uint))); for (var i = 0; i < table.length; i++) { var row = (NativeMethods.TcpRow)Marshal.PtrToStructure(rowPtr, typeof(NativeMethods.TcpRow)); if (row.owningPid == process.Id) { var local = new IPEndPoint(row.localAddr, (ushort)IPAddress.NetworkToHostOrder((short)row.localPort)); var remote = new IPEndPoint(row.remoteAddr, (ushort)IPAddress.NetworkToHostOrder((short)row.remotePort)); connections.Add(new Connection(local, remote)); } rowPtr = new IntPtr(rowPtr.ToInt64() + Marshal.SizeOf(typeof(NativeMethods.TcpRow))); } } else { IpcClient.SendSignal(Signal.InternalUnmanagedException, new [] { $"GetExtendedTcpTable Failed in Connection Picker: {Marshal.GetLastWin32Error()}", "Network", "ConnectionPicker", "GetConnections" }); if (connections.Count == 0) { IpcClient.SendSignal(Signal.MilvanethComponentExit, new[] { "Network", "ConnectionPicker" }); } } } catch (Exception ex) { IpcClient.SendSignal(Signal.InternalException, new [] { ex.Message, "Network", "ConnectionPicker", "GetConnections" }); if (connections.Count == 0) { IpcClient.SendSignal(Signal.MilvanethComponentExit, new[] { "Network", "ConnectionPicker" }); } } finally { if (tcpTable != IntPtr.Zero) { Marshal.FreeHGlobal(tcpTable); } } return(connections); }
private static void GlobalStart() { Log.Fatal("Logic Start"); IpcClient.SendSignal(Signal.MilvanethSubprocessReady, new[] { _gsm.GameProcess.Id.ToString() }); }
public unsafe void Output(TimestampedData timestampedData) { if (timestampedData.Protocol != 6) { return; // TCP } var mark = timestampedData.RouteMark; var tcpPacket = new TCPPacket(timestampedData.Data, timestampedData.HeaderLength); if (!tcpPacket.IsValid || (tcpPacket.Flags & TCPFlags.ACK) != TCPFlags.ACK) { if ((tcpPacket.Flags & FlagFinRst) != 0) { IpcClient.SendSignal(Signal.InternalConnectionFin, null, true); } return; } if (tcpPacket.Payload == null || tcpPacket.Payload.Length == 0) { return; } Interlocked.Increment(ref this._packetsAnalyzed); _buffer.Seek(0, SeekOrigin.End); // pos = end _buffer.Write(tcpPacket.Payload, 0, tcpPacket.Payload.Length); // pos = end var needPurge = false; var currentHeader = new byte[HeaderLength]; for (;;) { if (!needPurge) { _buffer.Seek(processedLength, SeekOrigin.Begin); // pos = 0 if (_buffer.Length - processedLength <= HeaderLength || processedLength > 65536) // not enough data { var remaining = _buffer.Length - processedLength; if (remaining < 0) { throw new AggregateException("Invalid processedLength"); } var tmp = new byte[remaining]; _buffer.Read(tmp, 0, tmp.Length); _buffer.SetLength(0); _buffer.Write(tmp, 0, tmp.Length); processedLength = 0; return; } _buffer.Read(currentHeader, 0, HeaderLength); // pos = 40 if (!IsValidHeader(currentHeader)) { needPurge = true; continue; } NetworkPacketHeader header; fixed(byte *p = ¤tHeader[0]) { header = *(NetworkPacketHeader *)p; } if (header.Malformed) { needPurge = true; continue; } var timeDelta = Math.Abs(Helper.DateTimeToUnixTimeStamp(DateTime.Now) * 1000 - header.Timestamp); if (header.Length < HeaderLength || (header.Timestamp != 0 && timeDelta > 60000)) // > 1 min { needPurge = true; continue; } if (header.Length > _buffer.Length - processedLength) { return; // wait for more content } var content = GenerateContent(_buffer, header.IsCompressed, header.Length); if (content.Length == 0) { content.Dispose(); needPurge = true; continue; } ConsumeContent(content, header, mark); processedLength += header.Length; // pos = 0 } else { needPurge = false; var bytes = _buffer.ToArray(); var newStart = processedLength + 1; var pos = FindMagic(new ArraySegment <byte>(bytes, newStart, bytes.Length - newStart)); if (pos == -1) { _buffer.SetLength(0); // no available data, drop all content processedLength = 0; return; } processedLength += pos + 1; } } }
private void NetworkStateMachine() { const int gameConnectionCount = 2; switch (_networkState) { case NetworkState.Lobby: _packetCycle = 0; _noPackCycle = 0; _requireSoftStop = false; Interlocked.Exchange(ref _softStopCycle, 0); var l_conn = ConnectionPicker.GetConnections(GameProcess); if (!l_conn.Any()) { _networkState = NetworkState.Unconnected; LobbyConnection.Clear(); Log.Information($"Network State: NoConnection"); break; } LobbyConnection = ExtractConnections(l_conn, null); OnLobbyUpdate?.Invoke(); var l_game = ExtractConnections(l_conn, LobbyConnection); if (l_game.Count == gameConnectionCount) { GameConnection = l_game; _networkState = NetworkState.GameUninitialized; Log.Information($"Network State: GameConnDetected"); } break; case NetworkState.Unconnected: _networkState = NetworkState.Fail; if (!NetworkInterface.GetIsNetworkAvailable()) { break; } if (!PingHost(ConnectionPicker.GetLobbyEndPoint(GameProcess))) { break; } _networkState = NetworkState.Pending; Log.Information($"Network State: WaitingLogin"); break; case NetworkState.Fail: Log.Information($"Network State: FailNetworkCheck"); IpcClient.SendSignal(Signal.ClientNetworkFail, new[] { GameProcess.Id.ToString() }); Stopping = true; break; case NetworkState.Pending: if (ConnectionPicker.GetConnections(GameProcess).Any()) { _networkState = NetworkState.Lobby; Log.Information($"Network State: ConnectedToLobby"); } break; case NetworkState.GameUninitialized: OnWorldStart?.Invoke(); _networkState = NetworkState.GameInitialized; Log.Information($"Network State: InitializeInvoked"); break; case NetworkState.GameInitialized: if (_requireSoftStop) { Interlocked.Increment(ref _softStopCycle); _networkState = NetworkState.GameNoPacket; Log.Information($"Network State: ConnectivityCheck"); break; } if (GetPacketAmount != null && _previousGamePacketCount != GetPacketAmount?.Invoke()) { _noPackCycle = 0; _previousGamePacketCount = GetPacketAmount.Invoke(); break; } _packetCycle++; _noPackCycle++; if (_packetCycle > 240) // 2min, Quality assurance { _networkState = NetworkState.GameNoPacket; Log.Information($"Network State: ScheduledQA"); } if (_noPackCycle > 10) // 5s, Suspected disconnected { _networkState = NetworkState.GameNoPacket; Log.Information($"Network State: NoPackNetworkCheck"); } break; case NetworkState.GameNoPacket: var gnp_conn = ConnectionPicker.GetConnections(GameProcess); if (!_connSetEqCp.Equals(gnp_conn, GameConnection)) { OnWorldStop?.Invoke(); _networkState = NetworkState.Lobby; Log.Information($"Network State: ConnectionLost"); break; } if (_softStopCycle > 20) // 10s, Kept monitoring { _requireSoftStop = false; Interlocked.Exchange(ref _softStopCycle, 0); } _packetCycle = 0; _noPackCycle = 0; _networkState = NetworkState.GameInitialized; Log.Information($"Network State: Normal"); break; default: _networkState = NetworkState.Lobby; Log.Information($"Network State: UnrecognizedToLobby"); break; } }