Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
## [Unreleased]

### Added

- Added support for different versions of the SDK to talk to each other in circumstances where changes permit it. Starting with this version and into future versions, patch versions should be compatible as long as the minor version is the same. (#2290)
- Added `NetworkObject` auto-add helper and Multiplayer Tools install reminder settings to Project Settings. (#2285)
- Added `public string DisconnectReason` getter to `NetworkManager` and `string Reason` to `ConnectionApprovalResponse`. Allows connection approval to communicate back a reason. Also added `public void DisconnectClient(ulong clientId, string reason)` allowing setting a disconnection reason, when explicitly disconnecting a client.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,19 @@ public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
private PostProcessorAssemblyResolver m_AssemblyResolver;

private MethodReference m_MessagingSystem_ReceiveMessage_MethodRef;
private MethodReference m_MessagingSystem_CreateMessageAndGetVersion_MethodRef;
private TypeReference m_MessagingSystem_MessageWithHandler_TypeRef;
private MethodReference m_MessagingSystem_MessageHandler_Constructor_TypeRef;
private MethodReference m_MessagingSystem_VersionGetter_Constructor_TypeRef;
private FieldReference m_ILPPMessageProvider___network_message_types_FieldRef;
private FieldReference m_MessagingSystem_MessageWithHandler_MessageType_FieldRef;
private FieldReference m_MessagingSystem_MessageWithHandler_Handler_FieldRef;
private FieldReference m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef;
private MethodReference m_Type_GetTypeFromHandle_MethodRef;
private MethodReference m_List_Add_MethodRef;

private const string k_ReceiveMessageName = nameof(MessagingSystem.ReceiveMessage);
private const string k_CreateMessageAndGetVersionName = nameof(MessagingSystem.CreateMessageAndGetVersion);

private bool ImportReferences(ModuleDefinition moduleDefinition)
{
Expand All @@ -126,6 +130,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition)
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();

TypeDefinition messageHandlerTypeDef = null;
TypeDefinition versionGetterTypeDef = null;
TypeDefinition messageWithHandlerTypeDef = null;
TypeDefinition ilppMessageProviderTypeDef = null;
TypeDefinition messagingSystemTypeDef = null;
Expand All @@ -137,6 +142,12 @@ private bool ImportReferences(ModuleDefinition moduleDefinition)
continue;
}

if (versionGetterTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.VersionGetter))
{
versionGetterTypeDef = netcodeTypeDef;
continue;
}

if (messageWithHandlerTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.MessageWithHandler))
{
messageWithHandlerTypeDef = netcodeTypeDef;
Expand All @@ -157,6 +168,7 @@ private bool ImportReferences(ModuleDefinition moduleDefinition)
}

m_MessagingSystem_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(messageHandlerTypeDef.GetConstructors().First());
m_MessagingSystem_VersionGetter_Constructor_TypeRef = moduleDefinition.ImportReference(versionGetterTypeDef.GetConstructors().First());

m_MessagingSystem_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerTypeDef);
foreach (var fieldDef in messageWithHandlerTypeDef.Fields)
Expand All @@ -169,6 +181,9 @@ private bool ImportReferences(ModuleDefinition moduleDefinition)
case nameof(MessagingSystem.MessageWithHandler.Handler):
m_MessagingSystem_MessageWithHandler_Handler_FieldRef = moduleDefinition.ImportReference(fieldDef);
break;
case nameof(MessagingSystem.MessageWithHandler.GetVersion):
m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef = moduleDefinition.ImportReference(fieldDef);
break;
}
}

Expand Down Expand Up @@ -211,6 +226,9 @@ private bool ImportReferences(ModuleDefinition moduleDefinition)
case k_ReceiveMessageName:
m_MessagingSystem_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodDef);
break;
case k_CreateMessageAndGetVersionName:
m_MessagingSystem_CreateMessageAndGetVersion_MethodRef = moduleDefinition.ImportReference(methodDef);
break;
}
}

Expand All @@ -236,7 +254,7 @@ private MethodDefinition GetOrCreateStaticConstructor(TypeDefinition typeDefinit
return staticCtorMethodDef;
}

private void CreateInstructionsToRegisterType(ILProcessor processor, List<Instruction> instructions, TypeReference type, MethodReference receiveMethod)
private void CreateInstructionsToRegisterType(ILProcessor processor, List<Instruction> instructions, TypeReference type, MethodReference receiveMethod, MethodReference versionMethod)
{
// MessagingSystem.__network_message_types.Add(new MessagingSystem.MessageWithHandler{MessageType=typeof(type), Handler=type.Receive});
processor.Body.Variables.Add(new VariableDefinition(m_MessagingSystem_MessageWithHandler_TypeRef));
Expand All @@ -252,14 +270,23 @@ private void CreateInstructionsToRegisterType(ILProcessor processor, List<Instru
instructions.Add(processor.Create(OpCodes.Call, m_Type_GetTypeFromHandle_MethodRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_MessageType_FieldRef));

// tmp.Handler = type.Receive
// tmp.Handler = MessageHandler.ReceveMessage<type>
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
instructions.Add(processor.Create(OpCodes.Ldnull));

instructions.Add(processor.Create(OpCodes.Ldftn, receiveMethod));
instructions.Add(processor.Create(OpCodes.Newobj, m_MessagingSystem_MessageHandler_Constructor_TypeRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_Handler_FieldRef));


// tmp.GetVersion = MessageHandler.CreateMessageAndGetVersion<type>
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
instructions.Add(processor.Create(OpCodes.Ldnull));

instructions.Add(processor.Create(OpCodes.Ldftn, versionMethod));
instructions.Add(processor.Create(OpCodes.Newobj, m_MessagingSystem_VersionGetter_Constructor_TypeRef));
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef));

