public static void Send <T>(IEnumerable <NetworkConnection> connections, T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message into byte[] once MessagePacker.Pack(msg, writer); var segment = writer.ToArraySegment(); int count = 0; foreach (NetworkConnection conn in connections) { // send to all connections, but don't wait for them _ = conn.SendAsync(segment); count++; } NetworkDiagnostics.OnSend(msg, channelId, segment.Count, count); } }
public static bool SendToAll(int msgType, MessageBase msg, int channelId = Channels.DefaultReliable) { if (LogFilter.Debug) { Debug.Log("Server.SendToAll id:" + msgType); } // pack message into byte[] once byte[] bytes = MessagePacker.PackMessage((ushort)msgType, msg); // send to all bool result = true; foreach (KeyValuePair <int, NetworkConnection> kvp in connections) { result &= kvp.Value.SendBytes(bytes, channelId); } return(result); }
// note: original HLAPI HandleBytes function handled >1 message in a while loop, but this wasn't necessary // anymore because NetworkServer/NetworkClient Update both use while loops to handle >1 data events per // frame already. // -> in other words, we always receive 1 message per Receive call, never two. // -> can be tested easily with a 1000ms send delay and then logging amount received in while loops here // and in NetworkServer/Client Update. HandleBytes already takes exactly one. /// <summary> /// This function allows custom network connection classes to process data from the network before it is passed to the application. /// </summary> /// <param name="buffer">The data received.</param> internal void TransportReceive(ArraySegment <byte> buffer, int channelId) { // unpack message using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(buffer)) { try { int msgType = MessagePacker.UnpackId(networkReader); // try to invoke the handler for that message InvokeHandler(msgType, networkReader, channelId); } catch (Exception ex) { Debug.LogError("Closed connection: " + this + ". Invalid message " + ex); Disconnect(); } } }
internal void InvokeHandler(int msgType, NetworkReader reader, int channelId) { if (messageHandlers.TryGetValue(msgType, out NetworkMessageDelegate msgDelegate)) { msgDelegate(this, reader, channelId); } else { try { Type type = MessagePacker.GetMessageType(msgType); throw new InvalidDataException($"Unexpected message {type} received in {this}. Did you register a handler for it?"); } catch (KeyNotFoundException) { throw new InvalidDataException($"Unexpected message ID {msgType} received in {this}. May be due to no existing RegisterHandler for this message."); } } }
/* TODO use or remove static void GenerateDataError(NetworkConnection conn, byte error) { NetworkError dataError = (NetworkError)error; Debug.LogError("Mirror Server Data Error: " + dataError); GenerateError(conn, error); } static void GenerateDisconnectError(NetworkConnection conn, byte error) { NetworkError disconnectError = (NetworkError)error; Debug.LogError("Mirror Server Disconnect Error: " + disconnectError + " conn:[" + conn + "]:" + conn.connectionId); GenerateError(conn, error); } */ static void GenerateError(NetworkConnection conn, byte error) { int msgId = MessagePacker.GetId<ErrorMessage>(); if (handlers.ContainsKey(msgId)) { ErrorMessage msg = new ErrorMessage { value = error }; // write the message to a local buffer NetworkWriter writer = new NetworkWriter(); msg.Serialize(writer); // pass a reader (attached to local buffer) to handler NetworkReader reader = new NetworkReader(writer.ToArray()); conn.InvokeHandler(msgId, reader); } }
static bool SendToObservers(NetworkIdentity identity, short msgType, MessageBase msg) { if (LogFilter.Debug) Debug.Log("Server.SendToObservers id:" + msgType); if (identity != null && identity.observers != null) { // pack message into byte[] once byte[] bytes = MessagePacker.PackMessage((ushort)msgType, msg); // send to all observers bool result = true; foreach (KeyValuePair<int, NetworkConnection> kvp in identity.observers) { result &= kvp.Value.SendBytes(bytes); } return result; } return false; }
/// <summary> /// This function invokes the registered handler function for a message. /// <para>Network connections used by the NetworkClient and NetworkServer use this function for handling network messages.</para> /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message object to process.</param> /// <returns></returns> public bool InvokeHandler <T>(T msg, int channelId) where T : IMessageBase { // get writer from pool NetworkWriter writer = NetworkWriterPool.GetWriter(); // pack and invoke int msgType = MessagePacker.GetId(msg.GetType()); MessagePacker.Pack(msg, writer); ArraySegment <byte> segment = writer.ToArraySegment(); NetworkReader reader = NetworkReaderPool.GetReader(segment); bool result = InvokeHandler(msgType, reader, channelId); NetworkReaderPool.Recycle(reader); // recycle writer and return NetworkWriterPool.Recycle(writer); return(result); }
/// <summary> /// This function invokes the registered handler function for a message. /// <para>Network connections used by the NetworkClient and NetworkServer use this function for handling network messages.</para> /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message object to process.</param> /// <returns></returns> public bool InvokeHandler <T>(T msg, int channelId) where T : IMessageBase { // get writer from pool NetworkWriter writer = NetworkWriterPool.GetWriter(); // if it is a value type, just use typeof(T) to avoid boxing // this works because value types cannot be derived // if it is a reference type (for example IMessageBase), // ask the message for the real type int msgType = MessagePacker.GetId(typeof(T).IsValueType ? typeof(T) : msg.GetType()); MessagePacker.Pack(msg, writer); ArraySegment <byte> segment = writer.ToArraySegment(); bool result = InvokeHandler(msgType, new NetworkReader(segment), channelId); // recycle writer and return NetworkWriterPool.Recycle(writer); return(result); }
public static bool SendToAll <T>(T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { if (LogFilter.Debug) { Debug.Log("Server.SendToAll id:" + typeof(T)); } // pack message into byte[] once NetworkWriter bytes = MessagePacker.PackWriter(msg); bytes.recycleCount = (ushort)connections.Count; bool result = true; foreach (KeyValuePair <int, NetworkConnection> kvp in connections) { result &= kvp.Value.SendWriter(bytes, channelId); } return(result); }
// this is like SendToReady - but it doesn't check the ready flag on the connection. // this is used for ObjectDestroy messages. static bool SendToObservers <T>(NetworkIdentity identity, T msg) where T : IMessageBase { if (LogFilter.Debug) { Debug.Log("Server.SendToObservers id:" + typeof(T)); } if (identity != null && identity.observers != null) { // pack message into byte[] once byte[] bytes = MessagePacker.Pack(msg); bool result = true; foreach (KeyValuePair <int, NetworkConnection> kvp in identity.observers) { result &= kvp.Value.SendBytes(bytes); } return(result); } return(false); }
public static bool SendToReady<T>(NetworkIdentity identity,T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { if (LogFilter.Debug) Debug.Log("Server.SendToReady msgType:" + typeof(T)); if (identity != null && identity.observers != null) { // pack message into byte[] once byte[] bytes = MessagePacker.Pack(msg); bool result = true; foreach (KeyValuePair<int, NetworkConnection> kvp in identity.observers) { if (kvp.Value.isReady) { result &= kvp.Value.SendBytes(bytes, channelId); } } return result; } return false; }
public static bool SendToReady(NetworkIdentity identity, short msgType, MessageBase msg, int channelId = Channels.DefaultReliable) { if (LogFilter.Debug) Debug.Log("Server.SendToReady msgType:" + msgType); if (identity != null && identity.observers != null) { // pack message into byte[] once byte[] bytes = MessagePacker.PackMessage((ushort)msgType, msg); // send to all ready observers bool result = true; foreach (KeyValuePair<int, NetworkConnection> kvp in identity.observers) { if (kvp.Value.isReady) { result &= kvp.Value.SendBytes(bytes, channelId); } } return result; } return false; }
// handle this message // note: original HLAPI HandleBytes function handled >1 message in a while loop, but this wasn't necessary // anymore because NetworkServer/NetworkClient.Update both use while loops to handle >1 data events per // frame already. // -> in other words, we always receive 1 message per Receive call, never two. // -> can be tested easily with a 1000ms send delay and then logging amount received in while loops here // and in NetworkServer/Client Update. HandleBytes already takes exactly one. public virtual void TransportReceive(byte[] buffer) { // unpack message NetworkReader reader = new NetworkReader(buffer); if (!MessagePacker.UnpackMessage(reader, out int msgType)) { Debug.LogError("Closed connection: " + connectionId + ". Invalid message header."); Disconnect(); } if (logNetworkMessages) { Debug.Log("ConnectionRecv con:" + connectionId + " msgType:" + msgType + " content:" + BitConverter.ToString(buffer)); } // try to invoke the handler for that message if (InvokeHandler(msgType, reader)) { lastMessageTime = Time.time; } }
// connect host mode internal static void ConnectLocalServer() { if (LogFilter.Debug) { Debug.Log("Client Connect Local Server"); } RegisterSystemHandlers(true); connectState = ConnectState.Connected; // create local connection to server connection = new ULocalConnectionToServer(); connection.SetHandlers(handlers); // create server connection to local client ULocalConnectionToClient connectionToClient = new ULocalConnectionToClient(); NetworkServer.SetLocalConnection(connectionToClient); localClientPacketQueue.Enqueue(new BufferHolder(MessagePacker.PackWriter(new ConnectMessage()))); }
public virtual void TransportReceive(ArraySegment <byte> buffer) { // unpack message NetworkReader reader = new NetworkReader(buffer); if (MessagePacker.UnpackMessage(reader, out int msgType)) { if (logNetworkMessages) { Debug.Log("ConnectionRecv con:" + connectionId + " msgType:" + msgType + " content:" + BitConverter.ToString(buffer.Array, buffer.Offset, buffer.Count)); } // try to invoke the handler for that message if (InvokeHandler(msgType, reader)) { lastMessageTime = Time.time; } } else { Debug.LogError("HandleBytes UnpackMessage failed for: " + BitConverter.ToString(buffer.Array, buffer.Offset, buffer.Count)); } }
// helper function protected bool UnpackAndInvoke(NetworkReader reader, int channelId) { if (MessagePacker.Unpack(reader, out int msgType)) { // try to invoke the handler for that message if (messageHandlers.TryGetValue(msgType, out NetworkMessageDelegate msgDelegate)) { msgDelegate.Invoke(this, reader, channelId); lastMessageTime = Time.time; return(true); } else { // Debug.Log("Unknown message ID " + msgType + " " + this + ". May be due to no existing RegisterHandler for this message."); return(false); } } else { Debug.LogError("Closed connection: " + this + ". Invalid message header."); Disconnect(); return(false); } }
/// <summary> /// Replaces a handler for a particular message type. /// <para>See also <see cref="RegisterHandler{T}(Action{NetworkConnection, T}, bool)">RegisterHandler(T)(Action(NetworkConnection, T), bool)</see></para> /// </summary> /// <typeparam name="T">Message type</typeparam> /// <param name="handler">Function handler which will be invoked when this message type is received.</param> /// <param name="requireAuthentication">True if the message requires an authenticated connection</param> public static void ReplaceHandler <T>(Action <NetworkConnection, T> handler, bool requireAuthentication = true) where T : NetworkMessage { int msgType = MessagePacker.GetId <T>(); handlers[msgType] = MessagePacker.MessageHandler(handler, requireAuthentication); }
public static void UnregisterHandler <T>() where T : IMessageBase { int msgType = MessagePacker.GetId <T>(); handlers.Remove(msgType); }
public virtual bool Send(int msgType, MessageBase msg, int channelId = Channels.DefaultReliable) { byte[] message = MessagePacker.PackMessage(msgType, msg); return(SendBytes(new ArraySegment <byte>(message), channelId)); }
public bool Send(int msgType, MessageBase msg, int channelId = Channels.DefaultReliable) { // pack message and send byte[] message = MessagePacker.PackMessage(msgType, msg); return(Send(new ArraySegment <byte>(message), channelId)); }
public virtual bool Send <T>(T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { // pack message and send return(SendWriter(MessagePacker.PackWriter(msg), channelId)); }
public virtual bool Send(int msgType, MessageBase msg, int channelId = Channels.DefaultReliable) { // pack message and send byte[] message = MessagePacker.PackMessage(msgType, msg); return(SendBytes(message, channelId)); }
/// <summary> /// This sends a network message with a message ID on the connection. This message is sent on channel zero, which by default is the reliable channel. /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message to send.</param> /// <param name="channelId">The transport layer channel to send on.</param> /// <returns></returns> public virtual bool Send <T>(T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { // pack message and send byte[] message = MessagePacker.Pack(msg); return(SendBytes(message, channelId)); }
/// <summary> /// Unregisters a handler for a particular message type. /// </summary> /// <typeparam name="T">Message type</typeparam> public void UnregisterHandler <T>() { int msgType = MessagePacker.GetId <T>(); messageHandlers.Remove(msgType); }