breaking: Force networkmessage as struct everywhere. Redundant MessagePacker.GetId(type) not needed anymore (#2413)

* breaking: Force NetworkMessage as struct everywhere

* Redundant GetId(type) is not needed anymore
This commit is contained in:
vis2k 2020-11-11 09:48:03 +01:00 committed by GitHub
parent 6d4aeaf3e3
commit 9c6c5d760c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 81 additions and 174 deletions

View File

@ -18,29 +18,21 @@ public static class MessagePacker
{
static readonly ILogger logger = LogFactory.GetLogger(typeof(MessagePacker));
public static int GetId<T>() where T : NetworkMessage
{
return GetId(typeof(T));
}
public static int GetId(Type type)
public static int GetId<T>() where T : struct, NetworkMessage
{
// paul: 16 bits is enough to avoid collisions
// - keeps the message size small because it gets varinted
// - in case of collisions, Mirror will display an error
return type.FullName.GetStableHashCode() & 0xFFFF;
return typeof(T).FullName.GetStableHashCode() & 0xFFFF;
}
// pack message before sending
// -> NetworkWriter passed as arg so that we can use .ToArraySegment
// and do an allocation free send before recycling it.
public static void Pack<T>(T message, NetworkWriter writer) where T : NetworkMessage
public static void Pack<T>(T message, NetworkWriter writer)
where T : struct, NetworkMessage
{
// 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 NetworkMessage),
// ask the message for the real type
int msgType = GetId(default(T) != null ? typeof(T) : message.GetType());
int msgType = GetId<T>();
writer.WriteUInt16((ushort)msgType);
// serialize message into writer
@ -48,7 +40,8 @@ public static void Pack<T>(T message, NetworkWriter writer) where T : NetworkMes
}
// unpack a message we received
public static T Unpack<T>(byte[] data) where T : NetworkMessage
public static T Unpack<T>(byte[] data)
where T : struct, NetworkMessage
{
using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(data))
{
@ -82,7 +75,7 @@ public static bool UnpackMessage(NetworkReader messageReader, out int msgType)
}
internal static NetworkMessageDelegate MessageHandler<T, C>(Action<C, T> handler, bool requireAuthenication)
where T : NetworkMessage
where T : struct, NetworkMessage
where C : NetworkConnection
=> (conn, reader, channelId) =>
{

View File

@ -238,7 +238,8 @@ static void RemoveTransportHandlers()
/// <typeparam name="T">The message type to unregister.</typeparam>
/// <param name="message"></param>
/// <param name="channelId"></param>
public static void Send<T>(T message, int channelId = Channels.DefaultReliable) where T : NetworkMessage
public static void Send<T>(T message, int channelId = Channels.DefaultReliable)
where T : struct, NetworkMessage
{
if (connection != null)
{
@ -306,7 +307,8 @@ internal static void RegisterSystemHandlers(bool hostMode)
/// <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 RegisterHandler<T>(Action<NetworkConnection, T> handler, bool requireAuthentication = true) where T : NetworkMessage
public static void RegisterHandler<T>(Action<NetworkConnection, T> handler, bool requireAuthentication = true)
where T : struct, NetworkMessage
{
int msgType = MessagePacker.GetId<T>();
if (handlers.ContainsKey(msgType))
@ -323,7 +325,8 @@ public static void RegisterHandler<T>(Action<NetworkConnection, T> handler, bool
/// <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 RegisterHandler<T>(Action<T> handler, bool requireAuthentication = true) where T : NetworkMessage
public static void RegisterHandler<T>(Action<T> handler, bool requireAuthentication = true)
where T : struct, NetworkMessage
{
RegisterHandler((NetworkConnection _, T value) => { handler(value); }, requireAuthentication);
}
@ -335,7 +338,8 @@ public static void RegisterHandler<T>(Action<T> handler, bool requireAuthenticat
/// <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
public static void ReplaceHandler<T>(Action<NetworkConnection, T> handler, bool requireAuthentication = true)
where T : struct, NetworkMessage
{
int msgType = MessagePacker.GetId<T>();
handlers[msgType] = MessagePacker.MessageHandler(handler, requireAuthentication);
@ -348,7 +352,8 @@ public static void ReplaceHandler<T>(Action<NetworkConnection, T> handler, bool
/// <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<T> handler, bool requireAuthentication = true) where T : NetworkMessage
public static void ReplaceHandler<T>(Action<T> handler, bool requireAuthentication = true)
where T : struct, NetworkMessage
{
ReplaceHandler((NetworkConnection _, T value) => { handler(value); }, requireAuthentication);
}
@ -357,7 +362,8 @@ public static void ReplaceHandler<T>(Action<T> handler, bool requireAuthenticati
/// Unregisters a network message handler.
/// </summary>
/// <typeparam name="T">The message type to unregister.</typeparam>
public static bool UnregisterHandler<T>() where T : NetworkMessage
public static bool UnregisterHandler<T>()
where T : struct, NetworkMessage
{
// use int to minimize collisions
int msgType = MessagePacker.GetId<T>();

View File

@ -112,7 +112,8 @@ internal void SetHandlers(Dictionary<int, NetworkMessageDelegate> handlers)
/// <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>
public void Send<T>(T msg, int channelId = Channels.DefaultReliable) where T : NetworkMessage
public void Send<T>(T msg, int channelId = Channels.DefaultReliable)
where T : struct, NetworkMessage
{
using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
@ -202,7 +203,8 @@ internal bool InvokeHandler(int msgType, NetworkReader reader, int channelId)
/// <typeparam name="T">The message type to unregister.</typeparam>
/// <param name="msg">The message object to process.</param>
/// <returns>Returns true if the handler was successfully invoked</returns>
public bool InvokeHandler<T>(T msg, int channelId) where T : NetworkMessage
public bool InvokeHandler<T>(T msg, int channelId)
where T : struct, NetworkMessage
{
using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{
@ -210,7 +212,7 @@ public bool InvokeHandler<T>(T msg, int channelId) where T : NetworkMessage
// this works because value types cannot be derived
// if it is a reference type (for example NetworkMessage),
// ask the message for the real type
int msgType = MessagePacker.GetId(default(T) != null ? typeof(T) : msg.GetType());
int msgType = MessagePacker.GetId<T>();
MessagePacker.Pack(msg, writer);
ArraySegment<byte> segment = writer.ToArraySegment();

View File

@ -48,7 +48,8 @@ internal MessageInfo(NetworkMessage message, int channel, int bytes, int count)
/// </summary>
public static event Action<MessageInfo> OutMessageEvent;
internal static void OnSend<T>(T message, int channel, int bytes, int count) where T : NetworkMessage
internal static void OnSend<T>(T message, int channel, int bytes, int count)
where T : struct, NetworkMessage
{
if (count > 0 && OutMessageEvent != null)
{
@ -66,7 +67,8 @@ internal static void OnSend<T>(T message, int channel, int bytes, int count) whe
/// </summary>
public static event Action<MessageInfo> InMessageEvent;
internal static void OnReceive<T>(T message, int channel, int bytes) where T : NetworkMessage
internal static void OnReceive<T>(T message, int channel, int bytes)
where T : struct, NetworkMessage
{
if (InMessageEvent != null)
{

View File

@ -248,7 +248,8 @@ public static void ActivateHostScene()
/// <param name="identity"></param>
/// <param name="msg"></param>
/// <param name="channelId"></param>
static void SendToObservers<T>(NetworkIdentity identity, T msg, int channelId = Channels.DefaultReliable) where T : NetworkMessage
static void SendToObservers<T>(NetworkIdentity identity, T msg, int channelId = Channels.DefaultReliable)
where T : struct, NetworkMessage
{
if (logger.LogEnabled()) logger.Log("Server.SendToObservers id:" + typeof(T));
@ -283,7 +284,8 @@ static void SendToObservers<T>(NetworkIdentity identity, T msg, int channelId =
/// <param name="msg">Message</param>
/// <param name="channelId">Transport channel to use</param>
/// <param name="sendToReadyOnly">Indicates if only ready clients should receive the message</param>
public static void SendToAll<T>(T msg, int channelId = Channels.DefaultReliable, bool sendToReadyOnly = false) where T : NetworkMessage
public static void SendToAll<T>(T msg, int channelId = Channels.DefaultReliable, bool sendToReadyOnly = false)
where T : struct, NetworkMessage
{
if (!active)
{
@ -329,7 +331,8 @@ public static void SendToAll<T>(T msg, int channelId = Channels.DefaultReliable,
/// <typeparam name="T">Message type.</typeparam>
/// <param name="msg">Message</param>
/// <param name="channelId">Transport channel to use</param>
public static void SendToReady<T>(T msg, int channelId = Channels.DefaultReliable) where T : NetworkMessage
public static void SendToReady<T>(T msg, int channelId = Channels.DefaultReliable)
where T : struct, NetworkMessage
{
if (!active)
{
@ -349,7 +352,8 @@ public static void SendToReady<T>(T msg, int channelId = Channels.DefaultReliabl
/// <param name="msg">Message</param>
/// <param name="includeOwner">Should the owner of the object be included</param>
/// <param name="channelId">Transport channel to use</param>
public static void SendToReady<T>(NetworkIdentity identity, T msg, bool includeOwner = true, int channelId = Channels.DefaultReliable) where T : NetworkMessage
public static void SendToReady<T>(NetworkIdentity identity, T msg, bool includeOwner = true, int channelId = Channels.DefaultReliable)
where T : struct, NetworkMessage
{
if (logger.LogEnabled()) logger.Log("Server.SendToReady msgType:" + typeof(T));
@ -391,7 +395,8 @@ public static void SendToReady<T>(NetworkIdentity identity, T msg, bool includeO
/// <param name="identity">identity of the object</param>
/// <param name="msg">Message</param>
/// <param name="channelId">Transport channel to use</param>
public static void SendToReady<T>(NetworkIdentity identity, T msg, int channelId) where T : NetworkMessage
public static void SendToReady<T>(NetworkIdentity identity, T msg, int channelId)
where T : struct, NetworkMessage
{
SendToReady(identity, msg, true, channelId);
}
@ -583,7 +588,8 @@ static void OnError(int connectionId, Exception exception)
/// <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 RegisterHandler<T>(Action<NetworkConnection, T> handler, bool requireAuthentication = true) where T : NetworkMessage
public static void RegisterHandler<T>(Action<NetworkConnection, T> handler, bool requireAuthentication = true)
where T : struct, NetworkMessage
{
int msgType = MessagePacker.GetId<T>();
if (handlers.ContainsKey(msgType))
@ -600,7 +606,8 @@ public static void RegisterHandler<T>(Action<NetworkConnection, T> handler, bool
/// <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 RegisterHandler<T>(Action<T> handler, bool requireAuthentication = true) where T : NetworkMessage
public static void RegisterHandler<T>(Action<T> handler, bool requireAuthentication = true)
where T : struct, NetworkMessage
{
RegisterHandler<T>((_, value) => { handler(value); }, requireAuthentication);
}
@ -612,7 +619,8 @@ public static void RegisterHandler<T>(Action<T> handler, bool requireAuthenticat
/// <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
public static void ReplaceHandler<T>(Action<NetworkConnection, T> handler, bool requireAuthentication = true)
where T : struct, NetworkMessage
{
int msgType = MessagePacker.GetId<T>();
handlers[msgType] = MessagePacker.MessageHandler(handler, requireAuthentication);
@ -625,7 +633,8 @@ public static void ReplaceHandler<T>(Action<NetworkConnection, T> handler, bool
/// <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<T> handler, bool requireAuthentication = true) where T : NetworkMessage
public static void ReplaceHandler<T>(Action<T> handler, bool requireAuthentication = true)
where T : struct, NetworkMessage
{
ReplaceHandler<T>((_, value) => { handler(value); }, requireAuthentication);
}
@ -634,7 +643,8 @@ public static void ReplaceHandler<T>(Action<T> handler, bool requireAuthenticati
/// Unregisters a handler for a particular message type.
/// </summary>
/// <typeparam name="T">Message type</typeparam>
public static void UnregisterHandler<T>() where T : NetworkMessage
public static void UnregisterHandler<T>()
where T : struct, NetworkMessage
{
int msgType = MessagePacker.GetId<T>();
handlers.Remove(msgType);
@ -654,7 +664,8 @@ public static void ClearHandlers()
/// <typeparam name="T">Message type</typeparam>
/// <param name="identity"></param>
/// <param name="msg"></param>
public static void SendToClientOfPlayer<T>(NetworkIdentity identity, T msg, int channelId = Channels.DefaultReliable) where T : NetworkMessage
public static void SendToClientOfPlayer<T>(NetworkIdentity identity, T msg, int channelId = Channels.DefaultReliable)
where T : struct, NetworkMessage
{
if (identity != null)
{

View File

@ -33,7 +33,7 @@ public static MockQuest WriteQuest(this NetworkReader reader)
public class CustomRWTest
{
public class QuestMessage : NetworkMessage
public struct QuestMessage : NetworkMessage
{
public MockQuest quest;
}

View File

@ -23,19 +23,19 @@ public static EnumReadWriteTests.MyCustomEnum ReadMyCustomEnum(this NetworkReade
}
public class EnumReadWriteTests
{
public class ByteMessage : NetworkMessage { public MyByteEnum byteEnum; }
public struct ByteMessage : NetworkMessage { public MyByteEnum byteEnum; }
public enum MyByteEnum : byte
{
A, B, C, D
}
public class ShortMessage : NetworkMessage { public MyShortEnum shortEnum; }
public struct ShortMessage : NetworkMessage { public MyShortEnum shortEnum; }
public enum MyShortEnum : short
{
E, F, G, H
}
public class CustomMessage : NetworkMessage { public MyCustomEnum customEnum; }
public struct CustomMessage : NetworkMessage { public MyCustomEnum customEnum; }
public enum MyCustomEnum
{
@ -51,8 +51,8 @@ public void ByteIsSentForByteEnum()
NetworkWriter writer = new NetworkWriter();
writer.Write(msg);
// should be 1 byte for null check and 1 byte for data
Assert.That(writer.Length, Is.EqualTo(2));
// should be 1 byte for data
Assert.That(writer.Length, Is.EqualTo(1));
}
[Test]
@ -63,8 +63,8 @@ public void ShortIsSentForShortEnum()
NetworkWriter writer = new NetworkWriter();
writer.Write(msg);
// should be 1 byte for null check and 2 bytes for data
Assert.That(writer.Length, Is.EqualTo(3));
// should be 2 bytes for data
Assert.That(writer.Length, Is.EqualTo(2));
}
[Test]
@ -76,7 +76,8 @@ public void CustomWriterIsUsedForEnum()
// custom writer should write N if it sees O
Assert.That(clientMsg.customEnum, Is.EqualTo(MyCustomEnum.N));
}
T SerializeAndDeserializeMessage<T>(T msg) where T : NetworkMessage
T SerializeAndDeserializeMessage<T>(T msg)
where T : struct, NetworkMessage
{
NetworkWriter writer = new NetworkWriter();

View File

@ -10,7 +10,7 @@ namespace Mirror.Tests.Generated.CollectionWriters
public class Array_int_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public int[] collection;
}
@ -74,7 +74,7 @@ public void SendsData()
public class Array_string_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public string[] collection;
}
@ -138,7 +138,7 @@ public void SendsData()
public class Array_Vector3_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public Vector3[] collection;
}
@ -202,7 +202,7 @@ public void SendsData()
public class Array_FloatStringStruct_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public FloatStringStruct[] collection;
}
@ -266,7 +266,7 @@ public void SendsData()
public class Array_ClassWithNoConstructor_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public ClassWithNoConstructor[] collection;
}
@ -330,7 +330,7 @@ public void SendsData()
public class ArraySegment_int_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public ArraySegment<int> collection;
}
@ -408,7 +408,7 @@ public void SendsData()
public class ArraySegment_string_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public ArraySegment<string> collection;
}
@ -486,7 +486,7 @@ public void SendsData()
public class ArraySegment_Vector3_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public ArraySegment<Vector3> collection;
}
@ -564,7 +564,7 @@ public void SendsData()
public class ArraySegment_FloatStringStruct_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public ArraySegment<FloatStringStruct> collection;
}
@ -642,7 +642,7 @@ public void SendsData()
public class ArraySegment_ClassWithNoConstructor_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public ArraySegment<ClassWithNoConstructor> collection;
}
@ -720,7 +720,7 @@ public void SendsData()
public class List_int_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public List<int> collection;
}
@ -784,7 +784,7 @@ public void SendsData()
public class List_string_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public List<string> collection;
}
@ -848,7 +848,7 @@ public void SendsData()
public class List_Vector3_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public List<Vector3> collection;
}
@ -912,7 +912,7 @@ public void SendsData()
public class List_FloatStringStruct_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public List<FloatStringStruct> collection;
}
@ -976,7 +976,7 @@ public void SendsData()
public class List_ClassWithNoConstructor_Test
{
public class Message : NetworkMessage
public struct Message : NetworkMessage
{
public List<ClassWithNoConstructor> collection;
}

View File

@ -37,45 +37,6 @@ struct StructWithEmptyMethodMessage : NetworkMessage
public double DoubleValue;
}
class ClassWithoutBaseMessage : NetworkMessage
{
public int[] array;
}
abstract class AbstractMessage : NetworkMessage
{
}
class OverrideMessage : AbstractMessage
{
public int someValue;
}
class Layer1Message : NetworkMessage
{
public int value1;
}
class Layer2Message : Layer1Message
{
public int value2;
}
class Layer3Message : Layer2Message
{
public int value3;
}
class NullableObject
{
public int id = 3;
}
class NullableObjectMessage : NetworkMessage
{
public string name;
public NullableObject nullObj;
}
[TestFixture]
public class MessageBaseTests
{
@ -98,75 +59,5 @@ public void StructWithEmptyMethods()
Assert.AreEqual("2", t.StringValue);
Assert.AreEqual(3.3, t.DoubleValue);
}
[Test]
public void ClassWithEmptyMethods()
{
ClassWithoutBaseMessage intMessage = new ClassWithoutBaseMessage
{
array = new[] { 3, 4, 5 }
};
byte[] data = MessagePackerTest.PackToByteArray(intMessage);
ClassWithoutBaseMessage unpacked = MessagePacker.Unpack<ClassWithoutBaseMessage>(data);
Assert.That(unpacked.array, Is.EquivalentTo(new int[] { 3, 4, 5 }));
}
[Test]
public void AbstractBaseClassWorks()
{
const int value = 10;
OverrideMessage intMessage = new OverrideMessage
{
someValue = value
};
byte[] data = MessagePackerTest.PackToByteArray(intMessage);
OverrideMessage unpacked = MessagePacker.Unpack<OverrideMessage>(data);
Assert.That(unpacked.someValue, Is.EqualTo(value));
}
[Test]
public void MessageInheirtanceWorksWithMultipleLayers()
{
const int value1 = 10;
const int value2 = 13;
const int value3 = 15;
Layer3Message intMessage = new Layer3Message
{
value1 = value1,
value2 = value2,
value3 = value3
};
byte[] data = MessagePackerTest.PackToByteArray(intMessage);
Layer3Message unpacked = MessagePacker.Unpack<Layer3Message>(data);
Assert.That(unpacked.value1, Is.EqualTo(value1));
Assert.That(unpacked.value2, Is.EqualTo(value2));
Assert.That(unpacked.value3, Is.EqualTo(value3));
}
[Test]
public void NullObjectMessageTest()
{
NullableObjectMessage nullableObjectMessage = new NullableObjectMessage
{
name = "pepe",
nullObj = null
};
byte[] data = MessagePackerTest.PackToByteArray(nullableObjectMessage);
NullableObjectMessage unpacked = MessagePacker.Unpack<NullableObjectMessage>(data);
Assert.That(unpacked.name, Is.EqualTo("pepe"));
Assert.That(unpacked.nullObj, Is.Null);
}
}
}

View File

@ -6,7 +6,8 @@ namespace Mirror.Tests
public class MessagePackerTest
{
// helper function to pack message into a simple byte[]
public static byte[] PackToByteArray<T>(T message) where T : NetworkMessage
public static byte[] PackToByteArray<T>(T message)
where T : struct, NetworkMessage
{
using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter())
{

View File

@ -2,7 +2,7 @@
namespace Mirror.Tests.MessageTests
{
class NoArgMethodMessage : NetworkMessage
struct NoArgMethodMessage : NetworkMessage
{
public int someValue;
@ -11,7 +11,7 @@ public void Serialize() { /* method with no arg */ }
public void Deserialize() { /* method with no arg */ }
}
class TwoArgMethodMessage : NetworkMessage
struct TwoArgMethodMessage : NetworkMessage
{
public int someValue;

View File

@ -16,7 +16,7 @@ public class ScriptableObjectWriterTest
// other types are generated by the weaver
public class ScriptableObjectMessage : NetworkMessage
public struct ScriptableObjectMessage : NetworkMessage
{
public MyScriptableObject scriptableObject;
}