// ILPPMessageProvider.__network_message_types.Add(tmp);
instructions.Add(processor.Create(OpCodes.Ldloc, messageWithHandlerLocIdx));
instructions.Add(processor.Create(OpCodes.Callvirt, m_List_Add_MethodRef));
Expand All @@ -285,7 +312,9 @@ private void CreateModuleInitializer(AssemblyDefinition assembly, List<TypeDefin
{
var receiveMethod = new GenericInstanceMethod(m_MessagingSystem_ReceiveMessage_MethodRef);
receiveMethod.GenericArguments.Add(type);
CreateInstructionsToRegisterType(processor, instructions, type, receiveMethod);
var versionMethod = new GenericInstanceMethod(m_MessagingSystem_CreateMessageAndGetVersion_MethodRef);
versionMethod.GenericArguments.Add(type);
CreateInstructionsToRegisterType(processor, instructions, type, receiveMethod, versionMethod);
}

instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ private void NetworkVariableUpdate(ulong targetClientId, int behaviourIndex)
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
using (tmpWriter)
{
message.Serialize(tmpWriter);
message.Serialize(tmpWriter, message.Version);
}
}
else
Expand Down
34 changes: 26 additions & 8 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1440,7 +1440,7 @@ internal void ShutdownInternal()
}
}

if (IsClient && IsConnectedClient)
if (IsClient && IsListening)
Comment thread
NoelStephensUnity marked this conversation as resolved.
{
// Client only, send disconnect to server
NetworkConfig.NetworkTransport.DisconnectLocalClient();
Expand Down Expand Up @@ -1598,6 +1598,7 @@ private void OnNetworkEarlyUpdate()
} while (IsListening && networkEvent != NetworkEvent.Nothing);

MessagingSystem.ProcessIncomingMessageQueue();
MessagingSystem.CleanupDisconnectedClients();

#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_TransportPoll.End();
Expand Down Expand Up @@ -1679,7 +1680,23 @@ private void SendConnectionRequest()
ShouldSendConnectionData = NetworkConfig.ConnectionApproval,
ConnectionData = NetworkConfig.ConnectionData
};

message.MessageVersions = new NativeArray<MessageVersionData>(MessagingSystem.MessageHandlers.Length, Allocator.Temp);
for (int index = 0; index < MessagingSystem.MessageHandlers.Length; index++)
{
if (MessagingSystem.MessageTypes[index] != null)
{
var type = MessagingSystem.MessageTypes[index];
message.MessageVersions[index] = new MessageVersionData
{
Hash = XXHash.Hash32(type.FullName),
Version = MessagingSystem.GetLocalVersion(type)
};
}
}

SendMessage(ref message, NetworkDelivery.ReliableSequenced, ServerClientId);
message.MessageVersions.Dispose();
}

private IEnumerator ApprovalTimeout(ulong clientId)
Expand Down Expand Up @@ -2230,22 +2247,23 @@ internal void HandleConnectionApproval(ulong ownerClientId, ConnectionApprovalRe
}
}

SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);

