diff --git a/Mirror/Runtime/CustomAttributes.cs b/Mirror/Runtime/CustomAttributes.cs index 2fbbe3df0..277172b7b 100644 --- a/Mirror/Runtime/CustomAttributes.cs +++ b/Mirror/Runtime/CustomAttributes.cs @@ -18,21 +18,25 @@ public class SyncVarAttribute : Attribute [AttributeUsage(AttributeTargets.Method)] public class CommandAttribute : Attribute { + public int channel = Channels.DefaultReliable; // this is zero } [AttributeUsage(AttributeTargets.Method)] public class ClientRpcAttribute : Attribute { + public int channel = Channels.DefaultReliable; // this is zero } [AttributeUsage(AttributeTargets.Method)] public class TargetRpcAttribute : Attribute { + public int channel = Channels.DefaultReliable; // this is zero } [AttributeUsage(AttributeTargets.Event)] public class SyncEventAttribute : Attribute { + public int channel = Channels.DefaultReliable; // this is zero } [AttributeUsage(AttributeTargets.Method)] diff --git a/Mirror/Runtime/LocalConnections.cs b/Mirror/Runtime/LocalConnections.cs index 187ee0682..348154e50 100644 --- a/Mirror/Runtime/LocalConnections.cs +++ b/Mirror/Runtime/LocalConnections.cs @@ -17,7 +17,7 @@ public ULocalConnectionToClient(LocalClient localClient) m_LocalClient = localClient; } - protected override bool SendBytes(byte[] bytes) + protected override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable) { m_LocalClient.InvokeBytesOnClient(bytes); return true; @@ -33,7 +33,7 @@ public ULocalConnectionToServer() address = "localServer"; } - protected override bool SendBytes(byte[] bytes) + protected override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable) { if (bytes.Length == 0) { diff --git a/Mirror/Runtime/NetworkBehaviour.cs b/Mirror/Runtime/NetworkBehaviour.cs index 0e8cd8355..9e3885071 100644 --- a/Mirror/Runtime/NetworkBehaviour.cs +++ b/Mirror/Runtime/NetworkBehaviour.cs @@ -74,7 +74,7 @@ protected void InitSyncObject(SyncObject syncObject) // ----------------------------- Commands -------------------------------- [EditorBrowsable(EditorBrowsableState.Never)] - protected void SendCommandInternal(int cmdHash, NetworkWriter writer, string cmdName) + protected void SendCommandInternal(int cmdHash, NetworkWriter writer, int channelId, string cmdName) { // local players can always send commands, regardless of authority, other objects must have authority. if (!(isLocalPlayer || hasAuthority)) @@ -96,7 +96,7 @@ protected void SendCommandInternal(int cmdHash, NetworkWriter writer, string cmd message.cmdHash = cmdHash; message.payload = writer.ToArray(); - ClientScene.readyConnection.Send((short)MsgType.Command, message); + ClientScene.readyConnection.Send((short)MsgType.Command, message, channelId); } [EditorBrowsable(EditorBrowsableState.Never)] @@ -108,7 +108,7 @@ public virtual bool InvokeCommand(int cmdHash, NetworkReader reader) // ----------------------------- Client RPCs -------------------------------- [EditorBrowsable(EditorBrowsableState.Never)] - protected void SendRPCInternal(int rpcHash, NetworkWriter writer, string rpcName) + protected void SendRPCInternal(int rpcHash, NetworkWriter writer, int channelId, string rpcName) { // This cannot use NetworkServer.active, as that is not specific to this object. if (!isServer) @@ -124,11 +124,11 @@ protected void SendRPCInternal(int rpcHash, NetworkWriter writer, string rpcName message.rpcHash = rpcHash; message.payload = writer.ToArray(); - NetworkServer.SendToReady(gameObject, (short)MsgType.Rpc, message); + NetworkServer.SendToReady(gameObject, (short)MsgType.Rpc, message, channelId); } [EditorBrowsable(EditorBrowsableState.Never)] - protected void SendTargetRPCInternal(NetworkConnection conn, int rpcHash, NetworkWriter writer, string rpcName) + protected void SendTargetRPCInternal(NetworkConnection conn, int rpcHash, NetworkWriter writer, int channelId, string rpcName) { // This cannot use NetworkServer.active, as that is not specific to this object. if (!isServer) @@ -144,7 +144,7 @@ protected void SendTargetRPCInternal(NetworkConnection conn, int rpcHash, Networ message.rpcHash = rpcHash; message.payload = writer.ToArray(); - conn.Send((short)MsgType.Rpc, message); + conn.Send((short)MsgType.Rpc, message, channelId); } [EditorBrowsable(EditorBrowsableState.Never)] @@ -156,7 +156,7 @@ public virtual bool InvokeRPC(int cmdHash, NetworkReader reader) // ----------------------------- Sync Events -------------------------------- [EditorBrowsable(EditorBrowsableState.Never)] - protected void SendEventInternal(int eventHash, NetworkWriter writer, string eventName) + protected void SendEventInternal(int eventHash, NetworkWriter writer, int channelId, string eventName) { if (!NetworkServer.active) { @@ -171,7 +171,7 @@ protected void SendEventInternal(int eventHash, NetworkWriter writer, string eve message.eventHash = eventHash; message.payload = writer.ToArray(); - NetworkServer.SendToReady(gameObject, (short)MsgType.SyncEvent, message); + NetworkServer.SendToReady(gameObject, (short)MsgType.SyncEvent, message, channelId); } [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/Mirror/Runtime/NetworkConnection.cs b/Mirror/Runtime/NetworkConnection.cs index 48ee21cc9..ea0657ddc 100644 --- a/Mirror/Runtime/NetworkConnection.cs +++ b/Mirror/Runtime/NetworkConnection.cs @@ -158,19 +158,19 @@ internal void RemovePlayerController() m_PlayerController = null; } - public virtual bool Send(short msgType, MessageBase msg) + public virtual bool Send(short msgType, MessageBase msg, int channelId = Channels.DefaultReliable) { NetworkWriter writer = new NetworkWriter(); msg.Serialize(writer); // pack message and send byte[] message = Protocol.PackMessage((ushort)msgType, writer.ToArray()); - return SendBytes(message); + return SendBytes(message, channelId); } // protected because no one except NetworkConnection should ever send bytes directly to the client, as they // would be detected as some kind of message. send messages instead. - protected virtual bool SendBytes(byte[] bytes) + protected virtual bool SendBytes( byte[] bytes, int channelId = Channels.DefaultReliable) { if (logNetworkMessages) { Debug.Log("ConnectionSend con:" + connectionId + " bytes:" + BitConverter.ToString(bytes)); } @@ -188,7 +188,7 @@ protected virtual bool SendBytes(byte[] bytes) } byte error; - return TransportSend(bytes, out error); + return TransportSend(channelId, bytes, out error); } // handle this message @@ -269,17 +269,17 @@ public virtual void TransportReceive(byte[] bytes) HandleBytes(bytes); } - public virtual bool TransportSend(byte[] bytes, out byte error) + public virtual bool TransportSend(int channelId, byte[] bytes, out byte error) { error = 0; if (Transport.layer.ClientConnected()) { - Transport.layer.ClientSend(bytes); + Transport.layer.ClientSend(channelId, bytes); return true; } else if (Transport.layer.ServerActive()) { - Transport.layer.ServerSend(connectionId, bytes); + Transport.layer.ServerSend(connectionId, channelId, bytes); return true; } return false; diff --git a/Mirror/Runtime/NetworkServer.cs b/Mirror/Runtime/NetworkServer.cs index c4e3979c6..42d6fd63b 100644 --- a/Mirror/Runtime/NetworkServer.cs +++ b/Mirror/Runtime/NetworkServer.cs @@ -235,7 +235,7 @@ static bool SendToObservers(GameObject contextObj, short msgType, MessageBase ms return false; } - public static bool SendToAll(short msgType, MessageBase msg) + public static bool SendToAll(short msgType, MessageBase msg, int channelId = Channels.DefaultReliable) { if (LogFilter.Debug) { Debug.Log("Server.SendToAll id:" + msgType); } @@ -243,12 +243,12 @@ public static bool SendToAll(short msgType, MessageBase msg) foreach (KeyValuePair kvp in connections) { NetworkConnection conn = kvp.Value; - result &= conn.Send(msgType, msg); + result &= conn.Send(msgType, msg, channelId); } return result; } - public static bool SendToReady(GameObject contextObj, short msgType, MessageBase msg) + public static bool SendToReady(GameObject contextObj, short msgType, MessageBase msg, int channelId = Channels.DefaultReliable) { if (LogFilter.Debug) { Debug.Log("Server.SendToReady msgType:" + msgType); } @@ -260,7 +260,7 @@ public static bool SendToReady(GameObject contextObj, short msgType, MessageBase NetworkConnection conn = kvp.Value; if (conn.isReady) { - conn.Send(msgType, msg); + conn.Send(msgType, msg, channelId); } } return true; @@ -274,7 +274,7 @@ public static bool SendToReady(GameObject contextObj, short msgType, MessageBase { if (kvp.Value.isReady) { - result &= kvp.Value.Send(msgType, msg); + result &= kvp.Value.Send(msgType, msg, channelId); } } return result; diff --git a/Mirror/Runtime/Transport/LLAPITransport.cs b/Mirror/Runtime/Transport/LLAPITransport.cs index 067923aef..27f71e6e4 100644 --- a/Mirror/Runtime/Transport/LLAPITransport.cs +++ b/Mirror/Runtime/Transport/LLAPITransport.cs @@ -65,7 +65,10 @@ public LLAPITransport(GlobalConfig globalConfig = null, ConnectionConfig connect connectionConfig.BandwidthPeakFactor = 2; connectionConfig.WebSocketReceiveBufferMaxSize = 0; connectionConfig.UdpSocketReceiveBufferMaxSize = 0; - channelId = connectionConfig.AddChannel(QosType.ReliableFragmentedSequenced); + // channel 0 is reliable fragmented sequenced + connectionConfig.AddChannel(QosType.ReliableFragmentedSequenced); + // channel 1 is unreliable + connectionConfig.AddChannel(QosType.Unreliable); } this.connectionConfig = connectionConfig; @@ -96,7 +99,7 @@ public void ClientConnect(string address, int port) } } - public bool ClientSend(byte[] data) + public bool ClientSend(int channelId, byte[] data) { return NetworkTransport.Send(clientId, clientConnectionId, channelId, data, data.Length, out error); } @@ -124,23 +127,21 @@ public bool ClientGetNextMessage(out TransportEvent transportEvent, out byte[] d switch (networkEvent) { - case NetworkEventType.Nothing: - return false; case NetworkEventType.ConnectEvent: transportEvent = TransportEvent.Connected; break; case NetworkEventType.DataEvent: transportEvent = TransportEvent.Data; + data = new byte[receivedSize]; + Array.Copy(clientReceiveBuffer, data, receivedSize); break; case NetworkEventType.DisconnectEvent: transportEvent = TransportEvent.Disconnected; break; + default: + return false; } - // assign rest of the values and return true - data = new byte[receivedSize]; - Array.Copy(clientReceiveBuffer, data, receivedSize); - //Debug.Log("LLAPITransport.ClientGetNextMessage: clientid=" + clientId + " connid=" + connectionId + " event=" + networkEvent + " data=" + BitConverter.ToString(data) + " error=" + error); return true; } @@ -173,7 +174,7 @@ public void ServerStartWebsockets(string address, int port, int maxConnections) //Debug.Log("LLAPITransport.ServerStartWebsockets port=" + port + " max=" + maxConnections + " hostid=" + serverHostId); } - public bool ServerSend(int connectionId, byte[] data) + public bool ServerSend(int connectionId, int channelId, byte[] data) { return NetworkTransport.Send(serverHostId, connectionId, channelId, data, data.Length, out error); } @@ -201,30 +202,29 @@ public bool ServerGetNextMessage(out int connectionId, out TransportEvent transp // 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) + /*if (channel != channelId) { return false; - } + }*/ switch (networkEvent) { - case NetworkEventType.Nothing: - return false; case NetworkEventType.ConnectEvent: transportEvent = TransportEvent.Connected; break; case NetworkEventType.DataEvent: transportEvent = TransportEvent.Data; + data = new byte[receivedSize]; + Array.Copy(serverReceiveBuffer, data, receivedSize); break; case NetworkEventType.DisconnectEvent: transportEvent = TransportEvent.Disconnected; break; + default: + // nothing or a message we don't recognize + return false; } - // assign rest of the values and return true - data = new byte[receivedSize]; - Array.Copy(serverReceiveBuffer, data, receivedSize); - //Debug.Log("LLAPITransport.ServerGetNextMessage: hostId=" + serverHostId + " event=" + networkEvent + "connid=" + connectionId + " channel=" + channel + " data=" + BitConverter.ToString(data) + " error=" + error); return true; } diff --git a/Mirror/Runtime/Transport/TelepathyTransport.cs b/Mirror/Runtime/Transport/TelepathyTransport.cs index 605b6cbd5..3737d336c 100644 --- a/Mirror/Runtime/Transport/TelepathyTransport.cs +++ b/Mirror/Runtime/Transport/TelepathyTransport.cs @@ -25,7 +25,7 @@ public TelepathyTransport() // client public bool ClientConnected() { return client.Connected; } public void ClientConnect(string address, int port) { client.Connect(address, port); } - public bool ClientSend(byte[] data) { return client.Send(data); } + public bool ClientSend(int channelId, byte[] data) { return client.Send(data); } public bool ClientGetNextMessage(out TransportEvent transportEvent, out byte[] data) { Telepathy.Message message; @@ -59,7 +59,7 @@ public void ServerStartWebsockets(string address, int port, int maxConnections) { Debug.LogWarning("TelepathyTransport.ServerStartWebsockets not implemented yet!"); } - public bool ServerSend(int connectionId, byte[] data) { return server.Send(connectionId, data); } + public bool ServerSend(int connectionId, int channelId, byte[] data) { return server.Send(connectionId, data); } public bool ServerGetNextMessage(out int connectionId, out TransportEvent transportEvent, out byte[] data) { Telepathy.Message message; diff --git a/Mirror/Runtime/Transport/TelepathyWebsocketsMultiplexTransport.cs b/Mirror/Runtime/Transport/TelepathyWebsocketsMultiplexTransport.cs index eb6414fbf..e8aebdb06 100644 --- a/Mirror/Runtime/Transport/TelepathyWebsocketsMultiplexTransport.cs +++ b/Mirror/Runtime/Transport/TelepathyWebsocketsMultiplexTransport.cs @@ -36,9 +36,9 @@ public void ClientConnect(string address, int port) { client.ClientConnect(address, port); } - public bool ClientSend(byte[] data) + public bool ClientSend(int channelId, byte[] data) { - return client.ClientSend(data); + return client.ClientSend(channelId, data); } public bool ClientGetNextMessage(out TransportEvent transportEvent, out byte[] data) { @@ -81,9 +81,9 @@ public void ServerStartWebsockets(string address, int port, int maxConnections) else Debug.LogWarning("ServerStartWebsockets can't be called in WebGL."); } - public bool ServerSend(int connectionId, byte[] data) + public bool ServerSend(int connectionId, int channelId, byte[] data) { - return server != null && server.ServerSend(connectionId, data); + return server != null && server.ServerSend(connectionId, channelId, data); } public bool ServerGetNextMessage(out int connectionId, out TransportEvent transportEvent, out byte[] data) diff --git a/Mirror/Runtime/Transport/Transport.cs b/Mirror/Runtime/Transport/Transport.cs index e5438f4c1..73417f3b4 100644 --- a/Mirror/Runtime/Transport/Transport.cs +++ b/Mirror/Runtime/Transport/Transport.cs @@ -23,7 +23,7 @@ public interface TransportLayer // client bool ClientConnected(); void ClientConnect(string address, int port); - bool ClientSend(byte[] data); + bool ClientSend(int channelId, byte[] data); bool ClientGetNextMessage(out TransportEvent transportEvent, out byte[] data); void ClientDisconnect(); @@ -31,7 +31,7 @@ public interface TransportLayer bool ServerActive(); void ServerStart(string address, int port, int maxConnections); void ServerStartWebsockets(string address, int port, int maxConnections); - bool ServerSend(int connectionId, byte[] data); + bool ServerSend(int connectionId, int channelId, byte[] data); bool ServerGetNextMessage(out int connectionId, out TransportEvent transportEvent, out byte[] data); bool ServerDisconnect(int connectionId); bool GetConnectionInfo(int connectionId, out string address); diff --git a/Mirror/Runtime/UNetwork.cs b/Mirror/Runtime/UNetwork.cs index a52188e8a..ba3467361 100644 --- a/Mirror/Runtime/UNetwork.cs +++ b/Mirror/Runtime/UNetwork.cs @@ -77,6 +77,12 @@ public enum Version Current = 1 } + public class Channels + { + public const int DefaultReliable = 0; + public const int DefaultUnreliable = 1; + } + // network protocol all in one place, instead of constructing headers in all kinds of different places // // MsgType (1-n bytes) diff --git a/Mirror/Weaver/UNetBehaviourProcessor.cs b/Mirror/Weaver/UNetBehaviourProcessor.cs index da8456917..d3071fab1 100644 --- a/Mirror/Weaver/UNetBehaviourProcessor.cs +++ b/Mirror/Weaver/UNetBehaviourProcessor.cs @@ -501,6 +501,19 @@ void GenerateSerialization() m_td.Methods.Add(serialize); } + static int GetChannelId(CustomAttribute ca) + { + foreach (CustomAttributeNamedArgument customField in ca.Fields) + { + if (customField.Name == "channel") + { + return (int)customField.Argument.Value; + } + } + + return 0; + } + // returns false for error, not for no-hook-exists bool CheckForHookFunction(FieldDefinition syncVar, out MethodDefinition foundMethod) { @@ -900,7 +913,7 @@ public void CallCmdThrust(float thrusting, int spin) base.SendCommandInternal(ShipControl.kCmdCmdThrust, networkWriter, cmdName); } */ - MethodDefinition ProcessCommandCall(MethodDefinition md) + MethodDefinition ProcessCommandCall(MethodDefinition md, CustomAttribute ca) { MethodDefinition cmd = new MethodDefinition("Call" + md.Name, MethodAttributes.Public | MethodAttributes.HideBySig, @@ -965,6 +978,7 @@ MethodDefinition ProcessCommandCall(MethodDefinition md) cmdWorker.Append(cmdWorker.Create(OpCodes.Ldarg_0)); // load 'base.' to call the SendCommand function with cmdWorker.Append(cmdWorker.Create(OpCodes.Ldsfld, cmdConstant)); // cmdHash cmdWorker.Append(cmdWorker.Create(OpCodes.Ldloc_0)); // writer + cmdWorker.Append(cmdWorker.Create(OpCodes.Ldc_I4, GetChannelId(ca))); cmdWorker.Append(cmdWorker.Create(OpCodes.Ldstr, cmdName)); cmdWorker.Append(cmdWorker.Create(OpCodes.Call, Weaver.sendCommandInternal)); @@ -1046,7 +1060,7 @@ public void CallTargetTest (NetworkConnection conn, int param) } } */ - MethodDefinition ProcessTargetRpcCall(MethodDefinition md) + MethodDefinition ProcessTargetRpcCall(MethodDefinition md, CustomAttribute ca) { MethodDefinition rpc = new MethodDefinition("Call" + md.Name, MethodAttributes.Public | MethodAttributes.HideBySig, @@ -1101,6 +1115,7 @@ MethodDefinition ProcessTargetRpcCall(MethodDefinition md) rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_1)); // connection rpcWorker.Append(rpcWorker.Create(OpCodes.Ldsfld, rpcConstant)); // rpcHash rpcWorker.Append(rpcWorker.Create(OpCodes.Ldloc_0)); // writer + rpcWorker.Append(rpcWorker.Create(OpCodes.Ldc_I4, GetChannelId(ca))); rpcWorker.Append(rpcWorker.Create(OpCodes.Ldstr, rpcName)); rpcWorker.Append(rpcWorker.Create(OpCodes.Callvirt, Weaver.sendTargetRpcInternal)); @@ -1121,7 +1136,7 @@ public void CallRpcTest (int param) } } */ - MethodDefinition ProcessRpcCall(MethodDefinition md) + MethodDefinition ProcessRpcCall(MethodDefinition md, CustomAttribute ca) { MethodDefinition rpc = new MethodDefinition("Call" + md.Name, MethodAttributes.Public | MethodAttributes.HideBySig, @@ -1163,6 +1178,7 @@ MethodDefinition ProcessRpcCall(MethodDefinition md) rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_0)); // this rpcWorker.Append(rpcWorker.Create(OpCodes.Ldsfld, rpcConstant)); // rpcHash rpcWorker.Append(rpcWorker.Create(OpCodes.Ldloc_0)); // writer + rpcWorker.Append(rpcWorker.Create(OpCodes.Ldc_I4, GetChannelId(ca))); rpcWorker.Append(rpcWorker.Create(OpCodes.Ldstr, rpcName)); rpcWorker.Append(rpcWorker.Create(OpCodes.Callvirt, Weaver.sendRpcInternal)); @@ -1379,7 +1395,7 @@ void ProcessMethods() m_CmdInvocationFuncs.Add(cmdFunc); } - MethodDefinition cmdCallFunc = ProcessCommandCall(md); + MethodDefinition cmdCallFunc = ProcessCommandCall(md, ca); if (cmdCallFunc != null) { m_CmdCallFuncs.Add(cmdCallFunc); @@ -1409,7 +1425,7 @@ void ProcessMethods() m_TargetRpcInvocationFuncs.Add(rpcFunc); } - MethodDefinition rpcCallFunc = ProcessTargetRpcCall(md); + MethodDefinition rpcCallFunc = ProcessTargetRpcCall(md, ca); if (rpcCallFunc != null) { m_TargetRpcCallFuncs.Add(rpcCallFunc); @@ -1439,7 +1455,7 @@ void ProcessMethods() m_RpcInvocationFuncs.Add(rpcFunc); } - MethodDefinition rpcCallFunc = ProcessRpcCall(md); + MethodDefinition rpcCallFunc = ProcessRpcCall(md, ca); if (rpcCallFunc != null) { m_RpcCallFuncs.Add(rpcCallFunc); @@ -1537,7 +1553,7 @@ MethodDefinition ProcessEventInvoke(EventDefinition ed) return cmd; } - MethodDefinition ProcessEventCall(EventDefinition ed) + MethodDefinition ProcessEventCall(EventDefinition ed, CustomAttribute ca) { MethodReference invoke = Weaver.ResolveMethod(ed.EventType, "Invoke"); MethodDefinition evt = new MethodDefinition("Call" + ed.Name, MethodAttributes.Public | @@ -1572,6 +1588,7 @@ MethodDefinition ProcessEventCall(EventDefinition ed) evtWorker.Append(evtWorker.Create(OpCodes.Ldarg_0)); // this evtWorker.Append(evtWorker.Create(OpCodes.Ldsfld, evtConstant)); // eventHash evtWorker.Append(evtWorker.Create(OpCodes.Ldloc_0)); // writer + evtWorker.Append(evtWorker.Create(OpCodes.Ldc_I4, GetChannelId(ca))); evtWorker.Append(evtWorker.Create(OpCodes.Ldstr, ed.Name)); evtWorker.Append(evtWorker.Create(OpCodes.Call, Weaver.sendEventInternal)); @@ -1615,7 +1632,7 @@ void ProcessEvents() Weaver.DLog(m_td, "ProcessEvent " + ed); - MethodDefinition eventCallFunc = ProcessEventCall(ed); + MethodDefinition eventCallFunc = ProcessEventCall(ed, ca); m_td.Methods.Add(eventCallFunc); Weaver.lists.replacedEvents.Add(ed);