public bool ProcessServerMessage() { Telepathy.Message message; if (booster.GetNextMessage(out message)) { switch (message.eventType) { case Telepathy.EventType.Connected: Debug.Log("Booster connected."); break; case Telepathy.EventType.Disconnected: Debug.Log("Booster disconnected."); ServerStop(); break; case Telepathy.EventType.Data: //Debug.Log("Booster data."); ProcessErlangenMessage(message.data); break; default: // TODO handle errors from Telepathy when telepathy can report errors OnServerDisconnected.Invoke(message.connectionId); break; } return(true); } return(false); }
public override void ServerStart() { // create server server = new Telepathy.Server(serverMaxMessageSize); // server hooks // other systems hook into transport events in OnCreate or // OnStartRunning in no particular order. the only way to avoid // race conditions where telepathy uses OnConnected before another // system's hook (e.g. statistics OnData) was added is to wrap // them all in a lambda and always call the latest hook. // (= lazy call) server.OnConnected = (connectionId) => OnServerConnected.Invoke(connectionId); server.OnData = (connectionId, segment) => OnServerDataReceived.Invoke(connectionId, segment, Channels.Reliable); server.OnDisconnected = (connectionId) => OnServerDisconnected.Invoke(connectionId); // server configuration server.NoDelay = NoDelay; server.SendTimeout = SendTimeout; server.ReceiveTimeout = ReceiveTimeout; server.SendQueueLimit = serverSendQueueLimitPerConnection; server.ReceiveQueueLimit = serverReceiveQueueLimitPerConnection; server.Start(port); }
// Server processing loop. private bool ProcessServerMessages() { // Get to the queue! Check those corners! while (MirrorServerIncomingQueue.TryDequeue(out IncomingPacket pkt)) { switch (pkt.type) { case MirrorPacketType.ServerClientConnected: OnServerConnected?.Invoke(pkt.connectionId); break; case MirrorPacketType.ServerClientDisconnected: OnServerDisconnected?.Invoke(pkt.connectionId); break; case MirrorPacketType.ServerClientSentData: OnServerDataReceived?.Invoke(pkt.connectionId, new ArraySegment <byte>(pkt.data), pkt.channelId); break; default: // Nothing to see here. break; } } // Flashbang though the window and race to the finish. return(true); }
public WebsocketTransport() { // dispatch the events from the server server.Connected += (connectionId) => OnServerConnected.Invoke(connectionId); server.Disconnected += (connectionId) => OnServerDisconnected.Invoke(connectionId); server.ReceivedData += (connectionId, data) => OnServerDataReceived.Invoke(connectionId, data); server.ReceivedError += (connectionId, error) => OnServerError.Invoke(connectionId, error); // dispatch events from the client client.Connected += () => OnClientConnected.Invoke(); client.Disconnected += () => OnClientDisconnected.Invoke(); client.ReceivedData += (data) => OnClientDataReceived.Invoke(data); client.ReceivedError += (error) => OnClientError.Invoke(error); // configure client.NoDelay = NoDelay; server.NoDelay = NoDelay; // HLAPI's local connection uses hard coded connectionId '0', so we // need to make sure that external connections always start at '1' // by simple eating the first one before the server starts Server.NextConnectionId(); Debug.Log("Websocket transport initialized!"); }
private void RegisterMirrorEvents() { // Server _svConnected = (id) => OnServerConnected?.Invoke(id); _svDisconnected = (id) => OnServerDisconnected?.Invoke(id); _svDataReceived = (id, data) => OnServerDataReceived?.Invoke(id, new ArraySegment <byte>(data)); _svError = (id, exception) => OnServerError?.Invoke(id, exception); Server.OnConnected += _svConnected; Server.OnDisconnected += _svDisconnected; Server.OnDataReceived += _svDataReceived; Server.OnError += _svError; // Client _clConnected = () => OnClientConnected?.Invoke(); _clDisconnected = () => OnClientDisconnected?.Invoke(); _clDataReceived = (data) => OnClientDataReceived?.Invoke(new ArraySegment <byte>(data)); _clError = (exception) => OnClientError?.Invoke(exception); Client.OnConnected += _clConnected; Client.OnDisconnected += _clDisconnected; Client.OnDataReceived += _clDataReceived; Client.OnError += _clError; _eventsRegistered = true; }
// Server processing loop. private bool ProcessServerMessages() { // Get to the queue! Check those corners! while (MirrorIncomingQueue.TryDequeue(out IncomingPacket pkt)) { // print($"Firing a packet with type: {pkt.type} for connection ID: {pkt.connectionId}\n{(pkt.data == null ? "NO DATA" : BitConverter.ToString(pkt.data))}"); switch (pkt.type) { case MirrorPacketType.ServerClientConnected: OnServerConnected?.Invoke(pkt.connectionId); break; case MirrorPacketType.ServerClientDisconnected: OnServerDisconnected?.Invoke(pkt.connectionId); break; case MirrorPacketType.ServerClientSentData: OnServerDataReceived?.Invoke(pkt.connectionId, new ArraySegment <byte>(pkt.data)); break; default: // Nothing to see here. break; } } // Flashbang though the window and race to the finish. return(true); }
public bool ProcessServerMessage() { if (server.GetNextMessage(out Telepathy.Message message)) { switch (message.eventType) { case Telepathy.EventType.Connected: OnServerConnected.Invoke(message.connectionId); break; case Telepathy.EventType.Data: OnServerDataReceived.Invoke(message.connectionId, new ArraySegment <byte>(message.data), Channels.DefaultReliable); break; case Telepathy.EventType.Disconnected: OnServerDisconnected.Invoke(message.connectionId); break; default: // TODO handle errors from Telepathy when telepathy can report errors OnServerDisconnected.Invoke(message.connectionId); break; } return(true); } return(false); }
public void ProcessServerMessages() { if (server.Active) { server.GetNextMessages(queue); while (queue.Count > 0) { Apathy.Message message = queue.Dequeue(); switch (message.eventType) { case Apathy.EventType.Connected: OnServerConnected.Invoke(message.connectionId); break; // breaks switch, not while case Apathy.EventType.Data: OnServerDataReceived.Invoke(message.connectionId, message.data, Channels.DefaultReliable); break; // breaks switch, not while case Apathy.EventType.Disconnected: OnServerDisconnected.Invoke(message.connectionId); break; // breaks switch, not while } } } }
public bool ProcessServerMessage() { Telepathy.Message message; if (server.GetNextMessage(out message)) { switch (message.eventType) { // convert Telepathy EventType to TransportEvent case Telepathy.EventType.Connected: OnServerConnected.Invoke(message.connectionId); break; case Telepathy.EventType.Data: OnServerDataReceived.Invoke(message.connectionId, message.data); break; case Telepathy.EventType.Disconnected: OnServerDisconnected.Invoke(message.connectionId); break; default: // TODO handle errors from Telepathy when telepathy can report errors OnServerDisconnected.Invoke(message.connectionId); break; } return(true); } return(false); }
public override void ServerStart() { m_Server?.Dispose(); ServerConfig config; if (!string.IsNullOrWhiteSpace(m_RsaXmlPath)) { var xml = Resources.Load <TextAsset>(m_RsaXmlPath); config = ServerConfig.FromXML(xml.text, m_Port); } else { config = ServerConfig.Create(m_Port); } m_Server = Connection.StartServer(config); m_Server.OnPeerEvent += (e) => { switch (e.EventType) { case PeerEvent.Type.Add: OnServerConnected?.Invoke(Mathf.Abs(e.Peer.ConnectionId)); break; case PeerEvent.Type.Remove: OnServerDisconnected?.Invoke(Mathf.Abs(e.Peer.ConnectionId)); break; } }; }
void Awake() { // logging if (debugLog) { Log.Info = Debug.Log; } Log.Warning = Debug.LogWarning; Log.Error = Debug.LogError; // TODO simplify after converting Mirror Transport events to Action client = new KcpClient( () => OnClientConnected.Invoke(), (message) => OnClientDataReceived.Invoke(message, Channels.DefaultReliable), () => OnClientDisconnected.Invoke() ); // TODO simplify after converting Mirror Transport events to Action server = new KcpServer( (connectionId) => OnServerConnected.Invoke(connectionId), (connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.DefaultReliable), (connectionId) => OnServerDisconnected.Invoke(connectionId), NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize ); Debug.Log("KcpTransport initialized!"); }
void AddServerCallbacks() { // wire all the base transports to my events for (int i = 0; i < transports.Length; i++) { // this is required for the handlers, if I use i directly // then all the handlers will use the last i int locali = i; Transport transport = transports[i]; transport.OnServerConnected = (baseConnectionId => { OnServerConnected.Invoke(FromBaseId(locali, baseConnectionId)); }); transport.OnServerDataReceived = (baseConnectionId, data, channel) => { OnServerDataReceived.Invoke(FromBaseId(locali, baseConnectionId), data, channel); }; transport.OnServerError = (baseConnectionId, error) => { OnServerError.Invoke(FromBaseId(locali, baseConnectionId), error); }; transport.OnServerDisconnected = baseConnectionId => { OnServerDisconnected.Invoke(FromBaseId(locali, baseConnectionId)); }; } }
// messages should always be processed in early update public override void ServerEarlyUpdate() { while (serverIncoming.Count > 0) { Message message = serverIncoming.Dequeue(); switch (message.eventType) { case EventType.Connected: Debug.Log("MemoryTransport Server Message: Connected"); // event might be null in tests if no NetworkClient is used. OnServerConnected?.Invoke(message.connectionId); break; case EventType.Data: Debug.Log($"MemoryTransport Server Message: Data: {BitConverter.ToString(message.data)}"); // event might be null in tests if no NetworkClient is used. OnServerDataReceived?.Invoke(message.connectionId, new ArraySegment <byte>(message.data), 0); break; case EventType.Disconnected: Debug.Log("MemoryTransport Server Message: Disconnected"); // event might be null in tests if no NetworkClient is used. OnServerDisconnected?.Invoke(message.connectionId); break; } } }
void InitServer() { recipientsCache = new List <int> [transports.Length]; // wire all the base transports to my events for (int i = 0; i < transports.Length; i++) { recipientsCache[i] = new List <int>(); // this is required for the handlers, if I use i directly // then all the handlers will use the last i int locali = i; Transport transport = transports[i]; transport.OnServerConnected.AddListener(baseConnectionId => { OnServerConnected.Invoke(FromBaseId(locali, baseConnectionId)); }); transport.OnServerDataReceived.AddListener((baseConnectionId, data, channel) => { OnServerDataReceived.Invoke(FromBaseId(locali, baseConnectionId), data, channel); }); transport.OnServerError.AddListener((baseConnectionId, error) => { OnServerError.Invoke(FromBaseId(locali, baseConnectionId), error); }); transport.OnServerDisconnected.AddListener(baseConnectionId => { OnServerDisconnected.Invoke(FromBaseId(locali, baseConnectionId)); }); } }
// Server processing loop. private bool ProcessServerMessages() { // Get to the queue! Check those corners! while (MirrorServerIncomingQueue.TryDequeue(out IncomingPacket pkt)) { switch (pkt.type) { case MirrorPacketType.ServerClientConnected: OnServerConnected?.Invoke(pkt.connectionId); break; case MirrorPacketType.ServerClientDisconnected: OnServerDisconnected?.Invoke(pkt.connectionId); break; case MirrorPacketType.ServerClientSentData: OnServerDataReceived?.Invoke(pkt.connectionId, new ArraySegment <byte>(pkt.data), pkt.channelId); break; default: // Nothing to see here. break; } // Some messages can disable the transport // If the transport was disabled by any of the messages, we have to break out of the loop and wait until we've been re-enabled. if (!enabled) { break; } } // Flashbang though the window and race to the finish. return(true); }
void ProcessErlangenMessage(byte[] message) { // check first byte: 1 = connected, 2 = disconnected, 3 = data if (message.Length == 5 && message[0] == 0x01) { // extract connectionId int connectionId = BytesToIntBigEndian(message, 1); OnServerConnected.Invoke(connectionId); //Debug.Log("Booster: client connected. connId=" + connectionId); } else if (message.Length == 5 && message[0] == 0x02) { // extract connectionId int connectionId = BytesToIntBigEndian(message, 1); OnServerDisconnected.Invoke(connectionId); //Debug.Log("Booster: client disconnected. connId=" + connectionId); } else if (message.Length >= 5 && message[0] == 0x03) { // extract connectionId int connectionId = BytesToIntBigEndian(message, 1); // create data segment int dataOffset = 1 + 4; // type=1, connectionid=4 int dataLength = message.Length - dataOffset; ArraySegment <byte> segment = new ArraySegment <byte>(message, dataOffset, dataLength); OnServerDataReceived.Invoke(connectionId, segment, Channels.DefaultReliable); //Debug.Log("Booster: client data. connId=" + connectionId + " data=" + BitConverter.ToString(segment, segment.Offset, segment.Count)); } else { Debug.LogWarning("Booster: parse failed: " + BitConverter.ToString(message)); } }
public bool ProcessServerMessage() { if (serverHostId == -1) { return(false); } int connectionId = -1; int channel; int receivedSize; NetworkEventType networkEvent = NetworkTransport.ReceiveFromHost(serverHostId, out connectionId, out channel, serverReceiveBuffer, serverReceiveBuffer.Length, out receivedSize, out error); // note: 'error' is used for extra information, e.g. the reason for // a disconnect. we don't necessarily have to throw an error if // error != 0. but let's log it for easier debugging. // // DO NOT return after error != 0. otherwise Disconnect won't be // registered. NetworkError networkError = (NetworkError)error; if (networkError != NetworkError.Ok) { string message = "NetworkTransport.Receive failed: hostid=" + serverHostId + " connId=" + connectionId + " channelId=" + channel + " error=" + networkError; // TODO write a TransportException or better OnServerError.Invoke(connectionId, new Exception(message)); } // LLAPI client sends keep alive messages (75-6C-6C) on channel=110. // ignore all messages that aren't for our selected channel. /*if (channel != channelId) * { * return false; * }*/ switch (networkEvent) { case NetworkEventType.ConnectEvent: OnServerConnected.Invoke(connectionId); break; case NetworkEventType.DataEvent: byte[] data = new byte[receivedSize]; Array.Copy(serverReceiveBuffer, data, receivedSize); OnServerDataReceived.Invoke(connectionId, data); break; case NetworkEventType.DisconnectEvent: OnServerDisconnected.Invoke(connectionId); break; default: // nothing or a message we don't recognize return(false); } return(true); }
public override void OnDisconnected(DisconnectCause cause) { Debug.Log("Disconnected from a Photon server."); _needsToReconnect = true; OnServerDisconnected?.Invoke(); }
async private void DelayDisconnect() { await Task.Delay(100); _isConnected = false; OnServerDisconnected?.Invoke(); }
public override void Awake() { KCPConfig conf = new KCPConfig(); if (!File.Exists("KCPConfig.json")) { File.WriteAllText("KCPConfig.json", JsonConvert.SerializeObject(conf, Formatting.Indented)); } else { conf = JsonConvert.DeserializeObject <KCPConfig>(File.ReadAllText("KCPConfig.json")); } NoDelay = conf.NoDelay; Interval = conf.Interval; FastResend = conf.FastResend; CongestionWindow = conf.CongestionWindow; SendWindowSize = conf.SendWindowSize; ReceiveWindowSize = conf.ReceiveWindowSize; ConnectionTimeout = conf.ConnectionTimeout; // logging // Log.Info should use Debug.Log if enabled, or nothing otherwise // (don't want to spam the console on headless servers) if (debugLog) { Log.Info = Console.WriteLine; } else { Log.Info = _ => { } }; Log.Warning = Console.WriteLine; Log.Error = Console.WriteLine; // client client = new KcpClient( () => OnClientConnected.Invoke(), (message) => OnClientDataReceived.Invoke(message, 0), () => OnClientDisconnected.Invoke() ); // server server = new KcpServer( (connectionId) => OnServerConnected.Invoke(connectionId), (connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, 0), (connectionId) => OnServerDisconnected.Invoke(connectionId), NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize ); Console.WriteLine("KcpTransport initialized!"); }
void OnServerDisconnect(int conn) { if (_serverSessions.ContainsKey(conn)) { _serverSessions.Remove(conn); } OnServerDisconnected?.Invoke(conn); }
public void DirectRemoveClient(int clientID) { if (!isServer) { return; } OnServerDisconnected?.Invoke(connectedDirectClients.GetByFirst(clientID)); connectedDirectClients.Remove(clientID); }
void dataRead(IAsyncResult result) { byte[] buffer = result.AsyncState as byte[]; string data = ASCIIEncoding.ASCII.GetString(buffer, 0, buffer.Length); TransmitedDataType datatype = TransmitedDataType.Unknown; data = data.TrimEnd(); if (data.Length > 4 && data.StartsWith("C")) { datatype = data.StartsWith("C101") ? TransmitedDataType.Message : data.StartsWith("C102") ? TransmitedDataType.Status : data.StartsWith("C103") ? TransmitedDataType.Command : data.StartsWith("C104") ? TransmitedDataType.Headers : data.StartsWith("C105") ? TransmitedDataType.RawData : TransmitedDataType.Unknown; if (datatype == TransmitedDataType.Status) { var stateID = Enum.Parse(typeof(NetworkStates), data.Substring(4)); if (stateID is NetworkStates) { ServerState = (NetworkStates)stateID; if (ServerState == NetworkStates.Disconnected) { StopReceiving(); State = NetworkStates.Disconnected; ServerState = NetworkStates.Disconnected; connected = false; client.Close(); if (OnServerDisconnected != null) { OnServerDisconnected.Invoke(); } } // return; } } if (datatype != TransmitedDataType.Unknown) { data = data.Substring(4); } } latestReceivedData = data; if (OnDataReceived != null) { OnDataReceived.Invoke(data, datatype); } if (!stopReceiver) { buffer = new byte[BufferSize]; connectionStream.BeginRead(buffer, 0, BufferSize, dataRead, buffer); } }
public override void OnAwake() { base.OnAwake(); // create client & server client = new Telepathy.Client(clientMaxMessageSize); server = new Telepathy.Server(serverMaxMessageSize); // tell Telepathy to use Unity's Debug.Log Telepathy.Log.Info = Debug.Log; Telepathy.Log.Warning = Debug.LogWarning; Telepathy.Log.Error = Debug.LogError; // client hooks // other systems hook into transport events in OnCreate or // OnStartRunning in no particular order. the only way to avoid // race conditions where telepathy uses OnConnected before another // system's hook (e.g. statistics OnData) was added is to wrap // them all in a lambda and always call the latest hook. // (= lazy call) client.OnConnected = () => OnClientConnected.Invoke(); client.OnData = (segment) => OnClientDataReceived.Invoke(segment, Channels.DefaultReliable); client.OnDisconnected = () => OnClientDisconnected.Invoke(); // client configuration client.NoDelay = NoDelay; client.SendTimeout = SendTimeout; client.ReceiveTimeout = ReceiveTimeout; client.SendQueueLimit = clientSendQueueLimit; client.ReceiveQueueLimit = clientReceiveQueueLimit; // server hooks // other systems hook into transport events in OnCreate or // OnStartRunning in no particular order. the only way to avoid // race conditions where telepathy uses OnConnected before another // system's hook (e.g. statistics OnData) was added is to wrap // them all in a lambda and always call the latest hook. // (= lazy call) server.OnConnected = (connectionId) => OnServerConnected.Invoke(connectionId); server.OnData = (connectionId, segment) => OnServerDataReceived.Invoke(connectionId, segment, Channels.DefaultReliable); server.OnDisconnected = (connectionId) => OnServerDisconnected.Invoke(connectionId); // server configuration server.NoDelay = NoDelay; server.SendTimeout = SendTimeout; server.ReceiveTimeout = ReceiveTimeout; server.SendQueueLimit = serverSendQueueLimitPerConnection; server.ReceiveQueueLimit = serverReceiveQueueLimitPerConnection; // allocate enabled check only once enabledCheck = () => Enabled; Debug.Log("TelepathyTransport initialized!"); }
void OnLibuvServerClosed(TcpStream handle) { Debug.Log($"libuv sv: closed client {handle}"); // important: remove the connection BEFORE calling the DOTSNET event // otherwise DOTSNET OnDisconnected Unspawn might try to send to a // disposed connection which we didn't remove yet. do it first. connections.Remove((int)handle.UserToken); handle.Dispose(); // Mirror event OnServerDisconnected.Invoke((int)handle.UserToken); }
public override void OnAwake() { base.OnAwake(); // logging // Log.Info should use Debug.Log if enabled, or nothing otherwise // (don't want to spam the console on headless servers) if (debugLog) { Log.Info = Debug.Log; } else { Log.Info = _ => {} }; Log.Warning = Debug.LogWarning; Log.Error = Debug.LogError; // client client = new KcpClient( () => OnClientConnected.Invoke(), (message) => OnClientDataReceived.Invoke(message, Channels.DefaultReliable), () => OnClientDisconnected.Invoke() ); // server server = new KcpServer( (connectionId) => OnServerConnected.Invoke(connectionId), (connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.DefaultReliable), (connectionId) => OnServerDisconnected.Invoke(connectionId), NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize ); if (statisticsLog) { Task.Run(async() => { await Task.Delay(100); OnLogStatistics(); await Task.Delay(100); }); //InvokeRepeating(nameof(OnLogStatistics), 1, 1); } Debug.Log("KcpTransport initialized!"); }
private void LobbyManager_OnMemberDisconnect(long lobbyId, long userId) { if (ServerActive()) { OnServerDisconnected?.Invoke(clients.GetByFirst(userId)); clients.Remove(userId); } if (currentLobby.OwnerId == userId) { ClientDisconnect(); OnClientDisconnected?.Invoke(); } }
public static void Init(bool isServer = false, bool isClient = false, bool isSimulated = false) { IsServer = isServer ? true : IsServer; IsClient = isClient ? true : IsClient; IsSimulated = isSimulated ? true : IsSimulated; if (isSimulated) { if (NetLogFilter.logInfo) { Debug.Log("Transport Layer Initialized: Simulation"); } return; } if (IsServer && server == null) { // server server = new KcpServer( (connectionId) => OnServerConnected.Invoke(connectionId), (connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, (int)UDPChannels.Reliable), (connectionId) => OnServerDisconnected.Invoke(connectionId), NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize ); if (NetLogFilter.logInfo) { Debug.Log("Transport Layer Initialized: Server"); } } if (IsClient && client == null) { // client client = new KcpClient( () => OnClientConnected.Invoke(), (message) => OnClientDataReceived.Invoke(message, (int)UDPChannels.Reliable), () => OnClientDisconnected.Invoke() ); if (NetLogFilter.logInfo) { Debug.Log("Transport Layer Initialized: Client"); } } }
public SteamworksTransport() { _client = new P2PClient(); _server = new P2PServer(); _client.OnConnected.AddListener(() => OnClientConnected?.Invoke()); _client.OnData.AddListener(bytes => { OnClientDataReceived?.Invoke(bytes); }); _client.OnError.AddListener(exception => { OnClientError?.Invoke(exception); }); _client.OnDisconnect.AddListener(() => { OnClientDisconnected?.Invoke(); }); _server.OnConnect.AddListener(id => { OnServerConnected?.Invoke(id); }); _server.OnData.AddListener((id, data) => { OnServerDataReceived?.Invoke(id, data); }); _server.OnError.AddListener((id, exception) => { OnServerError?.Invoke(id, exception); }); _server.OnDisconnect.AddListener(id => { OnServerDisconnected?.Invoke(id); }); }
public bool ProcessServerMessage() { if (server.GetNextMessage(out Telepathy.Message message)) { switch (message.eventType) { case Telepathy.EventType.PreConnect: OnServerPreConnect?.Invoke(message.connectionId); break; case Telepathy.EventType.Connected: OnServerConnected.Invoke(message.connectionId); break; case Telepathy.EventType.Data: #if UNITY_EDITOR if (clientSimulatedDelay > 0) { StartCoroutine(_DelayDispatchServerData(clientSimulatedDelay, message)); } else #endif OnServerDataReceived.Invoke(message.connectionId, new ArraySegment <byte>(message.data), Channels.DefaultReliable); break; case Telepathy.EventType.Disconnected: // Wappen: without modifying OnServerDisconnected signature // I will tug in error to separate field if (message.data != null) { string reason; Common.WappenDeserializeDisconnectMessage(message.data, out reason); bool isAttacker = reason != null && reason.StartsWith("ATK"); ServerWriteDisconnectReason(message.connectionId, reason, isAttacker); } OnServerDisconnected.Invoke(message.connectionId); break; default: // TODO handle errors from Telepathy when telepathy can report errors OnServerDisconnected.Invoke(message.connectionId); break; } return(true); } return(false); }