message.MessageVersions = new NativeArray<MessageVersionData>(MessagingSystem.MessageHandlers.Length, Allocator.Temp);
for (int index = 0; index < MessagingSystem.MessageHandlers.Length; index++)
{
if (MessagingSystem.MessageTypes[index] != null)
{
var orderingMessage = new OrderingMessage
var type = MessagingSystem.MessageTypes[index];
message.MessageVersions[index] = new MessageVersionData
{
Order = index,
Hash = XXHash.Hash32(MessagingSystem.MessageTypes[index].FullName)
Hash = XXHash.Hash32(type.FullName),
Version = MessagingSystem.GetLocalVersion(type)
};

SendMessage(ref orderingMessage, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
}
}

SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
message.MessageVersions.Dispose();

// If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization
if (!NetworkConfig.EnableSceneManagement)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,26 @@ internal struct DisconnectReasonMessage : INetworkMessage
{
public string Reason;

public void Serialize(FastBufferWriter writer)
public int Version => 0;

public void Serialize(FastBufferWriter writer, int targetVersion)
{
string reasonSent = Reason;
if (reasonSent == null)
{
reasonSent = string.Empty;
}

// Since we don't send a ConnectionApprovedMessage, the version for this message is encded with the message
// itself. However, note that we HAVE received a ConnectionRequestMessage, so we DO have a valid targetVersion
// on this side of things - we just have to make sure the receiving side knows what version we sent it,
// since whoever has the higher version number is responsible for versioning and they may be the one
// with the higher version number.
BytePacker.WriteValueBitPacked(writer, Version);

if (writer.TryBeginWrite(FastBufferWriter.GetWriteSize(reasonSent)))
{
writer.WriteValueSafe(reasonSent);
writer.WriteValue(reasonSent);
}
else
{
Expand All @@ -24,8 +33,11 @@ public void Serialize(FastBufferWriter writer)
}
}

public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
{
// Since we don't get a ConnectionApprovedMessage, the version for this message is encded with the message
// itself. This will override what we got from MessagingSystem... which will always be 0 here.
ByteUnpacker.ReadValueBitPacked(reader, out receivedMessageVersion);
reader.ReadValueSafe(out Reason);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ namespace Unity.Netcode
/// </summary>
internal interface INetworkMessage
{
void Serialize(FastBufferWriter writer);
bool Deserialize(FastBufferReader reader, ref NetworkContext context);
void Serialize(FastBufferWriter writer, int targetVersion);
bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion);
void Handle(ref NetworkContext context);
int Version { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ namespace Unity.Netcode
{
internal struct ChangeOwnershipMessage : INetworkMessage, INetworkSerializeByMemcpy
{
public int Version => 0;

public ulong NetworkObjectId;
public ulong OwnerClientId;

public void Serialize(FastBufferWriter writer)
public void Serialize(FastBufferWriter writer, int targetVersion)
{
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
BytePacker.WriteValueBitPacked(writer, OwnerClientId);
}

public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
{
var networkManager = (NetworkManager)context.SystemOwner;
if (!networkManager.IsClient)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using Unity.Collections;

namespace Unity.Netcode
{
internal struct ConnectionApprovedMessage : INetworkMessage
{
public int Version => 0;

public ulong OwnerClientId;
public int NetworkTick;

Expand All @@ -13,12 +15,24 @@ internal struct ConnectionApprovedMessage : INetworkMessage

private FastBufferReader m_ReceivedSceneObjectData;

public void Serialize(FastBufferWriter writer)
public NativeArray<MessageVersionData> MessageVersions;

public void Serialize(FastBufferWriter writer, int targetVersion)
{
if (!writer.TryBeginWrite(sizeof(ulong) + sizeof(int) + sizeof(int)))
// ============================================================
Comment thread
NoelStephensUnity marked this conversation as resolved.
// BEGIN FORBIDDEN SEGMENT
// DO NOT CHANGE THIS HEADER. Everything added to this message
// must go AFTER the message version header.
// ============================================================
BytePacker.WriteValueBitPacked(writer, MessageVersions.Length);
foreach (var messageVersion in MessageVersions)
Comment on lines +27 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it necessary to include all versions in the message header rather than just the highest version?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every message is versioned separately. We need to know what each message's version is... if message A is version 1 but message B is version 2, we don't want to tell the remote end that message A and B are both version 2, because then it'll serialize message A in a way we can't read.

{
throw new OverflowException($"Not enough space in the write buffer to serialize {nameof(ConnectionApprovedMessage)}");
messageVersion.Serialize(writer);
}
// ============================================================
// END FORBIDDEN SEGMENT
// ============================================================

BytePacker.WriteValueBitPacked(writer, OwnerClientId);
BytePacker.WriteValueBitPacked(writer, NetworkTick);

Expand Down Expand Up @@ -51,14 +65,42 @@ public void Serialize(FastBufferWriter writer)
}
}

public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
{
var networkManager = (NetworkManager)context.SystemOwner;
if (!networkManager.IsClient)
{
return false;
}

// ============================================================
// BEGIN FORBIDDEN SEGMENT
// DO NOT CHANGE THIS HEADER. Everything added to this message
// must go AFTER the message version header.
// ============================================================
ByteUnpacker.ReadValueBitPacked(reader, out int length);
var messageHashesInOrder = new NativeArray<uint>(length, Allocator.Temp);
for (var i = 0; i < length; ++i)
{
var messageVersion = new MessageVersionData();
messageVersion.Deserialize(reader);
networkManager.MessagingSystem.SetVersion(context.SenderId, messageVersion.Hash, messageVersion.Version);
messageHashesInOrder[i] = messageVersion.Hash;

// Update the received version since this message will always be passed version 0, due to the map not
// being initialized until just now.
var messageType = networkManager.MessagingSystem.GetMessageForHash(messageVersion.Hash);
if (messageType == typeof(ConnectionApprovedMessage))
{
receivedMessageVersion = messageVersion.Version;
}
}
networkManager.MessagingSystem.SetServerMessageOrder(messageHashesInOrder);
messageHashesInOrder.Dispose();
// ============================================================
// END FORBIDDEN SEGMENT
// ============================================================

ByteUnpacker.ReadValueBitPacked(reader, out OwnerClientId);
ByteUnpacker.ReadValueBitPacked(reader, out NetworkTick);
m_ReceivedSceneObjectData = reader;
Expand Down
Loading