Skip to content

Commit 6a4bb8f

Browse files
feat: enum ordered message versioning (#2929)
* update Added the MessageTypeDefines ordering class that orders messages based on the NetworkMessageType. * test Fixing issue with tests using their own message provider. Fixing a few more tests that don't need the message ordering or count verification stuff to be running. Removed the message ordering related tests since this it was no longer needed.
1 parent 4e35f84 commit 6a4bb8f

6 files changed

Lines changed: 137 additions & 103 deletions

File tree

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Additional documentation and release notes are available at [Multiplayer Documen
2222

2323
### Changed
2424

25+
- Changed messages are now sorted by enum values as opposed to ordinally sorting the messages by their type name. (#2929)
26+
2527

2628
## [2.0.0-exp.2] - 2024-04-02
2729

com.unity.netcode.gameobjects/Runtime/Messaging/ILPPMessageProvider.cs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
#if UNITY_EDITOR
34
using UnityEditor;
@@ -12,9 +13,117 @@ internal struct ILPPMessageProvider : INetworkMessageProvider
1213
internal static readonly List<NetworkMessageManager.MessageWithHandler> __network_message_types = new List<NetworkMessageManager.MessageWithHandler>();
1314
#pragma warning restore IDE1006 // restore naming rule violation check
1415

16+
/// <summary>
17+
/// Enum representing the different types of messages that can be sent over the network.
18+
/// The values cannot be changed, as they are used to serialize and deserialize messages.
19+
/// Adding new messages should be done by adding new values to the end of the enum
20+
/// using the next free value.
21+
/// </summary>
22+
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
23+
/// Add any new Message types to this table at the END with incremented index value
24+
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
25+
internal enum NetworkMessageTypes : uint
26+
{
27+
ConnectionApproved = 0,
28+
ConnectionRequest = 1,
29+
ChangeOwnership = 2,
30+
ClientConnected = 3,
31+
ClientDisconnected = 4,
32+
ClientRpc = 5,
33+
CreateObject = 6,
34+
DestroyObject = 7,
35+
DisconnectReason = 8,
36+
ForwardClientRpc = 9,
37+
ForwardServerRpc = 10,
38+
NamedMessage = 11,
39+
NetworkTransformMessage = 12,
40+
NetworkVariableDelta = 13,
41+
ParentSync = 14,
42+
Proxy = 15,
43+
Rpc = 16,
44+
SceneEvent = 17,
45+
ServerLog = 18,
46+
ServerRpc = 19,
47+
TimeSync = 20,
48+
Unnamed = 21,
49+
SessionOwner = 22
50+
}
51+
52+
53+
// Enable this for integration tests that need no message types defined
54+
internal static bool IntegrationTestNoMessages;
55+
1556
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
1657
{
17-
return __network_message_types;
58+
// return no message types when defined for integration tests
59+
if (IntegrationTestNoMessages)
60+
{
61+
return new List<NetworkMessageManager.MessageWithHandler>();
62+
}
63+
var messageTypeCount = Enum.GetValues(typeof(NetworkMessageTypes)).Length;
64+
// Assure the allowed types count is the same as our NetworkMessageType enum count
65+
if (__network_message_types.Count != messageTypeCount)
66+
{
67+
throw new Exception($"Allowed types is not equal to the number of message type indices! Allowed Count: {__network_message_types.Count} | Index Count: {messageTypeCount}");
68+
}
69+
70+
// Populate with blanks to be replaced later
71+
var adjustedMessageTypes = new List<NetworkMessageManager.MessageWithHandler>();
72+
var blank = new NetworkMessageManager.MessageWithHandler();
73+
for (int i = 0; i < messageTypeCount; i++)
74+
{
75+
adjustedMessageTypes.Add(blank);
76+
}
77+
78+
// Create a type to enum index lookup table
79+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
80+
// Add new Message types to this table paired with its new NetworkMessageTypes enum
81+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
82+
var messageTypes = new Dictionary<Type, NetworkMessageTypes>
83+
{
84+
{ typeof(ConnectionApprovedMessage), NetworkMessageTypes.ConnectionApproved }, // This MUST be first
85+
{ typeof(ConnectionRequestMessage), NetworkMessageTypes.ConnectionRequest }, // This MUST be second
86+
{ typeof(ChangeOwnershipMessage), NetworkMessageTypes.ChangeOwnership },
87+
{ typeof(ClientConnectedMessage), NetworkMessageTypes.ClientConnected },
88+
{ typeof(ClientDisconnectedMessage), NetworkMessageTypes.ClientDisconnected },
89+
{ typeof(ClientRpcMessage), NetworkMessageTypes.ClientRpc },
90+
{ typeof(CreateObjectMessage), NetworkMessageTypes.CreateObject },
91+
{ typeof(DestroyObjectMessage), NetworkMessageTypes.DestroyObject },
92+
{ typeof(DisconnectReasonMessage), NetworkMessageTypes.DisconnectReason },
93+
{ typeof(ForwardClientRpcMessage), NetworkMessageTypes.ForwardClientRpc },
94+
{ typeof(ForwardServerRpcMessage), NetworkMessageTypes.ForwardServerRpc },
95+
{ typeof(NamedMessage), NetworkMessageTypes.NamedMessage },
96+
{ typeof(NetworkTransformMessage), NetworkMessageTypes.NetworkTransformMessage },
97+
{ typeof(NetworkVariableDeltaMessage), NetworkMessageTypes.NetworkVariableDelta },
98+
{ typeof(ParentSyncMessage), NetworkMessageTypes.ParentSync },
99+
{ typeof(ProxyMessage), NetworkMessageTypes.Proxy },
100+
{ typeof(RpcMessage), NetworkMessageTypes.Rpc },
101+
{ typeof(SceneEventMessage), NetworkMessageTypes.SceneEvent },
102+
{ typeof(ServerLogMessage), NetworkMessageTypes.ServerLog },
103+
{ typeof(ServerRpcMessage), NetworkMessageTypes.ServerRpc },
104+
{ typeof(TimeSyncMessage), NetworkMessageTypes.TimeSync },
105+
{ typeof(UnnamedMessage), NetworkMessageTypes.Unnamed },
106+
{ typeof(SessionOwnerMessage), NetworkMessageTypes.SessionOwner }
107+
};
108+
109+
// Assure the type to lookup table count and NetworkMessageType enum count matches (i.e. to catch human error when adding new messages)
110+
if (messageTypes.Count != messageTypeCount)
111+
{
112+
throw new Exception($"Message type to Message type index count mistmatch! Table Count: {messageTypes.Count} | Index Count: {messageTypeCount}");
113+
}
114+
115+
// Now order the allowed types list based on the order of the NetworkMessageType enum
116+
foreach (var messageHandler in __network_message_types)
117+
{
118+
if (!messageTypes.ContainsKey(messageHandler.MessageType))
119+
{
120+
throw new Exception($"Missing message type from lookup table: {messageHandler.MessageType}");
121+
}
122+
adjustedMessageTypes[(int)messageTypes[messageHandler.MessageType]] = messageHandler;
123+
}
124+
125+
// return the NetworkMessageType enum ordered list
126+
return adjustedMessageTypes;
18127
}
19128

20129
#if UNITY_EDITOR

com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -120,49 +120,20 @@ internal struct MessageWithHandler
120120
public VersionGetter GetVersion;
121121
}
122122

123-
internal List<MessageWithHandler> PrioritizeMessageOrder(List<MessageWithHandler> allowedTypes)
124-
{
125-
var prioritizedTypes = new List<MessageWithHandler>();
126-
127-
// First pass puts the priority message in the first indices
128-
// Those are the messages that must be delivered in order to allow re-ordering the others later
129-
foreach (var t in allowedTypes)
130-
{
131-
if (t.MessageType.FullName == typeof(ConnectionRequestMessage).FullName ||
132-
t.MessageType.FullName == typeof(ConnectionApprovedMessage).FullName)
133-
{
134-
prioritizedTypes.Add(t);
135-
}
136-
}
137-
138-
foreach (var t in allowedTypes)
139-
{
140-
if (t.MessageType.FullName != typeof(ConnectionRequestMessage).FullName &&
141-
t.MessageType.FullName != typeof(ConnectionApprovedMessage).FullName)
142-
{
143-
prioritizedTypes.Add(t);
144-
}
145-
}
146-
147-
return prioritizedTypes;
148-
}
149-
150123
public NetworkMessageManager(INetworkMessageSender sender, object owner, INetworkMessageProvider provider = null)
151124
{
152125
try
153126
{
154127
m_Sender = sender;
155128
m_Owner = owner;
156-
157129
if (provider == null)
158130
{
159131
provider = new ILPPMessageProvider();
160132
}
161133

134+
// Get the presorted message types returned by the provider
162135
var allowedTypes = provider.GetMessages();
163136

164-
allowedTypes.Sort((a, b) => string.CompareOrdinal(a.MessageType.FullName, b.MessageType.FullName));
165-
allowedTypes = PrioritizeMessageOrder(allowedTypes);
166137
foreach (var type in allowedTypes)
167138
{
168139
RegisterMessageType(type);

com.unity.netcode.gameobjects/Tests/Editor/Messaging/MessageRegistrationTests.cs

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -191,77 +191,5 @@ public void WhenCreatingMessageSystem_BoundTypeMessageHandlersAreRegistered()
191191
Assert.AreEqual(handlerFour, systemThree.MessageHandlers[systemThree.GetMessageType(typeof(TestMessageFour))]);
192192
}
193193
}
194-
195-
internal class AAAEarlyLexicographicNetworkMessage : INetworkMessage
196-
{
197-
public void Serialize(FastBufferWriter writer, int targetVersion)
198-
{
199-
}
200-
201-
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
202-
{
203-
return true;
204-
}
205-
206-
public void Handle(ref NetworkContext context)
207-
{
208-
}
209-
210-
public int Version => 0;
211-
}
212-
213-
#pragma warning disable IDE1006
214-
internal class zzzLateLexicographicNetworkMessage : AAAEarlyLexicographicNetworkMessage
215-
{
216-
}
217-
#pragma warning restore IDE1006
218-
219-
internal class OrderingMessageProvider : INetworkMessageProvider
220-
{
221-
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
222-
{
223-
var listMessages = new List<NetworkMessageManager.MessageWithHandler>();
224-
225-
var messageWithHandler = new NetworkMessageManager.MessageWithHandler
226-
{
227-
MessageType = typeof(zzzLateLexicographicNetworkMessage),
228-
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<zzzLateLexicographicNetworkMessage>
229-
};
230-
listMessages.Add(messageWithHandler);
231-
232-
messageWithHandler.MessageType = typeof(ConnectionRequestMessage);
233-
messageWithHandler.GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<ConnectionRequestMessage>;
234-
listMessages.Add(messageWithHandler);
235-
236-
messageWithHandler.MessageType = typeof(ConnectionApprovedMessage);
237-
messageWithHandler.GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<ConnectionApprovedMessage>;
238-
listMessages.Add(messageWithHandler);
239-
240-
messageWithHandler.MessageType = typeof(AAAEarlyLexicographicNetworkMessage);
241-
messageWithHandler.GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<AAAEarlyLexicographicNetworkMessage>;
242-
listMessages.Add(messageWithHandler);
243-
244-
return listMessages;
245-
}
246-
}
247-
248-
[Test]
249-
public void MessagesGetPrioritizedCorrectly()
250-
{
251-
var sender = new NopMessageSender();
252-
var provider = new OrderingMessageProvider();
253-
using var messageManager = new NetworkMessageManager(sender, null, provider);
254-
255-
// the 2 priority messages should appear first, in lexicographic order
256-
Assert.AreEqual(messageManager.MessageTypes[0], typeof(ConnectionApprovedMessage));
257-
Assert.AreEqual(messageManager.MessageTypes[1], typeof(ConnectionRequestMessage));
258-
259-
// the other should follow after
260-
Assert.AreEqual(messageManager.MessageTypes[2], typeof(AAAEarlyLexicographicNetworkMessage));
261-
Assert.AreEqual(messageManager.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
262-
263-
// there should not be any extras
264-
Assert.AreEqual(messageManager.MessageHandlerCount, 4);
265-
}
266194
}
267195
}

com.unity.netcode.gameobjects/Tests/Editor/NetworkManagerConfigurationTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ namespace Unity.Netcode.EditorTests
1111
{
1212
public class NetworkManagerConfigurationTests
1313
{
14+
[SetUp]
15+
public void OnSetup()
16+
{
17+
ILPPMessageProvider.IntegrationTestNoMessages = true;
18+
}
19+
20+
[TearDown]
21+
public void OnTearDown()
22+
{
23+
ILPPMessageProvider.IntegrationTestNoMessages = false;
24+
}
25+
1426
/// <summary>
1527
/// Does a simple check to make sure the nested network manager will
1628
/// notify the user when in the editor. This is just a unit test to

com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ namespace Unity.Netcode.EditorTests
77
{
88
public class UnityTransportTests
99
{
10+
[SetUp]
11+
public void OnSetup()
12+
{
13+
ILPPMessageProvider.IntegrationTestNoMessages = true;
14+
}
15+
16+
[TearDown]
17+
public void OnTearDown()
18+
{
19+
ILPPMessageProvider.IntegrationTestNoMessages = false;
20+
}
21+
1022
// Check that starting an IPv4 server succeeds.
1123
[Test]
1224
public void UnityTransport_BasicInitServer_IPv4()

0 commit comments

Comments
 (0)