com.unity.netcode.gameobjects@1.2.0
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.2.0] - 2022-11-21 ### Added - Added protected method `NetworkBehaviour.OnSynchronize` which is invoked during the initial `NetworkObject` synchronization process. This provides users the ability to include custom serialization information that will be applied to the `NetworkBehaviour` prior to the `NetworkObject` being spawned. (#2298) - 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. (#2280) ### Changed - Changed 3rd-party `XXHash` (32 & 64) implementation with an in-house reimplementation (#2310) - When `NetworkConfig.EnsureNetworkVariableLengthSafety` is disabled `NetworkVariable` fields do not write the additional `ushort` size value (_which helps to reduce the total synchronization message size_), but when enabled it still writes the additional `ushort` value. (#2298) - Optimized bandwidth usage by encoding most integer fields using variable-length encoding. (#2276) ### Fixed - Fixed issue where `NetworkTransform` components nested under a parent with a `NetworkObject` component (i.e. network prefab) would not have their associated `GameObject`'s transform synchronized. (#2298) - Fixed issue where `NetworkObject`s that failed to instantiate could cause the entire synchronization pipeline to be disrupted/halted for a connecting client. (#2298) - Fixed issue where in-scene placed `NetworkObject`s nested under a `GameObject` would be added to the orphaned children list causing continual console warning log messages. (#2298) - Custom messages are now properly received by the local client when they're sent while running in host mode. (#2296) - Fixed issue where the host would receive more than one event completed notification when loading or unloading a scene only when no clients were connected. (#2292) - Fixed an issue in `UnityTransport` where an error would be logged if the 'Use Encryption' flag was enabled with a Relay configuration that used a secure protocol. (#2289) - Fixed issue where in-scene placed `NetworkObjects` were not honoring the `AutoObjectParentSync` property. (#2281) - Fixed the issue where `NetworkManager.OnClientConnectedCallback` was being invoked before in-scene placed `NetworkObject`s had been spawned when starting `NetworkManager` as a host. (#2277) - Creating a `FastBufferReader` with `Allocator.None` will not result in extra memory being allocated for the buffer (since it's owned externally in that scenario). (#2265) ### Removed - Removed the `NetworkObject` auto-add and Multiplayer Tools install reminder settings from the Menu interface. (#2285)
This commit is contained in:
37
CHANGELOG.md
37
CHANGELOG.md
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
@@ -7,6 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|||||||
|
|
||||||
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
|
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
|
||||||
|
|
||||||
|
## [1.2.0] - 2022-11-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added protected method `NetworkBehaviour.OnSynchronize` which is invoked during the initial `NetworkObject` synchronization process. This provides users the ability to include custom serialization information that will be applied to the `NetworkBehaviour` prior to the `NetworkObject` being spawned. (#2298)
|
||||||
|
- 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. (#2280)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Changed 3rd-party `XXHash` (32 & 64) implementation with an in-house reimplementation (#2310)
|
||||||
|
- When `NetworkConfig.EnsureNetworkVariableLengthSafety` is disabled `NetworkVariable` fields do not write the additional `ushort` size value (_which helps to reduce the total synchronization message size_), but when enabled it still writes the additional `ushort` value. (#2298)
|
||||||
|
- Optimized bandwidth usage by encoding most integer fields using variable-length encoding. (#2276)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed issue where `NetworkTransform` components nested under a parent with a `NetworkObject` component (i.e. network prefab) would not have their associated `GameObject`'s transform synchronized. (#2298)
|
||||||
|
- Fixed issue where `NetworkObject`s that failed to instantiate could cause the entire synchronization pipeline to be disrupted/halted for a connecting client. (#2298)
|
||||||
|
- Fixed issue where in-scene placed `NetworkObject`s nested under a `GameObject` would be added to the orphaned children list causing continual console warning log messages. (#2298)
|
||||||
|
- Custom messages are now properly received by the local client when they're sent while running in host mode. (#2296)
|
||||||
|
- Fixed issue where the host would receive more than one event completed notification when loading or unloading a scene only when no clients were connected. (#2292)
|
||||||
|
- Fixed an issue in `UnityTransport` where an error would be logged if the 'Use Encryption' flag was enabled with a Relay configuration that used a secure protocol. (#2289)
|
||||||
|
- Fixed issue where in-scene placed `NetworkObjects` were not honoring the `AutoObjectParentSync` property. (#2281)
|
||||||
|
- Fixed the issue where `NetworkManager.OnClientConnectedCallback` was being invoked before in-scene placed `NetworkObject`s had been spawned when starting `NetworkManager` as a host. (#2277)
|
||||||
|
- Creating a `FastBufferReader` with `Allocator.None` will not result in extra memory being allocated for the buffer (since it's owned externally in that scenario). (#2265)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Removed the `NetworkObject` auto-add and Multiplayer Tools install reminder settings from the Menu interface. (#2285)
|
||||||
|
|
||||||
## [1.1.0] - 2022-10-21
|
## [1.1.0] - 2022-10-21
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -157,6 +187,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
|
|||||||
- Removed `ClientNetworkTransform` from the package samples and moved to Boss Room's Utilities package which can be found [here](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Packages/com.unity.multiplayer.samples.coop/Utilities/Net/ClientAuthority/ClientNetworkTransform.cs) (#1912)
|
- Removed `ClientNetworkTransform` from the package samples and moved to Boss Room's Utilities package which can be found [here](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Packages/com.unity.multiplayer.samples.coop/Utilities/Net/ClientAuthority/ClientNetworkTransform.cs) (#1912)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed issue where `NetworkSceneManager` did not synchronize despawned in-scene placed NetworkObjects. (#1898)
|
- Fixed issue where `NetworkSceneManager` did not synchronize despawned in-scene placed NetworkObjects. (#1898)
|
||||||
- Fixed `NetworkTransform` generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890)
|
- Fixed `NetworkTransform` generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890)
|
||||||
- Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884)
|
- Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884)
|
||||||
@@ -210,10 +241,12 @@ Additional documentation and release notes are available at [Multiplayer Documen
|
|||||||
## [1.0.0-pre.6] - 2022-03-02
|
## [1.0.0-pre.6] - 2022-03-02
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- NetworkAnimator now properly synchrhonizes all animation layers as well as runtime-adjusted weighting between them (#1765)
|
- NetworkAnimator now properly synchrhonizes all animation layers as well as runtime-adjusted weighting between them (#1765)
|
||||||
- Added first set of tests for NetworkAnimator - parameter syncing, trigger set / reset, override network animator (#1735)
|
- Added first set of tests for NetworkAnimator - parameter syncing, trigger set / reset, override network animator (#1735)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed an issue where sometimes the first client to connect to the server could see messages from the server as coming from itself. (#1683)
|
- Fixed an issue where sometimes the first client to connect to the server could see messages from the server as coming from itself. (#1683)
|
||||||
- Fixed an issue where clients seemed to be able to send messages to ClientId 1, but these messages would actually still go to the server (id 0) instead of that client. (#1683)
|
- Fixed an issue where clients seemed to be able to send messages to ClientId 1, but these messages would actually still go to the server (id 0) instead of that client. (#1683)
|
||||||
- Improved clarity of error messaging when a client attempts to send a message to a destination other than the server, which isn't allowed. (#1683)
|
- Improved clarity of error messaging when a client attempts to send a message to a destination other than the server, which isn't allowed. (#1683)
|
||||||
@@ -265,6 +298,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
|
|||||||
- Removed `FixedQueue`, `StreamExtensions`, `TypeExtensions` (#1398)
|
- Removed `FixedQueue`, `StreamExtensions`, `TypeExtensions` (#1398)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed in-scene NetworkObjects that are moved into the DDOL scene not getting restored to their original active state (enabled/disabled) after a full scene transition (#1354)
|
- Fixed in-scene NetworkObjects that are moved into the DDOL scene not getting restored to their original active state (enabled/disabled) after a full scene transition (#1354)
|
||||||
- Fixed invalid IL code being generated when using `this` instead of `this ref` for the FastBufferReader/FastBufferWriter parameter of an extension method. (#1393)
|
- Fixed invalid IL code being generated when using `this` instead of `this ref` for the FastBufferReader/FastBufferWriter parameter of an extension method. (#1393)
|
||||||
- Fixed an issue where if you are running as a server (not host) the LoadEventCompleted and UnloadEventCompleted events would fire early by the NetworkSceneManager (#1379)
|
- Fixed an issue where if you are running as a server (not host) the LoadEventCompleted and UnloadEventCompleted events would fire early by the NetworkSceneManager (#1379)
|
||||||
@@ -279,6 +313,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
|
|||||||
- Fixed network tick value sometimes being duplicated or skipped. (#1614)
|
- Fixed network tick value sometimes being duplicated or skipped. (#1614)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The SDK no longer limits message size to 64k. (The transport may still impose its own limits, but the SDK no longer does.) (#1384)
|
- The SDK no longer limits message size to 64k. (The transport may still impose its own limits, but the SDK no longer does.) (#1384)
|
||||||
- Updated com.unity.collections to 1.1.0 (#1451)
|
- Updated com.unity.collections to 1.1.0 (#1451)
|
||||||
- NetworkManager's GameObject is no longer allowed to be nested under one or more GameObject(s).(#1484)
|
- NetworkManager's GameObject is no longer allowed to be nested under one or more GameObject(s).(#1484)
|
||||||
|
|||||||
@@ -451,6 +451,33 @@ namespace Unity.Netcode.Components
|
|||||||
return m_LastSentState;
|
return m_LastSentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is invoked when a new client joins (server and client sides)
|
||||||
|
/// Server Side: Serializes as if we were teleporting (everything is sent via NetworkTransformState)
|
||||||
|
/// Client Side: Adds the interpolated state which applies the NetworkTransformState as well
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
|
||||||
|
{
|
||||||
|
// We don't need to synchronize NetworkTransforms that are on the same
|
||||||
|
// GameObject as the NetworkObject.
|
||||||
|
if (NetworkObject.gameObject == gameObject)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var synchronizationState = new NetworkTransformState();
|
||||||
|
if (serializer.IsWriter)
|
||||||
|
{
|
||||||
|
synchronizationState.IsTeleportingNextFrame = true;
|
||||||
|
ApplyTransformToNetworkStateWithInfo(ref synchronizationState, m_CachedNetworkManager.LocalTime.Time, transform);
|
||||||
|
synchronizationState.NetworkSerialize(serializer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
synchronizationState.NetworkSerialize(serializer);
|
||||||
|
AddInterpolatedState(synchronizationState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This will try to send/commit the current transform delta states (if any)
|
/// This will try to send/commit the current transform delta states (if any)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -102,15 +102,19 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
private PostProcessorAssemblyResolver m_AssemblyResolver;
|
private PostProcessorAssemblyResolver m_AssemblyResolver;
|
||||||
|
|
||||||
private MethodReference m_MessagingSystem_ReceiveMessage_MethodRef;
|
private MethodReference m_MessagingSystem_ReceiveMessage_MethodRef;
|
||||||
|
private MethodReference m_MessagingSystem_CreateMessageAndGetVersion_MethodRef;
|
||||||
private TypeReference m_MessagingSystem_MessageWithHandler_TypeRef;
|
private TypeReference m_MessagingSystem_MessageWithHandler_TypeRef;
|
||||||
private MethodReference m_MessagingSystem_MessageHandler_Constructor_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_ILPPMessageProvider___network_message_types_FieldRef;
|
||||||
private FieldReference m_MessagingSystem_MessageWithHandler_MessageType_FieldRef;
|
private FieldReference m_MessagingSystem_MessageWithHandler_MessageType_FieldRef;
|
||||||
private FieldReference m_MessagingSystem_MessageWithHandler_Handler_FieldRef;
|
private FieldReference m_MessagingSystem_MessageWithHandler_Handler_FieldRef;
|
||||||
|
private FieldReference m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef;
|
||||||
private MethodReference m_Type_GetTypeFromHandle_MethodRef;
|
private MethodReference m_Type_GetTypeFromHandle_MethodRef;
|
||||||
private MethodReference m_List_Add_MethodRef;
|
private MethodReference m_List_Add_MethodRef;
|
||||||
|
|
||||||
private const string k_ReceiveMessageName = nameof(MessagingSystem.ReceiveMessage);
|
private const string k_ReceiveMessageName = nameof(MessagingSystem.ReceiveMessage);
|
||||||
|
private const string k_CreateMessageAndGetVersionName = nameof(MessagingSystem.CreateMessageAndGetVersion);
|
||||||
|
|
||||||
private bool ImportReferences(ModuleDefinition moduleDefinition)
|
private bool ImportReferences(ModuleDefinition moduleDefinition)
|
||||||
{
|
{
|
||||||
@@ -126,6 +130,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();
|
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();
|
||||||
|
|
||||||
TypeDefinition messageHandlerTypeDef = null;
|
TypeDefinition messageHandlerTypeDef = null;
|
||||||
|
TypeDefinition versionGetterTypeDef = null;
|
||||||
TypeDefinition messageWithHandlerTypeDef = null;
|
TypeDefinition messageWithHandlerTypeDef = null;
|
||||||
TypeDefinition ilppMessageProviderTypeDef = null;
|
TypeDefinition ilppMessageProviderTypeDef = null;
|
||||||
TypeDefinition messagingSystemTypeDef = null;
|
TypeDefinition messagingSystemTypeDef = null;
|
||||||
@@ -137,6 +142,12 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (versionGetterTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.VersionGetter))
|
||||||
|
{
|
||||||
|
versionGetterTypeDef = netcodeTypeDef;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (messageWithHandlerTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.MessageWithHandler))
|
if (messageWithHandlerTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.MessageWithHandler))
|
||||||
{
|
{
|
||||||
messageWithHandlerTypeDef = netcodeTypeDef;
|
messageWithHandlerTypeDef = netcodeTypeDef;
|
||||||
@@ -157,6 +168,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_MessagingSystem_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(messageHandlerTypeDef.GetConstructors().First());
|
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);
|
m_MessagingSystem_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerTypeDef);
|
||||||
foreach (var fieldDef in messageWithHandlerTypeDef.Fields)
|
foreach (var fieldDef in messageWithHandlerTypeDef.Fields)
|
||||||
@@ -169,6 +181,9 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
case nameof(MessagingSystem.MessageWithHandler.Handler):
|
case nameof(MessagingSystem.MessageWithHandler.Handler):
|
||||||
m_MessagingSystem_MessageWithHandler_Handler_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
m_MessagingSystem_MessageWithHandler_Handler_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
||||||
break;
|
break;
|
||||||
|
case nameof(MessagingSystem.MessageWithHandler.GetVersion):
|
||||||
|
m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +226,9 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
case k_ReceiveMessageName:
|
case k_ReceiveMessageName:
|
||||||
m_MessagingSystem_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodDef);
|
m_MessagingSystem_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodDef);
|
||||||
break;
|
break;
|
||||||
|
case k_CreateMessageAndGetVersionName:
|
||||||
|
m_MessagingSystem_CreateMessageAndGetVersion_MethodRef = moduleDefinition.ImportReference(methodDef);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +254,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
return staticCtorMethodDef;
|
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});
|
// MessagingSystem.__network_message_types.Add(new MessagingSystem.MessageWithHandler{MessageType=typeof(type), Handler=type.Receive});
|
||||||
processor.Body.Variables.Add(new VariableDefinition(m_MessagingSystem_MessageWithHandler_TypeRef));
|
processor.Body.Variables.Add(new VariableDefinition(m_MessagingSystem_MessageWithHandler_TypeRef));
|
||||||
@@ -252,7 +270,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.Add(processor.Create(OpCodes.Call, m_Type_GetTypeFromHandle_MethodRef));
|
instructions.Add(processor.Create(OpCodes.Call, m_Type_GetTypeFromHandle_MethodRef));
|
||||||
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_MessageType_FieldRef));
|
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.Ldloca, messageWithHandlerLocIdx));
|
||||||
instructions.Add(processor.Create(OpCodes.Ldnull));
|
instructions.Add(processor.Create(OpCodes.Ldnull));
|
||||||
|
|
||||||
@@ -260,6 +278,15 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.Add(processor.Create(OpCodes.Newobj, m_MessagingSystem_MessageHandler_Constructor_TypeRef));
|
instructions.Add(processor.Create(OpCodes.Newobj, m_MessagingSystem_MessageHandler_Constructor_TypeRef));
|
||||||
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_Handler_FieldRef));
|
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);
|
// ILPPMessageProvider.__network_message_types.Add(tmp);
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloc, messageWithHandlerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloc, messageWithHandlerLocIdx));
|
||||||
instructions.Add(processor.Create(OpCodes.Callvirt, m_List_Add_MethodRef));
|
instructions.Add(processor.Create(OpCodes.Callvirt, m_List_Add_MethodRef));
|
||||||
@@ -285,7 +312,9 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
{
|
{
|
||||||
var receiveMethod = new GenericInstanceMethod(m_MessagingSystem_ReceiveMessage_MethodRef);
|
var receiveMethod = new GenericInstanceMethod(m_MessagingSystem_ReceiveMessage_MethodRef);
|
||||||
receiveMethod.GenericArguments.Add(type);
|
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));
|
instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
|
||||||
|
|||||||
@@ -139,6 +139,19 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsSpecialCaseType(TypeReference type)
|
||||||
|
{
|
||||||
|
foreach (var supportedType in SpecialCaseTypes)
|
||||||
|
{
|
||||||
|
if (type.FullName == supportedType.FullName)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly)
|
private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly)
|
||||||
{
|
{
|
||||||
foreach (var typeDefinition in assembly.MainModule.Types)
|
foreach (var typeDefinition in assembly.MainModule.Types)
|
||||||
@@ -153,6 +166,11 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
foreach (var type in m_WrappedNetworkVariableTypes)
|
foreach (var type in m_WrappedNetworkVariableTypes)
|
||||||
{
|
{
|
||||||
|
if (IsSpecialCaseType(type))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// If a serializable type isn't found, FallbackSerializer will be used automatically, which will
|
// If a serializable type isn't found, FallbackSerializer will be used automatically, which will
|
||||||
// call into UserNetworkVariableSerialization, giving the user a chance to define their own serializaiton
|
// call into UserNetworkVariableSerialization, giving the user a chance to define their own serializaiton
|
||||||
// for types that aren't in our official supported types list.
|
// for types that aren't in our official supported types list.
|
||||||
@@ -257,6 +275,20 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEquals_MethodRef;
|
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_UnmanagedValueEquals_MethodRef;
|
||||||
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef;
|
private MethodReference m_NetworkVariableSerializationTypes_InitializeEqualityChecker_ManagedClassEquals_MethodRef;
|
||||||
|
|
||||||
|
private MethodReference m_BytePacker_WriteValueBitPacked_Short_MethodRef;
|
||||||
|
private MethodReference m_BytePacker_WriteValueBitPacked_UShort_MethodRef;
|
||||||
|
private MethodReference m_BytePacker_WriteValueBitPacked_Int_MethodRef;
|
||||||
|
private MethodReference m_BytePacker_WriteValueBitPacked_UInt_MethodRef;
|
||||||
|
private MethodReference m_BytePacker_WriteValueBitPacked_Long_MethodRef;
|
||||||
|
private MethodReference m_BytePacker_WriteValueBitPacked_ULong_MethodRef;
|
||||||
|
|
||||||
|
private MethodReference m_ByteUnpacker_ReadValueBitPacked_Short_MethodRef;
|
||||||
|
private MethodReference m_ByteUnpacker_ReadValueBitPacked_UShort_MethodRef;
|
||||||
|
private MethodReference m_ByteUnpacker_ReadValueBitPacked_Int_MethodRef;
|
||||||
|
private MethodReference m_ByteUnpacker_ReadValueBitPacked_UInt_MethodRef;
|
||||||
|
private MethodReference m_ByteUnpacker_ReadValueBitPacked_Long_MethodRef;
|
||||||
|
private MethodReference m_ByteUnpacker_ReadValueBitPacked_ULong_MethodRef;
|
||||||
|
|
||||||
private TypeReference m_FastBufferWriter_TypeRef;
|
private TypeReference m_FastBufferWriter_TypeRef;
|
||||||
private readonly Dictionary<string, MethodReference> m_FastBufferWriter_WriteValue_MethodRefs = new Dictionary<string, MethodReference>();
|
private readonly Dictionary<string, MethodReference> m_FastBufferWriter_WriteValue_MethodRefs = new Dictionary<string, MethodReference>();
|
||||||
private readonly List<MethodReference> m_FastBufferWriter_ExtensionMethodRefs = new List<MethodReference>();
|
private readonly List<MethodReference> m_FastBufferWriter_ExtensionMethodRefs = new List<MethodReference>();
|
||||||
@@ -276,12 +308,13 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
typeof(decimal),
|
typeof(decimal),
|
||||||
typeof(double),
|
typeof(double),
|
||||||
typeof(float),
|
typeof(float),
|
||||||
typeof(int),
|
// the following types have special handling
|
||||||
|
/*typeof(int),
|
||||||
typeof(uint),
|
typeof(uint),
|
||||||
typeof(long),
|
typeof(long),
|
||||||
typeof(ulong),
|
typeof(ulong),
|
||||||
typeof(short),
|
typeof(short),
|
||||||
typeof(ushort),
|
typeof(ushort),*/
|
||||||
typeof(Vector2),
|
typeof(Vector2),
|
||||||
typeof(Vector3),
|
typeof(Vector3),
|
||||||
typeof(Vector2Int),
|
typeof(Vector2Int),
|
||||||
@@ -293,6 +326,16 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
typeof(Ray),
|
typeof(Ray),
|
||||||
typeof(Ray2D)
|
typeof(Ray2D)
|
||||||
};
|
};
|
||||||
|
internal static readonly Type[] SpecialCaseTypes = new[]
|
||||||
|
{
|
||||||
|
// the following types have special handling
|
||||||
|
typeof(int),
|
||||||
|
typeof(uint),
|
||||||
|
typeof(long),
|
||||||
|
typeof(ulong),
|
||||||
|
typeof(short),
|
||||||
|
typeof(ushort),
|
||||||
|
};
|
||||||
|
|
||||||
private const string k_Debug_LogError = nameof(Debug.LogError);
|
private const string k_Debug_LogError = nameof(Debug.LogError);
|
||||||
private const string k_NetworkManager_LocalClientId = nameof(NetworkManager.LocalClientId);
|
private const string k_NetworkManager_LocalClientId = nameof(NetworkManager.LocalClientId);
|
||||||
@@ -343,6 +386,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
TypeDefinition fastBufferWriterTypeDef = null;
|
TypeDefinition fastBufferWriterTypeDef = null;
|
||||||
TypeDefinition fastBufferReaderTypeDef = null;
|
TypeDefinition fastBufferReaderTypeDef = null;
|
||||||
TypeDefinition networkVariableSerializationTypesTypeDef = null;
|
TypeDefinition networkVariableSerializationTypesTypeDef = null;
|
||||||
|
TypeDefinition bytePackerTypeDef = null;
|
||||||
|
TypeDefinition byteUnpackerTypeDef = null;
|
||||||
foreach (var netcodeTypeDef in m_NetcodeModule.GetAllTypes())
|
foreach (var netcodeTypeDef in m_NetcodeModule.GetAllTypes())
|
||||||
{
|
{
|
||||||
if (networkManagerTypeDef == null && netcodeTypeDef.Name == nameof(NetworkManager))
|
if (networkManagerTypeDef == null && netcodeTypeDef.Name == nameof(NetworkManager))
|
||||||
@@ -398,6 +443,18 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
networkVariableSerializationTypesTypeDef = netcodeTypeDef;
|
networkVariableSerializationTypesTypeDef = netcodeTypeDef;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bytePackerTypeDef == null && netcodeTypeDef.Name == nameof(BytePacker))
|
||||||
|
{
|
||||||
|
bytePackerTypeDef = netcodeTypeDef;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byteUnpackerTypeDef == null && netcodeTypeDef.Name == nameof(ByteUnpacker))
|
||||||
|
{
|
||||||
|
byteUnpackerTypeDef = netcodeTypeDef;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var methodDef in debugTypeDef.Methods)
|
foreach (var methodDef in debugTypeDef.Methods)
|
||||||
@@ -652,6 +709,82 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var method in bytePackerTypeDef.Methods)
|
||||||
|
{
|
||||||
|
if (!method.IsStatic)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (method.Name)
|
||||||
|
{
|
||||||
|
case nameof(BytePacker.WriteValueBitPacked):
|
||||||
|
if (method.Parameters[1].ParameterType.FullName == typeof(short).FullName)
|
||||||
|
{
|
||||||
|
m_BytePacker_WriteValueBitPacked_Short_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(ushort).FullName)
|
||||||
|
{
|
||||||
|
m_BytePacker_WriteValueBitPacked_UShort_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(int).FullName)
|
||||||
|
{
|
||||||
|
m_BytePacker_WriteValueBitPacked_Int_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(uint).FullName)
|
||||||
|
{
|
||||||
|
m_BytePacker_WriteValueBitPacked_UInt_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(long).FullName)
|
||||||
|
{
|
||||||
|
m_BytePacker_WriteValueBitPacked_Long_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(ulong).FullName)
|
||||||
|
{
|
||||||
|
m_BytePacker_WriteValueBitPacked_ULong_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var method in byteUnpackerTypeDef.Methods)
|
||||||
|
{
|
||||||
|
if (!method.IsStatic)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (method.Name)
|
||||||
|
{
|
||||||
|
case nameof(ByteUnpacker.ReadValueBitPacked):
|
||||||
|
if (method.Parameters[1].ParameterType.FullName == typeof(short).MakeByRefType().FullName)
|
||||||
|
{
|
||||||
|
m_ByteUnpacker_ReadValueBitPacked_Short_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(ushort).MakeByRefType().FullName)
|
||||||
|
{
|
||||||
|
m_ByteUnpacker_ReadValueBitPacked_UShort_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(int).MakeByRefType().FullName)
|
||||||
|
{
|
||||||
|
m_ByteUnpacker_ReadValueBitPacked_Int_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(uint).MakeByRefType().FullName)
|
||||||
|
{
|
||||||
|
m_ByteUnpacker_ReadValueBitPacked_UInt_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(long).MakeByRefType().FullName)
|
||||||
|
{
|
||||||
|
m_ByteUnpacker_ReadValueBitPacked_Long_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
else if (method.Parameters[1].ParameterType.FullName == typeof(ulong).MakeByRefType().FullName)
|
||||||
|
{
|
||||||
|
m_ByteUnpacker_ReadValueBitPacked_ULong_MethodRef = m_MainModule.ImportReference(method);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1008,6 +1141,36 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
private bool GetWriteMethodForParameter(TypeReference paramType, out MethodReference methodRef)
|
private bool GetWriteMethodForParameter(TypeReference paramType, out MethodReference methodRef)
|
||||||
{
|
{
|
||||||
|
if (paramType.FullName == typeof(short).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_BytePacker_WriteValueBitPacked_Short_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(ushort).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_BytePacker_WriteValueBitPacked_UShort_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(int).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_BytePacker_WriteValueBitPacked_Int_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(uint).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_BytePacker_WriteValueBitPacked_UInt_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(long).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_BytePacker_WriteValueBitPacked_Long_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(ulong).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_BytePacker_WriteValueBitPacked_ULong_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
var assemblyQualifiedName = paramType.FullName + ", " + paramType.Resolve().Module.Assembly.FullName;
|
var assemblyQualifiedName = paramType.FullName + ", " + paramType.Resolve().Module.Assembly.FullName;
|
||||||
var foundMethodRef = m_FastBufferWriter_WriteValue_MethodRefs.TryGetValue(assemblyQualifiedName, out methodRef);
|
var foundMethodRef = m_FastBufferWriter_WriteValue_MethodRefs.TryGetValue(assemblyQualifiedName, out methodRef);
|
||||||
|
|
||||||
@@ -1154,6 +1317,36 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
private bool GetReadMethodForParameter(TypeReference paramType, out MethodReference methodRef)
|
private bool GetReadMethodForParameter(TypeReference paramType, out MethodReference methodRef)
|
||||||
{
|
{
|
||||||
|
if (paramType.FullName == typeof(short).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_ByteUnpacker_ReadValueBitPacked_Short_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(ushort).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_ByteUnpacker_ReadValueBitPacked_UShort_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(int).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_ByteUnpacker_ReadValueBitPacked_Int_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(uint).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_ByteUnpacker_ReadValueBitPacked_UInt_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(long).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_ByteUnpacker_ReadValueBitPacked_Long_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (paramType.FullName == typeof(ulong).FullName)
|
||||||
|
{
|
||||||
|
methodRef = m_ByteUnpacker_ReadValueBitPacked_ULong_MethodRef;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
var assemblyQualifiedName = paramType.FullName + ", " + paramType.Resolve().Module.Assembly.FullName;
|
var assemblyQualifiedName = paramType.FullName + ", " + paramType.Resolve().Module.Assembly.FullName;
|
||||||
|
|
||||||
var foundMethodRef = m_FastBufferReader_ReadValue_MethodRefs.TryGetValue(assemblyQualifiedName, out methodRef);
|
var foundMethodRef = m_FastBufferReader_ReadValue_MethodRefs.TryGetValue(assemblyQualifiedName, out methodRef);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 2c61e8fe9a68a486fbbc3128d233ded2
|
guid: 52153943c346dd04e8712ab540ab9c22
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
39
Editor/Configuration/NetcodeForGameObjectsSettings.cs
Normal file
39
Editor/Configuration/NetcodeForGameObjectsSettings.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Editor.Configuration
|
||||||
|
{
|
||||||
|
internal class NetcodeForGameObjectsSettings
|
||||||
|
{
|
||||||
|
internal const string AutoAddNetworkObjectIfNoneExists = "AutoAdd-NetworkObject-When-None-Exist";
|
||||||
|
internal const string InstallMultiplayerToolsTipDismissedPlayerPrefKey = "Netcode_Tip_InstallMPTools_Dismissed";
|
||||||
|
|
||||||
|
internal static int GetNetcodeInstallMultiplayerToolTips()
|
||||||
|
{
|
||||||
|
if (EditorPrefs.HasKey(InstallMultiplayerToolsTipDismissedPlayerPrefKey))
|
||||||
|
{
|
||||||
|
return EditorPrefs.GetInt(InstallMultiplayerToolsTipDismissedPlayerPrefKey);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SetNetcodeInstallMultiplayerToolTips(int toolTipPrefSetting)
|
||||||
|
{
|
||||||
|
EditorPrefs.SetInt(InstallMultiplayerToolsTipDismissedPlayerPrefKey, toolTipPrefSetting);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool GetAutoAddNetworkObjectSetting()
|
||||||
|
{
|
||||||
|
if (EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists))
|
||||||
|
{
|
||||||
|
return EditorPrefs.GetBool(AutoAddNetworkObjectIfNoneExists);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SetAutoAddNetworkObjectSetting(bool autoAddSetting)
|
||||||
|
{
|
||||||
|
EditorPrefs.SetBool(AutoAddNetworkObjectIfNoneExists, autoAddSetting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 3ada9e8fd5bf94b1f9a6a21531c8a3ee
|
guid: 2f9c9b10bc41a0e46ab71324dd0ac6e1
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
94
Editor/Configuration/NetcodeSettingsProvider.cs
Normal file
94
Editor/Configuration/NetcodeSettingsProvider.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Editor.Configuration
|
||||||
|
{
|
||||||
|
internal static class NetcodeSettingsProvider
|
||||||
|
{
|
||||||
|
[SettingsProvider]
|
||||||
|
public static SettingsProvider CreateNetcodeSettingsProvider()
|
||||||
|
{
|
||||||
|
// First parameter is the path in the Settings window.
|
||||||
|
// Second parameter is the scope of this setting: it only appears in the Settings window for the Project scope.
|
||||||
|
var provider = new SettingsProvider("Project/NetcodeForGameObjects", SettingsScope.Project)
|
||||||
|
{
|
||||||
|
label = "Netcode for GameObjects",
|
||||||
|
keywords = new[] { "netcode", "editor" },
|
||||||
|
guiHandler = OnGuiHandler,
|
||||||
|
};
|
||||||
|
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static NetcodeSettingsLabel NetworkObjectsSectionLabel = new NetcodeSettingsLabel("NetworkObject Helper Settings", 20);
|
||||||
|
internal static NetcodeSettingsToggle AutoAddNetworkObjectToggle = new NetcodeSettingsToggle("Auto-Add NetworkObjects", "When enabled, NetworkObjects are automatically added to GameObjects when NetworkBehaviours are added first.", 20);
|
||||||
|
internal static NetcodeSettingsLabel MultiplayerToolsLabel = new NetcodeSettingsLabel("Multiplayer Tools", 20);
|
||||||
|
internal static NetcodeSettingsToggle MultiplayerToolTipStatusToggle = new NetcodeSettingsToggle("Multiplayer Tools Install Reminder", "When enabled, the NetworkManager will display " +
|
||||||
|
"the notification to install the multiplayer tools package.", 20);
|
||||||
|
|
||||||
|
private static void OnGuiHandler(string obj)
|
||||||
|
{
|
||||||
|
var autoAddNetworkObjectSetting = NetcodeForGameObjectsSettings.GetAutoAddNetworkObjectSetting();
|
||||||
|
var multiplayerToolsTipStatus = NetcodeForGameObjectsSettings.GetNetcodeInstallMultiplayerToolTips() == 0;
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
NetworkObjectsSectionLabel.DrawLabel();
|
||||||
|
autoAddNetworkObjectSetting = AutoAddNetworkObjectToggle.DrawToggle(autoAddNetworkObjectSetting);
|
||||||
|
MultiplayerToolsLabel.DrawLabel();
|
||||||
|
multiplayerToolsTipStatus = MultiplayerToolTipStatusToggle.DrawToggle(multiplayerToolsTipStatus);
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
NetcodeForGameObjectsSettings.SetAutoAddNetworkObjectSetting(autoAddNetworkObjectSetting);
|
||||||
|
NetcodeForGameObjectsSettings.SetNetcodeInstallMultiplayerToolTips(multiplayerToolsTipStatus ? 0 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NetcodeSettingsLabel : NetcodeGUISettings
|
||||||
|
{
|
||||||
|
private string m_LabelContent;
|
||||||
|
|
||||||
|
public void DrawLabel()
|
||||||
|
{
|
||||||
|
EditorGUIUtility.labelWidth = m_LabelSize;
|
||||||
|
GUILayout.Label(m_LabelContent, EditorStyles.boldLabel, m_LayoutWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetcodeSettingsLabel(string labelText, float layoutOffset = 0.0f)
|
||||||
|
{
|
||||||
|
m_LabelContent = labelText;
|
||||||
|
AdjustLableSize(labelText, layoutOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NetcodeSettingsToggle : NetcodeGUISettings
|
||||||
|
{
|
||||||
|
private GUIContent m_ToggleContent;
|
||||||
|
|
||||||
|
public bool DrawToggle(bool currentSetting)
|
||||||
|
{
|
||||||
|
EditorGUIUtility.labelWidth = m_LabelSize;
|
||||||
|
return EditorGUILayout.Toggle(m_ToggleContent, currentSetting, m_LayoutWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetcodeSettingsToggle(string labelText, string toolTip, float layoutOffset)
|
||||||
|
{
|
||||||
|
AdjustLableSize(labelText, layoutOffset);
|
||||||
|
m_ToggleContent = new GUIContent(labelText, toolTip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NetcodeGUISettings
|
||||||
|
{
|
||||||
|
private const float k_MaxLabelWidth = 450f;
|
||||||
|
protected float m_LabelSize { get; private set; }
|
||||||
|
|
||||||
|
protected GUILayoutOption m_LayoutWidth { get; private set; }
|
||||||
|
|
||||||
|
protected void AdjustLableSize(string labelText, float offset = 0.0f)
|
||||||
|
{
|
||||||
|
m_LabelSize = Mathf.Min(k_MaxLabelWidth, EditorStyles.label.CalcSize(new GUIContent(labelText)).x);
|
||||||
|
m_LayoutWidth = GUILayout.Width(m_LabelSize + offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
11
Editor/Configuration/NetcodeSettingsProvider.cs.meta
Normal file
11
Editor/Configuration/NetcodeSettingsProvider.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6b373a89fcbd41444a97ebd1798b326f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -3,9 +3,13 @@ using System.Collections.Generic;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
using Unity.Netcode.Editor.Configuration;
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
namespace Unity.Netcode.Editor
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="CustomEditor"/> for <see cref="NetworkBehaviour"/>
|
||||||
|
/// </summary>
|
||||||
[CustomEditor(typeof(NetworkBehaviour), true)]
|
[CustomEditor(typeof(NetworkBehaviour), true)]
|
||||||
[CanEditMultipleObjects]
|
[CanEditMultipleObjects]
|
||||||
public class NetworkBehaviourEditor : UnityEditor.Editor
|
public class NetworkBehaviourEditor : UnityEditor.Editor
|
||||||
@@ -33,8 +37,8 @@ namespace Unity.Netcode.Editor
|
|||||||
var ft = fields[i].FieldType;
|
var ft = fields[i].FieldType;
|
||||||
if (ft.IsGenericType && ft.GetGenericTypeDefinition() == typeof(NetworkVariable<>) && !fields[i].IsDefined(typeof(HideInInspector), true))
|
if (ft.IsGenericType && ft.GetGenericTypeDefinition() == typeof(NetworkVariable<>) && !fields[i].IsDefined(typeof(HideInInspector), true))
|
||||||
{
|
{
|
||||||
m_NetworkVariableNames.Add(fields[i].Name);
|
m_NetworkVariableNames.Add(ObjectNames.NicifyVariableName(fields[i].Name));
|
||||||
m_NetworkVariableFields.Add(fields[i].Name, fields[i]);
|
m_NetworkVariableFields.Add(ObjectNames.NicifyVariableName(fields[i].Name), fields[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,8 +234,6 @@ namespace Unity.Netcode.Editor
|
|||||||
CheckForNetworkObject((target as NetworkBehaviour).gameObject);
|
CheckForNetworkObject((target as NetworkBehaviour).gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal const string AutoAddNetworkObjectIfNoneExists = "AutoAdd-NetworkObject-When-None-Exist";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recursively finds the root parent of a <see cref="Transform"/>
|
/// Recursively finds the root parent of a <see cref="Transform"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -308,7 +310,7 @@ namespace Unity.Netcode.Editor
|
|||||||
// and the user has already turned "Auto-Add NetworkObject" on when first notified about the requirement
|
// and the user has already turned "Auto-Add NetworkObject" on when first notified about the requirement
|
||||||
// then just send a reminder to the user why the NetworkObject they just deleted seemingly "re-appeared"
|
// then just send a reminder to the user why the NetworkObject they just deleted seemingly "re-appeared"
|
||||||
// again.
|
// again.
|
||||||
if (networkObjectRemoved && EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists) && EditorPrefs.GetBool(AutoAddNetworkObjectIfNoneExists))
|
if (networkObjectRemoved && NetcodeForGameObjectsSettings.GetAutoAddNetworkObjectSetting())
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"{gameObject.name} still has {nameof(NetworkBehaviour)}s and Auto-Add NetworkObjects is enabled. A NetworkObject is being added back to {gameObject.name}.");
|
Debug.LogWarning($"{gameObject.name} still has {nameof(NetworkBehaviour)}s and Auto-Add NetworkObjects is enabled. A NetworkObject is being added back to {gameObject.name}.");
|
||||||
Debug.Log($"To reset Auto-Add NetworkObjects: Select the Netcode->General->Reset Auto-Add NetworkObject menu item.");
|
Debug.Log($"To reset Auto-Add NetworkObjects: Select the Netcode->General->Reset Auto-Add NetworkObject menu item.");
|
||||||
@@ -317,7 +319,7 @@ namespace Unity.Netcode.Editor
|
|||||||
// Notify and provide the option to add it one time, always add a NetworkObject, or do nothing and let the user manually add it
|
// Notify and provide the option to add it one time, always add a NetworkObject, or do nothing and let the user manually add it
|
||||||
if (EditorUtility.DisplayDialog($"{nameof(NetworkBehaviour)}s require a {nameof(NetworkObject)}",
|
if (EditorUtility.DisplayDialog($"{nameof(NetworkBehaviour)}s require a {nameof(NetworkObject)}",
|
||||||
$"{gameObject.name} does not have a {nameof(NetworkObject)} component. Would you like to add one now?", "Yes", "No (manually add it)",
|
$"{gameObject.name} does not have a {nameof(NetworkObject)} component. Would you like to add one now?", "Yes", "No (manually add it)",
|
||||||
DialogOptOutDecisionType.ForThisMachine, AutoAddNetworkObjectIfNoneExists))
|
DialogOptOutDecisionType.ForThisMachine, NetcodeForGameObjectsSettings.AutoAddNetworkObjectIfNoneExists))
|
||||||
{
|
{
|
||||||
gameObject.AddComponent<NetworkObject>();
|
gameObject.AddComponent<NetworkObject>();
|
||||||
var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||||
@@ -327,20 +329,5 @@ namespace Unity.Netcode.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This allows users to reset the Auto-Add NetworkObject preference
|
|
||||||
/// so the next time they add a NetworkBehaviour to a GameObject without
|
|
||||||
/// a NetworkObject it will display the dialog box again and not
|
|
||||||
/// automatically add a NetworkObject.
|
|
||||||
/// </summary>
|
|
||||||
[MenuItem("Netcode/General/Reset Auto-Add NetworkObject", false, 1)]
|
|
||||||
private static void ResetMultiplayerToolsTipStatus()
|
|
||||||
{
|
|
||||||
if (EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists))
|
|
||||||
{
|
|
||||||
EditorPrefs.SetBool(AutoAddNetworkObjectIfNoneExists, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEditorInternal;
|
using UnityEditorInternal;
|
||||||
|
using Unity.Netcode.Editor.Configuration;
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
namespace Unity.Netcode.Editor
|
||||||
{
|
{
|
||||||
@@ -14,7 +15,6 @@ namespace Unity.Netcode.Editor
|
|||||||
[CanEditMultipleObjects]
|
[CanEditMultipleObjects]
|
||||||
public class NetworkManagerEditor : UnityEditor.Editor
|
public class NetworkManagerEditor : UnityEditor.Editor
|
||||||
{
|
{
|
||||||
internal const string InstallMultiplayerToolsTipDismissedPlayerPrefKey = "Netcode_Tip_InstallMPTools_Dismissed";
|
|
||||||
private static GUIStyle s_CenteredWordWrappedLabelStyle;
|
private static GUIStyle s_CenteredWordWrappedLabelStyle;
|
||||||
private static GUIStyle s_HelpBoxStyle;
|
private static GUIStyle s_HelpBoxStyle;
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ namespace Unity.Netcode.Editor
|
|||||||
const string targetUrl = "https://docs-multiplayer.unity3d.com/netcode/current/tools/install-tools";
|
const string targetUrl = "https://docs-multiplayer.unity3d.com/netcode/current/tools/install-tools";
|
||||||
const string infoIconName = "console.infoicon";
|
const string infoIconName = "console.infoicon";
|
||||||
|
|
||||||
if (PlayerPrefs.GetInt(InstallMultiplayerToolsTipDismissedPlayerPrefKey, 0) != 0)
|
if (NetcodeForGameObjectsSettings.GetNetcodeInstallMultiplayerToolTips() != 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -405,7 +405,7 @@ namespace Unity.Netcode.Editor
|
|||||||
GUILayout.FlexibleSpace();
|
GUILayout.FlexibleSpace();
|
||||||
if (GUILayout.Button(dismissButtonText, dismissButtonStyle, GUILayout.ExpandWidth(false)))
|
if (GUILayout.Button(dismissButtonText, dismissButtonStyle, GUILayout.ExpandWidth(false)))
|
||||||
{
|
{
|
||||||
PlayerPrefs.SetInt(InstallMultiplayerToolsTipDismissedPlayerPrefKey, 1);
|
NetcodeForGameObjectsSettings.SetNetcodeInstallMultiplayerToolTips(1);
|
||||||
}
|
}
|
||||||
EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link);
|
EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link);
|
||||||
GUILayout.FlexibleSpace();
|
GUILayout.FlexibleSpace();
|
||||||
|
|||||||
@@ -65,7 +65,11 @@ namespace Unity.Netcode.Editor
|
|||||||
var scenesList = EditorBuildSettings.scenes.ToList();
|
var scenesList = EditorBuildSettings.scenes.ToList();
|
||||||
var activeScene = SceneManager.GetActiveScene();
|
var activeScene = SceneManager.GetActiveScene();
|
||||||
var isSceneInBuildSettings = scenesList.Count((c) => c.path == activeScene.path) == 1;
|
var isSceneInBuildSettings = scenesList.Count((c) => c.path == activeScene.path) == 1;
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkManager = Object.FindFirstObjectByType<NetworkManager>();
|
||||||
|
#else
|
||||||
var networkManager = Object.FindObjectOfType<NetworkManager>();
|
var networkManager = Object.FindObjectOfType<NetworkManager>();
|
||||||
|
#endif
|
||||||
if (!isSceneInBuildSettings && networkManager != null)
|
if (!isSceneInBuildSettings && networkManager != null)
|
||||||
{
|
{
|
||||||
if (networkManager.NetworkConfig != null && networkManager.NetworkConfig.EnableSceneManagement)
|
if (networkManager.NetworkConfig != null && networkManager.NetworkConfig.EnableSceneManagement)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace Unity.Netcode
|
|||||||
Client = 2
|
Client = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NetworkBehaviourILPP will override this in derived classes to return the name of the concrete type
|
// NetworkBehaviourILPP will override this in derived classes to return the name of the concrete type
|
||||||
internal virtual string __getTypeName() => nameof(NetworkBehaviour);
|
internal virtual string __getTypeName() => nameof(NetworkBehaviour);
|
||||||
|
|
||||||
@@ -286,7 +287,18 @@ namespace Unity.Netcode
|
|||||||
/// Gets the NetworkManager that owns this NetworkBehaviour instance
|
/// Gets the NetworkManager that owns this NetworkBehaviour instance
|
||||||
/// See note around `NetworkObject` for how there is a chicken / egg problem when we are not initialized
|
/// See note around `NetworkObject` for how there is a chicken / egg problem when we are not initialized
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NetworkManager NetworkManager => NetworkObject.NetworkManager;
|
public NetworkManager NetworkManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (NetworkObject?.NetworkManager != null)
|
||||||
|
{
|
||||||
|
return NetworkObject?.NetworkManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NetworkManager.Singleton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If a NetworkObject is assigned, it will return whether or not this NetworkObject
|
/// If a NetworkObject is assigned, it will return whether or not this NetworkObject
|
||||||
@@ -335,23 +347,29 @@ namespace Unity.Netcode
|
|||||||
m_NetworkObject.NetworkManager.IsServer;
|
m_NetworkObject.NetworkManager.IsServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the NetworkObject that owns this NetworkBehaviour instance
|
|
||||||
/// TODO: this needs an overhaul. It's expensive, it's ja little naive in how it looks for networkObject in
|
/// TODO: this needs an overhaul. It's expensive, it's ja little naive in how it looks for networkObject in
|
||||||
/// its parent and worst, it creates a puzzle if you are a NetworkBehaviour wanting to see if you're live or not
|
/// its parent and worst, it creates a puzzle if you are a NetworkBehaviour wanting to see if you're live or not
|
||||||
/// (e.g. editor code). All you want to do is find out if NetworkManager is null, but to do that you
|
/// (e.g. editor code). All you want to do is find out if NetworkManager is null, but to do that you
|
||||||
/// need NetworkObject, but if you try and grab NetworkObject and NetworkManager isn't up you'll get
|
/// need NetworkObject, but if you try and grab NetworkObject and NetworkManager isn't up you'll get
|
||||||
/// the warning below. This is why IsBehaviourEditable had to be created. Matt was going to re-do
|
/// the warning below. This is why IsBehaviourEditable had to be created. Matt was going to re-do
|
||||||
/// how NetworkObject works but it was close to the release and too risky to change
|
/// how NetworkObject works but it was close to the release and too risky to change
|
||||||
///
|
/// <summary>
|
||||||
|
/// Gets the NetworkObject that owns this NetworkBehaviour instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NetworkObject NetworkObject
|
public NetworkObject NetworkObject
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (m_NetworkObject == null)
|
try
|
||||||
{
|
{
|
||||||
m_NetworkObject = GetComponentInParent<NetworkObject>();
|
if (m_NetworkObject == null)
|
||||||
|
{
|
||||||
|
m_NetworkObject = GetComponentInParent<NetworkObject>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShutdownInProgress check:
|
// ShutdownInProgress check:
|
||||||
@@ -712,7 +730,7 @@ namespace Unity.Netcode
|
|||||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
||||||
using (tmpWriter)
|
using (tmpWriter)
|
||||||
{
|
{
|
||||||
message.Serialize(tmpWriter);
|
message.Serialize(tmpWriter, message.Version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -745,6 +763,14 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronizes by setting only the NetworkVariable field values that the client has permission to read.
|
||||||
|
/// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject)
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled each NetworkVariable field will be preceded
|
||||||
|
/// by the number of bytes written for that specific field.
|
||||||
|
/// </remarks>
|
||||||
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
|
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
|
||||||
{
|
{
|
||||||
if (NetworkVariableFields.Count == 0)
|
if (NetworkVariableFields.Count == 0)
|
||||||
@@ -754,27 +780,47 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||||
{
|
{
|
||||||
bool canClientRead = NetworkVariableFields[j].CanClientRead(targetClientId);
|
|
||||||
|
|
||||||
if (canClientRead)
|
if (NetworkVariableFields[j].CanClientRead(targetClientId))
|
||||||
{
|
{
|
||||||
var writePos = writer.Position;
|
if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
writer.WriteValueSafe((ushort)0);
|
{
|
||||||
var startPos = writer.Position;
|
var writePos = writer.Position;
|
||||||
NetworkVariableFields[j].WriteField(writer);
|
// Note: This value can't be packed because we don't know how large it will be in advance
|
||||||
var size = writer.Position - startPos;
|
// we reserve space for it, then write the data, then come back and fill in the space
|
||||||
writer.Seek(writePos);
|
// to pack here, we'd have to write data to a temporary buffer and copy it in - which
|
||||||
writer.WriteValueSafe((ushort)size);
|
// isn't worth possibly saving one byte if and only if the data is less than 63 bytes long...
|
||||||
writer.Seek(startPos + size);
|
// The way we do packing, any value > 63 in a ushort will use the full 2 bytes to represent.
|
||||||
|
writer.WriteValueSafe((ushort)0);
|
||||||
|
var startPos = writer.Position;
|
||||||
|
NetworkVariableFields[j].WriteField(writer);
|
||||||
|
var size = writer.Position - startPos;
|
||||||
|
writer.Seek(writePos);
|
||||||
|
writer.WriteValueSafe((ushort)size);
|
||||||
|
writer.Seek(startPos + size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetworkVariableFields[j].WriteField(writer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else // Only if EnsureNetworkVariableLengthSafety, otherwise just skip
|
||||||
|
if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe((ushort)0);
|
writer.WriteValueSafe((ushort)0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetNetworkVariableData(FastBufferReader reader)
|
/// <summary>
|
||||||
|
/// Synchronizes by setting only the NetworkVariable field values that the client has permission to read.
|
||||||
|
/// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject)
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled each NetworkVariable field will be preceded
|
||||||
|
/// by the number of bytes written for that specific field.
|
||||||
|
/// </remarks>
|
||||||
|
internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
|
||||||
{
|
{
|
||||||
if (NetworkVariableFields.Count == 0)
|
if (NetworkVariableFields.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -783,13 +829,23 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out ushort varSize);
|
var varSize = (ushort)0;
|
||||||
if (varSize == 0)
|
var readStartPos = 0;
|
||||||
|
if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
|
{
|
||||||
|
reader.ReadValueSafe(out varSize);
|
||||||
|
if (varSize == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
readStartPos = reader.Position;
|
||||||
|
}
|
||||||
|
else // If the client cannot read this field, then skip it
|
||||||
|
if (!NetworkVariableFields[j].CanClientRead(clientId))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var readStartPos = reader.Position;
|
|
||||||
NetworkVariableFields[j].ReadField(reader);
|
NetworkVariableFields[j].ReadField(reader);
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
@@ -826,6 +882,138 @@ namespace Unity.Netcode
|
|||||||
return NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkId, out NetworkObject networkObject) ? networkObject : null;
|
return NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkId, out NetworkObject networkObject) ? networkObject : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this method if your derived NetworkBehaviour requires custom synchronization data.
|
||||||
|
/// Note: Use of this method is only for the initial client synchronization of NetworkBehaviours
|
||||||
|
/// and will increase the payload size for client synchronization and dynamically spawned
|
||||||
|
/// <see cref="NetworkObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// When serializing (writing) this will be invoked during the client synchronization period and
|
||||||
|
/// when spawning new NetworkObjects.
|
||||||
|
/// When deserializing (reading), this will be invoked prior to the NetworkBehaviour's associated
|
||||||
|
/// NetworkObject being spawned.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="serializer">The serializer to use to read and write the data.</param>
|
||||||
|
/// <typeparam name="T">
|
||||||
|
/// Either BufferSerializerReader or BufferSerializerWriter, depending whether the serializer
|
||||||
|
/// is in read mode or write mode.
|
||||||
|
/// </typeparam>
|
||||||
|
protected virtual void OnSynchronize<T>(ref BufferSerializer<T> serializer) where T : IReaderWriter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal method that determines if a NetworkBehaviour has additional synchronization data to
|
||||||
|
/// be synchronized when first instantiated prior to its associated NetworkObject being spawned.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This includes try-catch blocks to recover from exceptions that might occur and continue to
|
||||||
|
/// synchronize any remaining NetworkBehaviours.
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns>true if it wrote synchronization data and false if it did not</returns>
|
||||||
|
internal bool Synchronize<T>(ref BufferSerializer<T> serializer) where T : IReaderWriter
|
||||||
|
{
|
||||||
|
if (serializer.IsWriter)
|
||||||
|
{
|
||||||
|
// Get the writer to handle seeking and determining how many bytes were written
|
||||||
|
var writer = serializer.GetFastBufferWriter();
|
||||||
|
// Save our position before we attempt to write anything so we can seek back to it (i.e. error occurs)
|
||||||
|
var positionBeforeWrite = writer.Position;
|
||||||
|
writer.WriteValueSafe(NetworkBehaviourId);
|
||||||
|
|
||||||
|
// Save our position where we will write the final size being written so we can skip over it in the
|
||||||
|
// event an exception occurs when deserializing.
|
||||||
|
var sizePosition = writer.Position;
|
||||||
|
writer.WriteValueSafe((ushort)0);
|
||||||
|
|
||||||
|
// Save our position before synchronizing to determine how much was written
|
||||||
|
var positionBeforeSynchronize = writer.Position;
|
||||||
|
var threwException = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OnSynchronize(ref serializer);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
threwException = true;
|
||||||
|
if (NetworkManager.LogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"{name} threw an exception during synchronization serialization, this {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!");
|
||||||
|
if (NetworkManager.LogLevel == LogLevel.Developer)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"{ex.Message}\n {ex.StackTrace}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var finalPosition = writer.Position;
|
||||||
|
|
||||||
|
// If we wrote nothing then skip writing anything for this NetworkBehaviour
|
||||||
|
if (finalPosition == positionBeforeSynchronize || threwException)
|
||||||
|
{
|
||||||
|
writer.Seek(positionBeforeWrite);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write the number of bytes serialized to handle exceptions on the deserialization side
|
||||||
|
var bytesWritten = finalPosition - positionBeforeSynchronize;
|
||||||
|
writer.Seek(sizePosition);
|
||||||
|
writer.WriteValueSafe((ushort)bytesWritten);
|
||||||
|
writer.Seek(finalPosition);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var reader = serializer.GetFastBufferReader();
|
||||||
|
// We will always read the expected byte count
|
||||||
|
reader.ReadValueSafe(out ushort expectedBytesToRead);
|
||||||
|
|
||||||
|
// Save our position before we begin synchronization deserialization
|
||||||
|
var positionBeforeSynchronize = reader.Position;
|
||||||
|
var synchronizationError = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Invoke synchronization
|
||||||
|
OnSynchronize(ref serializer);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (NetworkManager.LogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"{name} threw an exception during synchronization deserialization, this {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!");
|
||||||
|
if (NetworkManager.LogLevel == LogLevel.Developer)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"{ex.Message}\n {ex.StackTrace}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synchronizationError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalBytesRead = reader.Position - positionBeforeSynchronize;
|
||||||
|
if (totalBytesRead != expectedBytesToRead)
|
||||||
|
{
|
||||||
|
if (NetworkManager.LogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"{name} read {totalBytesRead} bytes but was expected to read {expectedBytesToRead} bytes during synchronization deserialization! This {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!");
|
||||||
|
}
|
||||||
|
synchronizationError = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over the entry if deserialization fails
|
||||||
|
if (synchronizationError)
|
||||||
|
{
|
||||||
|
var skipToPosition = positionBeforeSynchronize + expectedBytesToRead;
|
||||||
|
reader.Seek(skipToPosition);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when the <see cref="GameObject"/> the <see cref="NetworkBehaviour"/> is attached to.
|
/// Invoked when the <see cref="GameObject"/> the <see cref="NetworkBehaviour"/> is attached to.
|
||||||
/// NOTE: If you override this, you will want to always invoke this base class version of this
|
/// NOTE: If you override this, you will want to always invoke this base class version of this
|
||||||
|
|||||||
@@ -86,6 +86,12 @@ namespace Unity.Netcode
|
|||||||
private bool m_ShuttingDown;
|
private bool m_ShuttingDown;
|
||||||
private bool m_StopProcessingMessages;
|
private bool m_StopProcessingMessages;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When disconnected from the server, the server may send a reason. If a reason was sent, this property will
|
||||||
|
/// tell client code what the reason was. It should be queried after the OnClientDisconnectCallback is called
|
||||||
|
/// </summary>
|
||||||
|
public string DisconnectReason { get; internal set; }
|
||||||
|
|
||||||
private class NetworkManagerHooks : INetworkHooks
|
private class NetworkManagerHooks : INetworkHooks
|
||||||
{
|
{
|
||||||
private NetworkManager m_NetworkManager;
|
private NetworkManager m_NetworkManager;
|
||||||
@@ -443,6 +449,12 @@ namespace Unity.Netcode
|
|||||||
/// If the Approval decision cannot be made immediately, the client code can set Pending to true, keep a reference to the ConnectionApprovalResponse object and write to it later. Client code must exercise care to setting all the members to the value it wants before marking Pending to false, to indicate completion. If the field is set as Pending = true, we'll monitor the object until it gets set to not pending anymore and use the parameters then.
|
/// If the Approval decision cannot be made immediately, the client code can set Pending to true, keep a reference to the ConnectionApprovalResponse object and write to it later. Client code must exercise care to setting all the members to the value it wants before marking Pending to false, to indicate completion. If the field is set as Pending = true, we'll monitor the object until it gets set to not pending anymore and use the parameters then.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Pending;
|
public bool Pending;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optional reason. If Approved is false, this reason will be sent to the client so they know why they
|
||||||
|
/// were not approved.
|
||||||
|
/// </summary>
|
||||||
|
public string Reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -889,6 +901,7 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisconnectReason = string.Empty;
|
||||||
IsApproved = false;
|
IsApproved = false;
|
||||||
|
|
||||||
ComponentFactory.SetDefaults();
|
ComponentFactory.SetDefaults();
|
||||||
@@ -1181,6 +1194,11 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
|
SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
|
||||||
|
|
||||||
|
// This assures that any in-scene placed NetworkObject is spawned and
|
||||||
|
// any associated NetworkBehaviours' netcode related properties are
|
||||||
|
// set prior to invoking OnClientConnected.
|
||||||
|
InvokeOnClientConnectedCallback(LocalClientId);
|
||||||
|
|
||||||
OnServerStarted?.Invoke();
|
OnServerStarted?.Invoke();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1341,6 +1359,7 @@ namespace Unity.Netcode
|
|||||||
private void DisconnectRemoteClient(ulong clientId)
|
private void DisconnectRemoteClient(ulong clientId)
|
||||||
{
|
{
|
||||||
var transportId = ClientIdToTransportId(clientId);
|
var transportId = ClientIdToTransportId(clientId);
|
||||||
|
MessagingSystem.ProcessSendQueues();
|
||||||
NetworkConfig.NetworkTransport.DisconnectRemoteClient(transportId);
|
NetworkConfig.NetworkTransport.DisconnectRemoteClient(transportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1421,7 +1440,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsClient && IsConnectedClient)
|
if (IsClient && IsListening)
|
||||||
{
|
{
|
||||||
// Client only, send disconnect to server
|
// Client only, send disconnect to server
|
||||||
NetworkConfig.NetworkTransport.DisconnectLocalClient();
|
NetworkConfig.NetworkTransport.DisconnectLocalClient();
|
||||||
@@ -1579,6 +1598,7 @@ namespace Unity.Netcode
|
|||||||
} while (IsListening && networkEvent != NetworkEvent.Nothing);
|
} while (IsListening && networkEvent != NetworkEvent.Nothing);
|
||||||
|
|
||||||
MessagingSystem.ProcessIncomingMessageQueue();
|
MessagingSystem.ProcessIncomingMessageQueue();
|
||||||
|
MessagingSystem.CleanupDisconnectedClients();
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
s_TransportPoll.End();
|
s_TransportPoll.End();
|
||||||
@@ -1660,7 +1680,23 @@ namespace Unity.Netcode
|
|||||||
ShouldSendConnectionData = NetworkConfig.ConnectionApproval,
|
ShouldSendConnectionData = NetworkConfig.ConnectionApproval,
|
||||||
ConnectionData = NetworkConfig.ConnectionData
|
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);
|
SendMessage(ref message, NetworkDelivery.ReliableSequenced, ServerClientId);
|
||||||
|
message.MessageVersions.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerator ApprovalTimeout(ulong clientId)
|
private IEnumerator ApprovalTimeout(ulong clientId)
|
||||||
@@ -1821,7 +1857,18 @@ namespace Unity.Netcode
|
|||||||
NetworkLog.LogInfo($"Disconnect Event From {clientId}");
|
NetworkLog.LogInfo($"Disconnect Event From {clientId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
OnClientDisconnectCallback?.Invoke(clientId);
|
// Process the incoming message queue so that we get everything from the server disconnecting us
|
||||||
|
// or, if we are the server, so we got everything from that client.
|
||||||
|
MessagingSystem.ProcessIncomingMessageQueue();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OnClientDisconnectCallback?.Invoke(clientId);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
if (IsServer)
|
if (IsServer)
|
||||||
{
|
{
|
||||||
@@ -1987,12 +2034,31 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="clientId">The ClientId to disconnect</param>
|
/// <param name="clientId">The ClientId to disconnect</param>
|
||||||
public void DisconnectClient(ulong clientId)
|
public void DisconnectClient(ulong clientId)
|
||||||
|
{
|
||||||
|
DisconnectClient(clientId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnects the remote client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">The ClientId to disconnect</param>
|
||||||
|
/// <param name="reason">Disconnection reason. If set, client will receive a DisconnectReasonMessage and have the
|
||||||
|
/// reason available in the NetworkManager.DisconnectReason property</param>
|
||||||
|
public void DisconnectClient(ulong clientId, string reason)
|
||||||
{
|
{
|
||||||
if (!IsServer)
|
if (!IsServer)
|
||||||
{
|
{
|
||||||
throw new NotServerException($"Only server can disconnect remote clients. Please use `{nameof(Shutdown)}()` instead.");
|
throw new NotServerException($"Only server can disconnect remote clients. Please use `{nameof(Shutdown)}()` instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(reason))
|
||||||
|
{
|
||||||
|
var disconnectReason = new DisconnectReasonMessage();
|
||||||
|
disconnectReason.Reason = reason;
|
||||||
|
SendMessage(ref disconnectReason, NetworkDelivery.Reliable, clientId);
|
||||||
|
}
|
||||||
|
MessagingSystem.ProcessSendQueues();
|
||||||
|
|
||||||
OnClientDisconnectFromServer(clientId);
|
OnClientDisconnectFromServer(clientId);
|
||||||
DisconnectRemoteClient(clientId);
|
DisconnectRemoteClient(clientId);
|
||||||
}
|
}
|
||||||
@@ -2137,14 +2203,11 @@ namespace Unity.Netcode
|
|||||||
// Generate a SceneObject for the player object to spawn
|
// Generate a SceneObject for the player object to spawn
|
||||||
var sceneObject = new NetworkObject.SceneObject
|
var sceneObject = new NetworkObject.SceneObject
|
||||||
{
|
{
|
||||||
Header = new NetworkObject.SceneObject.HeaderData
|
OwnerClientId = ownerClientId,
|
||||||
{
|
IsPlayerObject = true,
|
||||||
IsPlayerObject = true,
|
IsSceneObject = false,
|
||||||
OwnerClientId = ownerClientId,
|
HasTransform = true,
|
||||||
IsSceneObject = false,
|
Hash = playerPrefabHash,
|
||||||
HasTransform = true,
|
|
||||||
Hash = playerPrefabHash,
|
|
||||||
},
|
|
||||||
TargetClientId = ownerClientId,
|
TargetClientId = ownerClientId,
|
||||||
Transform = new NetworkObject.SceneObject.TransformData
|
Transform = new NetworkObject.SceneObject.TransformData
|
||||||
{
|
{
|
||||||
@@ -2184,22 +2247,23 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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++)
|
for (int index = 0; index < MessagingSystem.MessageHandlers.Length; index++)
|
||||||
{
|
{
|
||||||
if (MessagingSystem.MessageTypes[index] != null)
|
if (MessagingSystem.MessageTypes[index] != null)
|
||||||
{
|
{
|
||||||
var orderingMessage = new OrderingMessage
|
var type = MessagingSystem.MessageTypes[index];
|
||||||
|
message.MessageVersions[index] = new MessageVersionData
|
||||||
{
|
{
|
||||||
Order = index,
|
Hash = XXHash.Hash32(type.FullName),
|
||||||
Hash = XXHash.Hash32(MessagingSystem.MessageTypes[index].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 scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization
|
||||||
if (!NetworkConfig.EnableSceneManagement)
|
if (!NetworkConfig.EnableSceneManagement)
|
||||||
{
|
{
|
||||||
@@ -2214,7 +2278,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
LocalClient = client;
|
LocalClient = client;
|
||||||
SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
|
SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
|
||||||
InvokeOnClientConnectedCallback(ownerClientId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.CreatePlayerObject || (response.PlayerPrefabHash == null && NetworkConfig.PlayerPrefab == null))
|
if (!response.CreatePlayerObject || (response.PlayerPrefabHash == null && NetworkConfig.PlayerPrefab == null))
|
||||||
@@ -2227,6 +2290,15 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (!string.IsNullOrEmpty(response.Reason))
|
||||||
|
{
|
||||||
|
var disconnectReason = new DisconnectReasonMessage();
|
||||||
|
disconnectReason.Reason = response.Reason;
|
||||||
|
SendMessage(ref disconnectReason, NetworkDelivery.Reliable, ownerClientId);
|
||||||
|
|
||||||
|
MessagingSystem.ProcessSendQueues();
|
||||||
|
}
|
||||||
|
|
||||||
PendingClients.Remove(ownerClientId);
|
PendingClients.Remove(ownerClientId);
|
||||||
DisconnectRemoteClient(ownerClientId);
|
DisconnectRemoteClient(ownerClientId);
|
||||||
}
|
}
|
||||||
@@ -2253,11 +2325,11 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key)
|
ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key)
|
||||||
};
|
};
|
||||||
message.ObjectInfo.Header.Hash = playerPrefabHash;
|
message.ObjectInfo.Hash = playerPrefabHash;
|
||||||
message.ObjectInfo.Header.IsSceneObject = false;
|
message.ObjectInfo.IsSceneObject = false;
|
||||||
message.ObjectInfo.Header.HasParent = false;
|
message.ObjectInfo.HasParent = false;
|
||||||
message.ObjectInfo.Header.IsPlayerObject = true;
|
message.ObjectInfo.IsPlayerObject = true;
|
||||||
message.ObjectInfo.Header.OwnerClientId = clientId;
|
message.ObjectInfo.OwnerClientId = clientId;
|
||||||
var size = SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientPair.Key);
|
var size = SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientPair.Key);
|
||||||
NetworkMetrics.TrackObjectSpawnSent(clientPair.Key, ConnectedClients[clientId].PlayerObject, size);
|
NetworkMetrics.TrackObjectSpawnSent(clientPair.Key, ConnectedClients[clientId].PlayerObject, size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -830,6 +830,9 @@ namespace Unity.Netcode
|
|||||||
// parent and then re-parents the child under a GameObject with a NetworkObject component attached.
|
// parent and then re-parents the child under a GameObject with a NetworkObject component attached.
|
||||||
if (parentNetworkObject == null)
|
if (parentNetworkObject == null)
|
||||||
{
|
{
|
||||||
|
// If we are parented under a GameObject, go ahead and mark the world position stays as false
|
||||||
|
// so clients synchronize their transform in local space. (only for in-scene placed NetworkObjects)
|
||||||
|
m_CachedWorldPositionStays = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else // If the parent still isn't spawned add this to the orphaned children and return false
|
else // If the parent still isn't spawned add this to the orphaned children and return false
|
||||||
@@ -841,8 +844,11 @@ namespace Unity.Netcode
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If we made it this far, go ahead and set the network parenting values
|
// If we made it this far, go ahead and set the network parenting values
|
||||||
// with the default WorldPoisitonSays value
|
// with the WorldPoisitonSays value set to false
|
||||||
SetNetworkParenting(parentNetworkObject.NetworkObjectId, true);
|
// Note: Since in-scene placed NetworkObjects are parented in the scene
|
||||||
|
// the default "assumption" is that children are parenting local space
|
||||||
|
// relative.
|
||||||
|
SetNetworkParenting(parentNetworkObject.NetworkObjectId, false);
|
||||||
|
|
||||||
// Set the cached parent
|
// Set the cached parent
|
||||||
m_CachedParent = parentNetworkObject.transform;
|
m_CachedParent = parentNetworkObject.transform;
|
||||||
@@ -1002,13 +1008,17 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal void SetNetworkVariableData(FastBufferReader reader)
|
|
||||||
|
/// <summary>
|
||||||
|
/// Only invoked during first synchronization of a NetworkObject (late join or newly spawned)
|
||||||
|
/// </summary>
|
||||||
|
internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
var behaviour = ChildNetworkBehaviours[i];
|
var behaviour = ChildNetworkBehaviours[i];
|
||||||
behaviour.InitializeVariables();
|
behaviour.InitializeVariables();
|
||||||
behaviour.SetNetworkVariableData(reader);
|
behaviour.SetNetworkVariableData(reader, clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1045,7 +1055,20 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
||||||
{
|
{
|
||||||
NetworkLog.LogError($"Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
|
NetworkLog.LogError($"{nameof(NetworkBehaviour)} index {index} was out of bounds for {name}. NetworkBehaviours must be the same, and in the same order, between server and client.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
|
{
|
||||||
|
var currentKnownChildren = new System.Text.StringBuilder();
|
||||||
|
currentKnownChildren.Append($"Known child {nameof(NetworkBehaviour)}s:");
|
||||||
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
|
{
|
||||||
|
var childNetworkBehaviour = ChildNetworkBehaviours[i];
|
||||||
|
currentKnownChildren.Append($" [{i}] {childNetworkBehaviour.__getTypeName()}");
|
||||||
|
currentKnownChildren.Append(i < ChildNetworkBehaviours.Count - 1 ? "," : ".");
|
||||||
|
}
|
||||||
|
NetworkLog.LogInfo(currentKnownChildren.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -1056,19 +1079,43 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal struct SceneObject
|
internal struct SceneObject
|
||||||
{
|
{
|
||||||
public struct HeaderData : INetworkSerializeByMemcpy
|
private byte m_BitField;
|
||||||
{
|
public uint Hash;
|
||||||
public ulong NetworkObjectId;
|
public ulong NetworkObjectId;
|
||||||
public ulong OwnerClientId;
|
public ulong OwnerClientId;
|
||||||
public uint Hash;
|
|
||||||
|
|
||||||
public bool IsPlayerObject;
|
public bool IsPlayerObject
|
||||||
public bool HasParent;
|
{
|
||||||
public bool IsSceneObject;
|
get => ByteUtility.GetBit(m_BitField, 0);
|
||||||
public bool HasTransform;
|
set => ByteUtility.SetBit(ref m_BitField, 0, value);
|
||||||
|
}
|
||||||
|
public bool HasParent
|
||||||
|
{
|
||||||
|
get => ByteUtility.GetBit(m_BitField, 1);
|
||||||
|
set => ByteUtility.SetBit(ref m_BitField, 1, value);
|
||||||
|
}
|
||||||
|
public bool IsSceneObject
|
||||||
|
{
|
||||||
|
get => ByteUtility.GetBit(m_BitField, 2);
|
||||||
|
set => ByteUtility.SetBit(ref m_BitField, 2, value);
|
||||||
|
}
|
||||||
|
public bool HasTransform
|
||||||
|
{
|
||||||
|
get => ByteUtility.GetBit(m_BitField, 3);
|
||||||
|
set => ByteUtility.SetBit(ref m_BitField, 3, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HeaderData Header;
|
public bool IsLatestParentSet
|
||||||
|
{
|
||||||
|
get => ByteUtility.GetBit(m_BitField, 4);
|
||||||
|
set => ByteUtility.SetBit(ref m_BitField, 4, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool WorldPositionStays
|
||||||
|
{
|
||||||
|
get => ByteUtility.GetBit(m_BitField, 5);
|
||||||
|
set => ByteUtility.SetBit(ref m_BitField, 5, value);
|
||||||
|
}
|
||||||
|
|
||||||
//If(Metadata.HasParent)
|
//If(Metadata.HasParent)
|
||||||
public ulong ParentObjectId;
|
public ulong ParentObjectId;
|
||||||
@@ -1084,7 +1131,6 @@ namespace Unity.Netcode
|
|||||||
public TransformData Transform;
|
public TransformData Transform;
|
||||||
|
|
||||||
//If(Metadata.IsReparented)
|
//If(Metadata.IsReparented)
|
||||||
public bool IsLatestParentSet;
|
|
||||||
|
|
||||||
//If(IsLatestParentSet)
|
//If(IsLatestParentSet)
|
||||||
public ulong? LatestParent;
|
public ulong? LatestParent;
|
||||||
@@ -1094,114 +1140,90 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public int NetworkSceneHandle;
|
public int NetworkSceneHandle;
|
||||||
|
|
||||||
public bool WorldPositionStays;
|
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
var writeSize = sizeof(HeaderData);
|
writer.WriteValueSafe(m_BitField);
|
||||||
if (Header.HasParent)
|
writer.WriteValueSafe(Hash);
|
||||||
|
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
|
||||||
|
BytePacker.WriteValueBitPacked(writer, OwnerClientId);
|
||||||
|
|
||||||
|
if (HasParent)
|
||||||
{
|
{
|
||||||
writeSize += FastBufferWriter.GetWriteSize(ParentObjectId);
|
BytePacker.WriteValueBitPacked(writer, ParentObjectId);
|
||||||
writeSize += FastBufferWriter.GetWriteSize(WorldPositionStays);
|
if (IsLatestParentSet)
|
||||||
writeSize += FastBufferWriter.GetWriteSize(IsLatestParentSet);
|
{
|
||||||
writeSize += IsLatestParentSet ? FastBufferWriter.GetWriteSize<ulong>() : 0;
|
BytePacker.WriteValueBitPacked(writer, LatestParent.Value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
writeSize += Header.HasTransform ? FastBufferWriter.GetWriteSize<TransformData>() : 0;
|
|
||||||
writeSize += Header.IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
|
var writeSize = 0;
|
||||||
|
writeSize += HasTransform ? FastBufferWriter.GetWriteSize<TransformData>() : 0;
|
||||||
|
writeSize += IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
|
||||||
|
|
||||||
if (!writer.TryBeginWrite(writeSize))
|
if (!writer.TryBeginWrite(writeSize))
|
||||||
{
|
{
|
||||||
throw new OverflowException("Could not serialize SceneObject: Out of buffer space.");
|
throw new OverflowException("Could not serialize SceneObject: Out of buffer space.");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteValue(Header);
|
if (HasTransform)
|
||||||
|
|
||||||
if (Header.HasParent)
|
|
||||||
{
|
|
||||||
writer.WriteValue(ParentObjectId);
|
|
||||||
writer.WriteValue(WorldPositionStays);
|
|
||||||
writer.WriteValue(IsLatestParentSet);
|
|
||||||
if (IsLatestParentSet)
|
|
||||||
{
|
|
||||||
writer.WriteValue(LatestParent.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Header.HasTransform)
|
|
||||||
{
|
{
|
||||||
writer.WriteValue(Transform);
|
writer.WriteValue(Transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their
|
// In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their
|
||||||
// NetworkSceneHandle and GlobalObjectIdHash. Since each loaded scene has a unique
|
// NetworkSceneHandle and GlobalObjectIdHash. Client-side NetworkSceneManagers use
|
||||||
// handle, it provides us with a unique and persistent "scene prefab asset" instance.
|
// this to locate their local instance of the in-scene placed NetworkObject instance.
|
||||||
// This is only set on in-scene placed NetworkObjects to reduce the over-all packet
|
// Only written for in-scene placed NetworkObjects.
|
||||||
// sizes for dynamically spawned NetworkObjects.
|
if (IsSceneObject)
|
||||||
if (Header.IsSceneObject)
|
|
||||||
{
|
{
|
||||||
writer.WriteValue(OwnerObject.GetSceneOriginHandle());
|
writer.WriteValue(OwnerObject.GetSceneOriginHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
|
// Synchronize NetworkVariables and NetworkBehaviours
|
||||||
|
var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
|
||||||
|
OwnerObject.SynchronizeNetworkBehaviours(ref bufferSerializer, TargetClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Deserialize(FastBufferReader reader)
|
public void Deserialize(FastBufferReader reader)
|
||||||
{
|
{
|
||||||
if (!reader.TryBeginRead(sizeof(HeaderData)))
|
reader.ReadValueSafe(out m_BitField);
|
||||||
|
reader.ReadValueSafe(out Hash);
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out OwnerClientId);
|
||||||
|
|
||||||
|
if (HasParent)
|
||||||
{
|
{
|
||||||
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
|
ByteUnpacker.ReadValueBitPacked(reader, out ParentObjectId);
|
||||||
}
|
if (IsLatestParentSet)
|
||||||
reader.ReadValue(out Header);
|
|
||||||
var readSize = 0;
|
|
||||||
if (Header.HasParent)
|
|
||||||
{
|
|
||||||
readSize += FastBufferWriter.GetWriteSize(ParentObjectId);
|
|
||||||
readSize += FastBufferWriter.GetWriteSize(WorldPositionStays);
|
|
||||||
readSize += FastBufferWriter.GetWriteSize(IsLatestParentSet);
|
|
||||||
// We need to read at this point in order to get the IsLatestParentSet value
|
|
||||||
if (!reader.TryBeginRead(readSize))
|
|
||||||
{
|
{
|
||||||
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
|
ByteUnpacker.ReadValueBitPacked(reader, out ulong latestParent);
|
||||||
|
LatestParent = latestParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the initial parenting related properties
|
|
||||||
reader.ReadValue(out ParentObjectId);
|
|
||||||
reader.ReadValue(out WorldPositionStays);
|
|
||||||
reader.ReadValue(out IsLatestParentSet);
|
|
||||||
|
|
||||||
// Now calculate the remaining bytes to read
|
|
||||||
readSize = 0;
|
|
||||||
readSize += IsLatestParentSet ? FastBufferWriter.GetWriteSize<ulong>() : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readSize += Header.HasTransform ? FastBufferWriter.GetWriteSize<TransformData>() : 0;
|
var readSize = 0;
|
||||||
readSize += Header.IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
|
readSize += HasTransform ? FastBufferWriter.GetWriteSize<TransformData>() : 0;
|
||||||
|
readSize += IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
|
||||||
|
|
||||||
// Try to begin reading the remaining bytes
|
// Try to begin reading the remaining bytes
|
||||||
if (!reader.TryBeginRead(readSize))
|
if (!reader.TryBeginRead(readSize))
|
||||||
{
|
{
|
||||||
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
|
throw new OverflowException("Could not deserialize SceneObject: Reading past the end of the buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsLatestParentSet)
|
if (HasTransform)
|
||||||
{
|
|
||||||
reader.ReadValueSafe(out ulong latestParent);
|
|
||||||
LatestParent = latestParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Header.HasTransform)
|
|
||||||
{
|
{
|
||||||
reader.ReadValue(out Transform);
|
reader.ReadValue(out Transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their
|
// In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their
|
||||||
// NetworkSceneHandle and GlobalObjectIdHash. Since each loaded scene has a unique
|
// NetworkSceneHandle and GlobalObjectIdHash. Client-side NetworkSceneManagers use
|
||||||
// handle, it provides us with a unique and persistent "scene prefab asset" instance.
|
// this to locate their local instance of the in-scene placed NetworkObject instance.
|
||||||
// Client-side NetworkSceneManagers use this to locate their local instance of the
|
// Only read for in-scene placed NetworkObjects
|
||||||
// NetworkObject instance.
|
if (IsSceneObject)
|
||||||
if (Header.IsSceneObject)
|
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out NetworkSceneHandle);
|
reader.ReadValue(out NetworkSceneHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1214,18 +1236,87 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles synchronizing NetworkVariables and custom synchronization data for NetworkBehaviours.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is where we determine how much data is written after the associated NetworkObject in order to recover
|
||||||
|
/// from a failed instantiated NetworkObject without completely disrupting client synchronization.
|
||||||
|
/// </remarks>
|
||||||
|
internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer, ulong targetClientId = 0) where T : IReaderWriter
|
||||||
|
{
|
||||||
|
if (serializer.IsWriter)
|
||||||
|
{
|
||||||
|
var writer = serializer.GetFastBufferWriter();
|
||||||
|
var positionBeforeSynchronizing = writer.Position;
|
||||||
|
writer.WriteValueSafe((ushort)0);
|
||||||
|
var sizeToSkipCalculationPosition = writer.Position;
|
||||||
|
|
||||||
|
// Synchronize NetworkVariables
|
||||||
|
WriteNetworkVariableData(writer, targetClientId);
|
||||||
|
// Reserve the NetworkBehaviour synchronization count position
|
||||||
|
var networkBehaviourCountPosition = writer.Position;
|
||||||
|
writer.WriteValueSafe((byte)0);
|
||||||
|
|
||||||
|
// Parse through all NetworkBehaviours and any that return true
|
||||||
|
// had additional synchronization data written.
|
||||||
|
// (See notes for reading/deserialization below)
|
||||||
|
var synchronizationCount = (byte)0;
|
||||||
|
foreach (var childBehaviour in ChildNetworkBehaviours)
|
||||||
|
{
|
||||||
|
if (childBehaviour.Synchronize(ref serializer))
|
||||||
|
{
|
||||||
|
synchronizationCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentPosition = writer.Position;
|
||||||
|
// Write the total number of bytes written for NetworkVariable and NetworkBehaviour
|
||||||
|
// synchronization.
|
||||||
|
writer.Seek(positionBeforeSynchronizing);
|
||||||
|
// We want the size of everything after our size to skip calculation position
|
||||||
|
var size = (ushort)(currentPosition - sizeToSkipCalculationPosition);
|
||||||
|
writer.WriteValueSafe(size);
|
||||||
|
// Write the number of NetworkBehaviours synchronized
|
||||||
|
writer.Seek(networkBehaviourCountPosition);
|
||||||
|
writer.WriteValueSafe(synchronizationCount);
|
||||||
|
// seek back to the position after writing NetworkVariable and NetworkBehaviour
|
||||||
|
// synchronization data.
|
||||||
|
writer.Seek(currentPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var reader = serializer.GetFastBufferReader();
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out ushort sizeOfSynchronizationData);
|
||||||
|
var seekToEndOfSynchData = reader.Position + sizeOfSynchronizationData;
|
||||||
|
// Apply the network variable synchronization data
|
||||||
|
SetNetworkVariableData(reader, targetClientId);
|
||||||
|
// Read the number of NetworkBehaviours to synchronize
|
||||||
|
reader.ReadValueSafe(out byte numberSynchronized);
|
||||||
|
var networkBehaviourId = (ushort)0;
|
||||||
|
|
||||||
|
// If a NetworkBehaviour writes synchronization data, it will first
|
||||||
|
// write its NetworkBehaviourId so when deserializing the client-side
|
||||||
|
// can find the right NetworkBehaviour to deserialize the synchronization data.
|
||||||
|
for (int i = 0; i < numberSynchronized; i++)
|
||||||
|
{
|
||||||
|
serializer.SerializeValue(ref networkBehaviourId);
|
||||||
|
var networkBehaviour = GetNetworkBehaviourAtOrderIndex(networkBehaviourId);
|
||||||
|
networkBehaviour.Synchronize(ref serializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal SceneObject GetMessageSceneObject(ulong targetClientId)
|
internal SceneObject GetMessageSceneObject(ulong targetClientId)
|
||||||
{
|
{
|
||||||
var obj = new SceneObject
|
var obj = new SceneObject
|
||||||
{
|
{
|
||||||
Header = new SceneObject.HeaderData
|
NetworkObjectId = NetworkObjectId,
|
||||||
{
|
OwnerClientId = OwnerClientId,
|
||||||
IsPlayerObject = IsPlayerObject,
|
IsPlayerObject = IsPlayerObject,
|
||||||
NetworkObjectId = NetworkObjectId,
|
IsSceneObject = IsSceneObject ?? true,
|
||||||
OwnerClientId = OwnerClientId,
|
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
||||||
IsSceneObject = IsSceneObject ?? true,
|
|
||||||
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
|
||||||
},
|
|
||||||
OwnerObject = this,
|
OwnerObject = this,
|
||||||
TargetClientId = targetClientId
|
TargetClientId = targetClientId
|
||||||
};
|
};
|
||||||
@@ -1235,11 +1326,18 @@ namespace Unity.Netcode
|
|||||||
if (!AlwaysReplicateAsRoot && transform.parent != null)
|
if (!AlwaysReplicateAsRoot && transform.parent != null)
|
||||||
{
|
{
|
||||||
parentNetworkObject = transform.parent.GetComponent<NetworkObject>();
|
parentNetworkObject = transform.parent.GetComponent<NetworkObject>();
|
||||||
|
// In-scene placed NetworkObjects parented under GameObjects with no NetworkObject
|
||||||
|
// should set the has parent flag and preserve the world position stays value
|
||||||
|
if (parentNetworkObject == null && obj.IsSceneObject)
|
||||||
|
{
|
||||||
|
obj.HasParent = true;
|
||||||
|
obj.WorldPositionStays = m_CachedWorldPositionStays;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentNetworkObject != null)
|
if (parentNetworkObject != null)
|
||||||
{
|
{
|
||||||
obj.Header.HasParent = true;
|
obj.HasParent = true;
|
||||||
obj.ParentObjectId = parentNetworkObject.NetworkObjectId;
|
obj.ParentObjectId = parentNetworkObject.NetworkObjectId;
|
||||||
obj.WorldPositionStays = m_CachedWorldPositionStays;
|
obj.WorldPositionStays = m_CachedWorldPositionStays;
|
||||||
var latestParent = GetNetworkParenting();
|
var latestParent = GetNetworkParenting();
|
||||||
@@ -1253,20 +1351,38 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(OwnerClientId))
|
if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(OwnerClientId))
|
||||||
{
|
{
|
||||||
obj.Header.HasTransform = true;
|
obj.HasTransform = true;
|
||||||
|
|
||||||
|
// We start with the default AutoObjectParentSync values to determine which transform space we will
|
||||||
|
// be synchronizing clients with.
|
||||||
|
var syncRotationPositionLocalSpaceRelative = obj.HasParent && !m_CachedWorldPositionStays;
|
||||||
|
var syncScaleLocalSpaceRelative = obj.HasParent && !m_CachedWorldPositionStays;
|
||||||
|
|
||||||
|
// If auto object synchronization is turned off
|
||||||
|
if (!AutoObjectParentSync)
|
||||||
|
{
|
||||||
|
// We always synchronize position and rotation world space relative
|
||||||
|
syncRotationPositionLocalSpaceRelative = false;
|
||||||
|
// Scale is special, it synchronizes local space relative if it has a
|
||||||
|
// parent since applying the world space scale under a parent with scale
|
||||||
|
// will result in the improper scale for the child
|
||||||
|
syncScaleLocalSpaceRelative = obj.HasParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
obj.Transform = new SceneObject.TransformData
|
obj.Transform = new SceneObject.TransformData
|
||||||
{
|
{
|
||||||
// If we are parented and we have the m_CachedWorldPositionStays disabled, then use local space
|
// If we are parented and we have the m_CachedWorldPositionStays disabled, then use local space
|
||||||
// values as opposed world space values.
|
// values as opposed world space values.
|
||||||
Position = parentNetworkObject && !m_CachedWorldPositionStays ? transform.localPosition : transform.position,
|
Position = syncRotationPositionLocalSpaceRelative ? transform.localPosition : transform.position,
|
||||||
Rotation = parentNetworkObject && !m_CachedWorldPositionStays ? transform.localRotation : transform.rotation,
|
Rotation = syncRotationPositionLocalSpaceRelative ? transform.localRotation : transform.rotation,
|
||||||
|
|
||||||
// We only use the lossyScale if the NetworkObject has a parent. Multi-generation nested children scales can
|
// We only use the lossyScale if the NetworkObject has a parent. Multi-generation nested children scales can
|
||||||
// impact the final scale of the child NetworkObject in question. The solution is to use the lossy scale
|
// impact the final scale of the child NetworkObject in question. The solution is to use the lossy scale
|
||||||
// which can be thought of as "world space scale".
|
// which can be thought of as "world space scale".
|
||||||
// More information:
|
// More information:
|
||||||
// https://docs.unity3d.com/ScriptReference/Transform-lossyScale.html
|
// https://docs.unity3d.com/ScriptReference/Transform-lossyScale.html
|
||||||
Scale = parentNetworkObject && !m_CachedWorldPositionStays ? transform.localScale : transform.lossyScale,
|
Scale = syncScaleLocalSpaceRelative ? transform.localScale : transform.lossyScale,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1278,10 +1394,10 @@ namespace Unity.Netcode
|
|||||||
/// when the client is approved or during a scene transition
|
/// when the client is approved or during a scene transition
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sceneObject">Deserialized scene object data</param>
|
/// <param name="sceneObject">Deserialized scene object data</param>
|
||||||
/// <param name="variableData">reader for the NetworkVariable data</param>
|
/// <param name="reader">FastBufferReader for the NetworkVariable data</param>
|
||||||
/// <param name="networkManager">NetworkManager instance</param>
|
/// <param name="networkManager">NetworkManager instance</param>
|
||||||
/// <returns>optional to use NetworkObject deserialized</returns>
|
/// <returns>optional to use NetworkObject deserialized</returns>
|
||||||
internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader variableData, NetworkManager networkManager)
|
internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader reader, NetworkManager networkManager)
|
||||||
{
|
{
|
||||||
//Attempt to create a local NetworkObject
|
//Attempt to create a local NetworkObject
|
||||||
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject);
|
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject);
|
||||||
@@ -1289,18 +1405,36 @@ namespace Unity.Netcode
|
|||||||
if (networkObject == null)
|
if (networkObject == null)
|
||||||
{
|
{
|
||||||
// Log the error that the NetworkObject failed to construct
|
// Log the error that the NetworkObject failed to construct
|
||||||
Debug.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Header.Hash}.");
|
if (networkManager.LogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Hash}.");
|
||||||
|
}
|
||||||
|
|
||||||
// If we failed to load this NetworkObject, then skip past the network variable data
|
try
|
||||||
variableData.ReadValueSafe(out ushort varSize);
|
{
|
||||||
variableData.Seek(variableData.Position + varSize);
|
// If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data
|
||||||
|
reader.ReadValueSafe(out ushort networkBehaviourSynchronizationDataLength);
|
||||||
|
reader.Seek(reader.Position + networkBehaviourSynchronizationDataLength);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
// We have nothing left to do here.
|
// We have nothing left to do here.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will get set again when the NetworkObject is spawned locally, but we set it here ahead of spawning
|
||||||
|
// in order to be able to determine which NetworkVariables the client will be allowed to read.
|
||||||
|
networkObject.OwnerClientId = sceneObject.OwnerClientId;
|
||||||
|
|
||||||
|
// Synchronize NetworkBehaviours
|
||||||
|
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
|
||||||
|
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
|
||||||
|
|
||||||
// Spawn the NetworkObject
|
// Spawn the NetworkObject
|
||||||
networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, variableData, false);
|
networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, false);
|
||||||
|
|
||||||
return networkObject;
|
return networkObject;
|
||||||
}
|
}
|
||||||
|
|||||||
248
Runtime/Hashing/XXHash.cs
Normal file
248
Runtime/Hashing/XXHash.cs
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
internal static class XXHash
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static unsafe uint Hash32(byte* input, int length, uint seed = 0)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
const uint prime1 = 2654435761u;
|
||||||
|
const uint prime2 = 2246822519u;
|
||||||
|
const uint prime3 = 3266489917u;
|
||||||
|
const uint prime4 = 0668265263u;
|
||||||
|
const uint prime5 = 0374761393u;
|
||||||
|
|
||||||
|
uint hash = seed + prime5;
|
||||||
|
|
||||||
|
if (length >= 16)
|
||||||
|
{
|
||||||
|
uint val0 = seed + prime1 + prime2;
|
||||||
|
uint val1 = seed + prime2;
|
||||||
|
uint val2 = seed + 0;
|
||||||
|
uint val3 = seed - prime1;
|
||||||
|
|
||||||
|
int count = length >> 4;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var pos0 = *(uint*)(input + 0);
|
||||||
|
var pos1 = *(uint*)(input + 4);
|
||||||
|
var pos2 = *(uint*)(input + 8);
|
||||||
|
var pos3 = *(uint*)(input + 12);
|
||||||
|
|
||||||
|
val0 += pos0 * prime2;
|
||||||
|
val0 = (val0 << 13) | (val0 >> (32 - 13));
|
||||||
|
val0 *= prime1;
|
||||||
|
|
||||||
|
val1 += pos1 * prime2;
|
||||||
|
val1 = (val1 << 13) | (val1 >> (32 - 13));
|
||||||
|
val1 *= prime1;
|
||||||
|
|
||||||
|
val2 += pos2 * prime2;
|
||||||
|
val2 = (val2 << 13) | (val2 >> (32 - 13));
|
||||||
|
val2 *= prime1;
|
||||||
|
|
||||||
|
val3 += pos3 * prime2;
|
||||||
|
val3 = (val3 << 13) | (val3 >> (32 - 13));
|
||||||
|
val3 *= prime1;
|
||||||
|
|
||||||
|
input += 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = ((val0 << 01) | (val0 >> (32 - 01))) +
|
||||||
|
((val1 << 07) | (val1 >> (32 - 07))) +
|
||||||
|
((val2 << 12) | (val2 >> (32 - 12))) +
|
||||||
|
((val3 << 18) | (val3 >> (32 - 18)));
|
||||||
|
}
|
||||||
|
|
||||||
|
hash += (uint)length;
|
||||||
|
|
||||||
|
length &= 15;
|
||||||
|
while (length >= 4)
|
||||||
|
{
|
||||||
|
hash += *(uint*)input * prime3;
|
||||||
|
hash = ((hash << 17) | (hash >> (32 - 17))) * prime4;
|
||||||
|
input += 4;
|
||||||
|
length -= 4;
|
||||||
|
}
|
||||||
|
while (length > 0)
|
||||||
|
{
|
||||||
|
hash += *input * prime5;
|
||||||
|
hash = ((hash << 11) | (hash >> (32 - 11))) * prime1;
|
||||||
|
++input;
|
||||||
|
--length;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash ^= hash >> 15;
|
||||||
|
hash *= prime2;
|
||||||
|
hash ^= hash >> 13;
|
||||||
|
hash *= prime3;
|
||||||
|
hash ^= hash >> 16;
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static unsafe ulong Hash64(byte* input, int length, uint seed = 0)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
const ulong prime1 = 11400714785074694791ul;
|
||||||
|
const ulong prime2 = 14029467366897019727ul;
|
||||||
|
const ulong prime3 = 01609587929392839161ul;
|
||||||
|
const ulong prime4 = 09650029242287828579ul;
|
||||||
|
const ulong prime5 = 02870177450012600261ul;
|
||||||
|
|
||||||
|
ulong hash = seed + prime5;
|
||||||
|
|
||||||
|
if (length >= 32)
|
||||||
|
{
|
||||||
|
ulong val0 = seed + prime1 + prime2;
|
||||||
|
ulong val1 = seed + prime2;
|
||||||
|
ulong val2 = seed + 0;
|
||||||
|
ulong val3 = seed - prime1;
|
||||||
|
|
||||||
|
int count = length >> 5;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var pos0 = *(ulong*)(input + 0);
|
||||||
|
var pos1 = *(ulong*)(input + 8);
|
||||||
|
var pos2 = *(ulong*)(input + 16);
|
||||||
|
var pos3 = *(ulong*)(input + 24);
|
||||||
|
|
||||||
|
val0 += pos0 * prime2;
|
||||||
|
val0 = (val0 << 31) | (val0 >> (64 - 31));
|
||||||
|
val0 *= prime1;
|
||||||
|
|
||||||
|
val1 += pos1 * prime2;
|
||||||
|
val1 = (val1 << 31) | (val1 >> (64 - 31));
|
||||||
|
val1 *= prime1;
|
||||||
|
|
||||||
|
val2 += pos2 * prime2;
|
||||||
|
val2 = (val2 << 31) | (val2 >> (64 - 31));
|
||||||
|
val2 *= prime1;
|
||||||
|
|
||||||
|
val3 += pos3 * prime2;
|
||||||
|
val3 = (val3 << 31) | (val3 >> (64 - 31));
|
||||||
|
val3 *= prime1;
|
||||||
|
|
||||||
|
input += 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = ((val0 << 01) | (val0 >> (64 - 01))) +
|
||||||
|
((val1 << 07) | (val1 >> (64 - 07))) +
|
||||||
|
((val2 << 12) | (val2 >> (64 - 12))) +
|
||||||
|
((val3 << 18) | (val3 >> (64 - 18)));
|
||||||
|
|
||||||
|
val0 *= prime2;
|
||||||
|
val0 = (val0 << 31) | (val0 >> (64 - 31));
|
||||||
|
val0 *= prime1;
|
||||||
|
hash ^= val0;
|
||||||
|
hash = hash * prime1 + prime4;
|
||||||
|
|
||||||
|
val1 *= prime2;
|
||||||
|
val1 = (val1 << 31) | (val1 >> (64 - 31));
|
||||||
|
val1 *= prime1;
|
||||||
|
hash ^= val1;
|
||||||
|
hash = hash * prime1 + prime4;
|
||||||
|
|
||||||
|
val2 *= prime2;
|
||||||
|
val2 = (val2 << 31) | (val2 >> (64 - 31));
|
||||||
|
val2 *= prime1;
|
||||||
|
hash ^= val2;
|
||||||
|
hash = hash * prime1 + prime4;
|
||||||
|
|
||||||
|
val3 *= prime2;
|
||||||
|
val3 = (val3 << 31) | (val3 >> (64 - 31));
|
||||||
|
val3 *= prime1;
|
||||||
|
hash ^= val3;
|
||||||
|
hash = hash * prime1 + prime4;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash += (ulong)length;
|
||||||
|
|
||||||
|
length &= 31;
|
||||||
|
while (length >= 8)
|
||||||
|
{
|
||||||
|
ulong lane = *(ulong*)input * prime2;
|
||||||
|
lane = ((lane << 31) | (lane >> (64 - 31))) * prime1;
|
||||||
|
hash ^= lane;
|
||||||
|
hash = ((hash << 27) | (hash >> (64 - 27))) * prime1 + prime4;
|
||||||
|
input += 8;
|
||||||
|
length -= 8;
|
||||||
|
}
|
||||||
|
if (length >= 4)
|
||||||
|
{
|
||||||
|
hash ^= *(uint*)input * prime1;
|
||||||
|
hash = ((hash << 23) | (hash >> (64 - 23))) * prime2 + prime3;
|
||||||
|
input += 4;
|
||||||
|
length -= 4;
|
||||||
|
}
|
||||||
|
while (length > 0)
|
||||||
|
{
|
||||||
|
hash ^= *input * prime5;
|
||||||
|
hash = ((hash << 11) | (hash >> (64 - 11))) * prime1;
|
||||||
|
++input;
|
||||||
|
--length;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash ^= hash >> 33;
|
||||||
|
hash *= prime2;
|
||||||
|
hash ^= hash >> 29;
|
||||||
|
hash *= prime3;
|
||||||
|
hash ^= hash >> 32;
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Hash32(this byte[] buffer)
|
||||||
|
{
|
||||||
|
int length = buffer.Length;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* pointer = buffer)
|
||||||
|
{
|
||||||
|
return Hash32(pointer, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Hash32(this string text) => Hash32(Encoding.UTF8.GetBytes(text));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Hash32(this Type type) => Hash32(type.FullName);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static uint Hash32<T>() => Hash32(typeof(T));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ulong Hash64(this byte[] buffer)
|
||||||
|
{
|
||||||
|
int length = buffer.Length;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* pointer = buffer)
|
||||||
|
{
|
||||||
|
return Hash64(pointer, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ulong Hash64(this string text) => Hash64(Encoding.UTF8.GetBytes(text));
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ulong Hash64(this Type type) => Hash64(type.FullName);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ulong Hash64<T>() => Hash64(typeof(T));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: b5aa7a49e9e694f148d810d34577546b
|
guid: c3077af091aa443acbdea9d3e97727b0
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015, 2016 Sedat Kapanoglu
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: cf89ecbf6f9954c8ea6d0848b1e79d87
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
// <copyright file="XXHash.cs" company="Sedat Kapanoglu">
|
|
||||||
// Copyright (c) 2015-2019 Sedat Kapanoglu
|
|
||||||
// MIT License (see LICENSE file for details)
|
|
||||||
// </copyright>
|
|
||||||
|
|
||||||
// @mfatihmar (Unity): Modified for Unity support
|
|
||||||
|
|
||||||
using System.Text;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// XXHash implementation.
|
|
||||||
/// </summary>
|
|
||||||
internal static class XXHash
|
|
||||||
{
|
|
||||||
private const ulong k_Prime64v1 = 11400714785074694791ul;
|
|
||||||
private const ulong k_Prime64v2 = 14029467366897019727ul;
|
|
||||||
private const ulong k_Prime64v3 = 1609587929392839161ul;
|
|
||||||
private const ulong k_Prime64v4 = 9650029242287828579ul;
|
|
||||||
private const ulong k_Prime64v5 = 2870177450012600261ul;
|
|
||||||
|
|
||||||
private const uint k_Prime32v1 = 2654435761u;
|
|
||||||
private const uint k_Prime32v2 = 2246822519u;
|
|
||||||
private const uint k_Prime32v3 = 3266489917u;
|
|
||||||
private const uint k_Prime32v4 = 668265263u;
|
|
||||||
private const uint k_Prime32v5 = 374761393u;
|
|
||||||
|
|
||||||
public static uint Hash32(string text) => Hash32(text, Encoding.UTF8);
|
|
||||||
public static uint Hash32(string text, Encoding encoding) => Hash32(encoding.GetBytes(text));
|
|
||||||
public static uint Hash32(byte[] buffer)
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
fixed (byte* ptr = buffer)
|
|
||||||
{
|
|
||||||
return Hash32(ptr, buffer.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generate a 32-bit xxHash value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">Input buffer.</param>
|
|
||||||
/// <param name="bufferLength">Input buffer length.</param>
|
|
||||||
/// <param name="seed">Optional seed.</param>
|
|
||||||
/// <returns>32-bit hash value.</returns>
|
|
||||||
public static unsafe uint Hash32(byte* buffer, int bufferLength, uint seed = 0)
|
|
||||||
{
|
|
||||||
const int stripeLength = 16;
|
|
||||||
|
|
||||||
int len = bufferLength;
|
|
||||||
int remainingLen = len;
|
|
||||||
uint acc;
|
|
||||||
|
|
||||||
byte* pInput = buffer;
|
|
||||||
if (len >= stripeLength)
|
|
||||||
{
|
|
||||||
uint acc1 = seed + k_Prime32v1 + k_Prime32v2;
|
|
||||||
uint acc2 = seed + k_Prime32v2;
|
|
||||||
uint acc3 = seed;
|
|
||||||
uint acc4 = seed - k_Prime32v1;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
acc = processStripe32(ref pInput, ref acc1, ref acc2, ref acc3, ref acc4);
|
|
||||||
remainingLen -= stripeLength;
|
|
||||||
} while (remainingLen >= stripeLength);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
acc = seed + k_Prime32v5;
|
|
||||||
}
|
|
||||||
|
|
||||||
acc += (uint)len;
|
|
||||||
acc = processRemaining32(pInput, acc, remainingLen);
|
|
||||||
|
|
||||||
return avalanche32(acc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ulong Hash64(string text) => Hash64(text, Encoding.UTF8);
|
|
||||||
public static ulong Hash64(string text, Encoding encoding) => Hash64(encoding.GetBytes(text));
|
|
||||||
public static ulong Hash64(byte[] buffer)
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
fixed (byte* ptr = buffer)
|
|
||||||
{
|
|
||||||
return Hash64(ptr, buffer.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generate a 64-bit xxHash value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">Input buffer.</param>
|
|
||||||
/// <param name="bufferLength">Input buffer length.</param>
|
|
||||||
/// <param name="seed">Optional seed.</param>
|
|
||||||
/// <returns>Computed 64-bit hash value.</returns>
|
|
||||||
public static unsafe ulong Hash64(byte* buffer, int bufferLength, ulong seed = 0)
|
|
||||||
{
|
|
||||||
const int stripeLength = 32;
|
|
||||||
|
|
||||||
int len = bufferLength;
|
|
||||||
int remainingLen = len;
|
|
||||||
ulong acc;
|
|
||||||
|
|
||||||
byte* pInput = buffer;
|
|
||||||
if (len >= stripeLength)
|
|
||||||
{
|
|
||||||
ulong acc1 = seed + k_Prime64v1 + k_Prime64v2;
|
|
||||||
ulong acc2 = seed + k_Prime64v2;
|
|
||||||
ulong acc3 = seed;
|
|
||||||
ulong acc4 = seed - k_Prime64v1;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
acc = processStripe64(ref pInput, ref acc1, ref acc2, ref acc3, ref acc4);
|
|
||||||
remainingLen -= stripeLength;
|
|
||||||
} while (remainingLen >= stripeLength);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
acc = seed + k_Prime64v5;
|
|
||||||
}
|
|
||||||
|
|
||||||
acc += (ulong)len;
|
|
||||||
acc = processRemaining64(pInput, acc, remainingLen);
|
|
||||||
|
|
||||||
|
|
||||||
return avalanche64(acc);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static unsafe ulong processStripe64(
|
|
||||||
ref byte* pInput,
|
|
||||||
ref ulong acc1,
|
|
||||||
ref ulong acc2,
|
|
||||||
ref ulong acc3,
|
|
||||||
ref ulong acc4)
|
|
||||||
{
|
|
||||||
processLane64(ref acc1, ref pInput);
|
|
||||||
processLane64(ref acc2, ref pInput);
|
|
||||||
processLane64(ref acc3, ref pInput);
|
|
||||||
processLane64(ref acc4, ref pInput);
|
|
||||||
|
|
||||||
ulong acc = Bits.RotateLeft(acc1, 1)
|
|
||||||
+ Bits.RotateLeft(acc2, 7)
|
|
||||||
+ Bits.RotateLeft(acc3, 12)
|
|
||||||
+ Bits.RotateLeft(acc4, 18);
|
|
||||||
|
|
||||||
mergeAccumulator64(ref acc, acc1);
|
|
||||||
mergeAccumulator64(ref acc, acc2);
|
|
||||||
mergeAccumulator64(ref acc, acc3);
|
|
||||||
mergeAccumulator64(ref acc, acc4);
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static unsafe void processLane64(ref ulong accn, ref byte* pInput)
|
|
||||||
{
|
|
||||||
ulong lane = *(ulong*)pInput;
|
|
||||||
accn = round64(accn, lane);
|
|
||||||
pInput += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static unsafe ulong processRemaining64(
|
|
||||||
byte* pInput,
|
|
||||||
ulong acc,
|
|
||||||
int remainingLen)
|
|
||||||
{
|
|
||||||
for (ulong lane; remainingLen >= 8; remainingLen -= 8, pInput += 8)
|
|
||||||
{
|
|
||||||
lane = *(ulong*)pInput;
|
|
||||||
|
|
||||||
acc ^= round64(0, lane);
|
|
||||||
acc = Bits.RotateLeft(acc, 27) * k_Prime64v1;
|
|
||||||
acc += k_Prime64v4;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint lane32; remainingLen >= 4; remainingLen -= 4, pInput += 4)
|
|
||||||
{
|
|
||||||
lane32 = *(uint*)pInput;
|
|
||||||
|
|
||||||
acc ^= lane32 * k_Prime64v1;
|
|
||||||
acc = Bits.RotateLeft(acc, 23) * k_Prime64v2;
|
|
||||||
acc += k_Prime64v3;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (byte lane8; remainingLen >= 1; remainingLen--, pInput++)
|
|
||||||
{
|
|
||||||
lane8 = *pInput;
|
|
||||||
acc ^= lane8 * k_Prime64v5;
|
|
||||||
acc = Bits.RotateLeft(acc, 11) * k_Prime64v1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static ulong avalanche64(ulong acc)
|
|
||||||
{
|
|
||||||
acc ^= acc >> 33;
|
|
||||||
acc *= k_Prime64v2;
|
|
||||||
acc ^= acc >> 29;
|
|
||||||
acc *= k_Prime64v3;
|
|
||||||
acc ^= acc >> 32;
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static ulong round64(ulong accn, ulong lane)
|
|
||||||
{
|
|
||||||
accn += lane * k_Prime64v2;
|
|
||||||
return Bits.RotateLeft(accn, 31) * k_Prime64v1;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void mergeAccumulator64(ref ulong acc, ulong accn)
|
|
||||||
{
|
|
||||||
acc ^= round64(0, accn);
|
|
||||||
acc *= k_Prime64v1;
|
|
||||||
acc += k_Prime64v4;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static unsafe uint processStripe32(
|
|
||||||
ref byte* pInput,
|
|
||||||
ref uint acc1,
|
|
||||||
ref uint acc2,
|
|
||||||
ref uint acc3,
|
|
||||||
ref uint acc4)
|
|
||||||
{
|
|
||||||
processLane32(ref pInput, ref acc1);
|
|
||||||
processLane32(ref pInput, ref acc2);
|
|
||||||
processLane32(ref pInput, ref acc3);
|
|
||||||
processLane32(ref pInput, ref acc4);
|
|
||||||
|
|
||||||
return Bits.RotateLeft(acc1, 1)
|
|
||||||
+ Bits.RotateLeft(acc2, 7)
|
|
||||||
+ Bits.RotateLeft(acc3, 12)
|
|
||||||
+ Bits.RotateLeft(acc4, 18);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static unsafe void processLane32(ref byte* pInput, ref uint accn)
|
|
||||||
{
|
|
||||||
uint lane = *(uint*)pInput;
|
|
||||||
accn = round32(accn, lane);
|
|
||||||
pInput += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static unsafe uint processRemaining32(
|
|
||||||
byte* pInput,
|
|
||||||
uint acc,
|
|
||||||
int remainingLen)
|
|
||||||
{
|
|
||||||
for (uint lane; remainingLen >= 4; remainingLen -= 4, pInput += 4)
|
|
||||||
{
|
|
||||||
lane = *(uint*)pInput;
|
|
||||||
acc += lane * k_Prime32v3;
|
|
||||||
acc = Bits.RotateLeft(acc, 17) * k_Prime32v4;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (byte lane; remainingLen >= 1; remainingLen--, pInput++)
|
|
||||||
{
|
|
||||||
lane = *pInput;
|
|
||||||
acc += lane * k_Prime32v5;
|
|
||||||
acc = Bits.RotateLeft(acc, 11) * k_Prime32v1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static uint round32(uint accn, uint lane)
|
|
||||||
{
|
|
||||||
accn += lane * k_Prime32v2;
|
|
||||||
accn = Bits.RotateLeft(accn, 13);
|
|
||||||
accn *= k_Prime32v1;
|
|
||||||
return accn;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static uint avalanche32(uint acc)
|
|
||||||
{
|
|
||||||
acc ^= acc >> 15;
|
|
||||||
acc *= k_Prime32v2;
|
|
||||||
acc ^= acc >> 13;
|
|
||||||
acc *= k_Prime32v3;
|
|
||||||
acc ^= acc >> 16;
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Bit operations.
|
|
||||||
/// </summary>
|
|
||||||
private static class Bits
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static ulong RotateLeft(ulong value, int bits)
|
|
||||||
{
|
|
||||||
return (value << bits) | (value >> (64 - bits));
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
internal static uint RotateLeft(uint value, int bits)
|
|
||||||
{
|
|
||||||
return (value << bits) | (value >> (32 - bits));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Unity.Collections;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
@@ -68,9 +69,23 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (clientIds == null)
|
if (clientIds == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("You must pass in a valid clientId List");
|
throw new ArgumentNullException(nameof(clientIds), "You must pass in a valid clientId List");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_NetworkManager.IsHost)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < clientIds.Count; ++i)
|
||||||
|
{
|
||||||
|
if (clientIds[i] == m_NetworkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
InvokeUnnamedMessage(
|
||||||
|
m_NetworkManager.LocalClientId,
|
||||||
|
new FastBufferReader(messageBuffer, Allocator.None),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
var message = new UnnamedMessage
|
var message = new UnnamedMessage
|
||||||
{
|
{
|
||||||
SendData = messageBuffer
|
SendData = messageBuffer
|
||||||
@@ -92,6 +107,18 @@ namespace Unity.Netcode
|
|||||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||||
public void SendUnnamedMessage(ulong clientId, FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
public void SendUnnamedMessage(ulong clientId, FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
||||||
{
|
{
|
||||||
|
if (m_NetworkManager.IsHost)
|
||||||
|
{
|
||||||
|
if (clientId == m_NetworkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
InvokeUnnamedMessage(
|
||||||
|
m_NetworkManager.LocalClientId,
|
||||||
|
new FastBufferReader(messageBuffer, Allocator.None),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
var message = new UnnamedMessage
|
var message = new UnnamedMessage
|
||||||
{
|
{
|
||||||
SendData = messageBuffer
|
SendData = messageBuffer
|
||||||
@@ -220,6 +247,20 @@ namespace Unity.Netcode
|
|||||||
hash = XXHash.Hash64(messageName);
|
hash = XXHash.Hash64(messageName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (m_NetworkManager.IsHost)
|
||||||
|
{
|
||||||
|
if (clientId == m_NetworkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
InvokeNamedMessage(
|
||||||
|
hash,
|
||||||
|
m_NetworkManager.LocalClientId,
|
||||||
|
new FastBufferReader(messageStream, Allocator.None),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var message = new NamedMessage
|
var message = new NamedMessage
|
||||||
{
|
{
|
||||||
@@ -251,7 +292,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (clientIds == null)
|
if (clientIds == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("You must pass in a valid clientId List");
|
throw new ArgumentNullException(nameof(clientIds), "You must pass in a valid clientId List");
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong hash = 0;
|
ulong hash = 0;
|
||||||
@@ -264,6 +305,21 @@ namespace Unity.Netcode
|
|||||||
hash = XXHash.Hash64(messageName);
|
hash = XXHash.Hash64(messageName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (m_NetworkManager.IsHost)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < clientIds.Count; ++i)
|
||||||
|
{
|
||||||
|
if (clientIds[i] == m_NetworkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
InvokeNamedMessage(
|
||||||
|
hash,
|
||||||
|
m_NetworkManager.LocalClientId,
|
||||||
|
new FastBufferReader(messageStream, Allocator.None),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
var message = new NamedMessage
|
var message = new NamedMessage
|
||||||
{
|
{
|
||||||
Hash = hash,
|
Hash = hash,
|
||||||
|
|||||||
50
Runtime/Messaging/DisconnectReasonMessage.cs
Normal file
50
Runtime/Messaging/DisconnectReasonMessage.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
internal struct DisconnectReasonMessage : INetworkMessage
|
||||||
|
{
|
||||||
|
public string Reason;
|
||||||
|
|
||||||
|
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.WriteValue(reasonSent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(string.Empty);
|
||||||
|
NetworkLog.LogWarning(
|
||||||
|
"Disconnect reason didn't fit. Disconnected without sending a reason. Consider shortening the reason string.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
((NetworkManager)context.SystemOwner).DisconnectReason = Reason;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
11
Runtime/Messaging/DisconnectReasonMessage.cs.meta
Normal file
11
Runtime/Messaging/DisconnectReasonMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d7742516058394f96999464f3ea32c71
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -40,8 +40,9 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal interface INetworkMessage
|
internal interface INetworkMessage
|
||||||
{
|
{
|
||||||
void Serialize(FastBufferWriter writer);
|
void Serialize(FastBufferWriter writer, int targetVersion);
|
||||||
bool Deserialize(FastBufferReader reader, ref NetworkContext context);
|
bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion);
|
||||||
void Handle(ref NetworkContext context);
|
void Handle(ref NetworkContext context);
|
||||||
|
int Version { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,26 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct ChangeOwnershipMessage : INetworkMessage, INetworkSerializeByMemcpy
|
internal struct ChangeOwnershipMessage : INetworkMessage, INetworkSerializeByMemcpy
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public ulong NetworkObjectId;
|
public ulong NetworkObjectId;
|
||||||
public ulong OwnerClientId;
|
public ulong OwnerClientId;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(this);
|
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;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
reader.ReadValueSafe(out this);
|
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out OwnerClientId);
|
||||||
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
|
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
|
||||||
{
|
{
|
||||||
networkManager.DeferredMessageManager.DeferMessage(IDeferredMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context);
|
networkManager.DeferredMessageManager.DeferMessage(IDeferredMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context);
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Unity.Collections;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
internal struct ConnectionApprovedMessage : INetworkMessage
|
internal struct ConnectionApprovedMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public ulong OwnerClientId;
|
public ulong OwnerClientId;
|
||||||
public int NetworkTick;
|
public int NetworkTick;
|
||||||
|
|
||||||
@@ -13,14 +15,26 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private FastBufferReader m_ReceivedSceneObjectData;
|
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)))
|
// ============================================================
|
||||||
|
// 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)
|
||||||
{
|
{
|
||||||
throw new OverflowException($"Not enough space in the write buffer to serialize {nameof(ConnectionApprovedMessage)}");
|
messageVersion.Serialize(writer);
|
||||||
}
|
}
|
||||||
writer.WriteValue(OwnerClientId);
|
// ============================================================
|
||||||
writer.WriteValue(NetworkTick);
|
// END FORBIDDEN SEGMENT
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
BytePacker.WriteValueBitPacked(writer, OwnerClientId);
|
||||||
|
BytePacker.WriteValueBitPacked(writer, NetworkTick);
|
||||||
|
|
||||||
uint sceneObjectCount = 0;
|
uint sceneObjectCount = 0;
|
||||||
if (SpawnedObjectsList != null)
|
if (SpawnedObjectsList != null)
|
||||||
@@ -39,17 +53,19 @@ namespace Unity.Netcode
|
|||||||
++sceneObjectCount;
|
++sceneObjectCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Seek(pos);
|
writer.Seek(pos);
|
||||||
writer.WriteValue(sceneObjectCount);
|
// Can't pack this value because its space is reserved, so it needs to always use all the reserved space.
|
||||||
|
writer.WriteValueSafe(sceneObjectCount);
|
||||||
writer.Seek(writer.Length);
|
writer.Seek(writer.Length);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writer.WriteValue(sceneObjectCount);
|
writer.WriteValueSafe(sceneObjectCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
@@ -57,13 +73,36 @@ namespace Unity.Netcode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reader.TryBeginRead(sizeof(ulong) + sizeof(int) + sizeof(int)))
|
// ============================================================
|
||||||
|
// 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)
|
||||||
{
|
{
|
||||||
throw new OverflowException($"Not enough space in the buffer to read {nameof(ConnectionApprovedMessage)}");
|
var messageVersion = new MessageVersionData();
|
||||||
}
|
messageVersion.Deserialize(reader);
|
||||||
|
networkManager.MessagingSystem.SetVersion(context.SenderId, messageVersion.Hash, messageVersion.Version);
|
||||||
|
messageHashesInOrder[i] = messageVersion.Hash;
|
||||||
|
|
||||||
reader.ReadValue(out OwnerClientId);
|
// Update the received version since this message will always be passed version 0, due to the map not
|
||||||
reader.ReadValue(out NetworkTick);
|
// 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;
|
m_ReceivedSceneObjectData = reader;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -85,7 +124,7 @@ namespace Unity.Netcode
|
|||||||
if (!networkManager.NetworkConfig.EnableSceneManagement)
|
if (!networkManager.NetworkConfig.EnableSceneManagement)
|
||||||
{
|
{
|
||||||
networkManager.SpawnManager.DestroySceneObjects();
|
networkManager.SpawnManager.DestroySceneObjects();
|
||||||
m_ReceivedSceneObjectData.ReadValue(out uint sceneObjectCount);
|
m_ReceivedSceneObjectData.ReadValueSafe(out uint sceneObjectCount);
|
||||||
|
|
||||||
// Deserializing NetworkVariable data is deferred from Receive() to Handle to avoid needing
|
// Deserializing NetworkVariable data is deferred from Receive() to Handle to avoid needing
|
||||||
// to create a list to hold the data. This is a breach of convention for performance reasons.
|
// to create a list to hold the data. This is a breach of convention for performance reasons.
|
||||||
|
|||||||
@@ -1,15 +1,35 @@
|
|||||||
|
using Unity.Collections;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
internal struct ConnectionRequestMessage : INetworkMessage
|
internal struct ConnectionRequestMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public ulong ConfigHash;
|
public ulong ConfigHash;
|
||||||
|
|
||||||
public byte[] ConnectionData;
|
public byte[] ConnectionData;
|
||||||
|
|
||||||
public bool ShouldSendConnectionData;
|
public bool ShouldSendConnectionData;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public NativeArray<MessageVersionData> MessageVersions;
|
||||||
|
|
||||||
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
|
// ============================================================
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
messageVersion.Serialize(writer);
|
||||||
|
}
|
||||||
|
// ============================================================
|
||||||
|
// END FORBIDDEN SEGMENT
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
if (ShouldSendConnectionData)
|
if (ShouldSendConnectionData)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(ConfigHash);
|
writer.WriteValueSafe(ConfigHash);
|
||||||
@@ -21,7 +41,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsServer)
|
if (!networkManager.IsServer)
|
||||||
@@ -29,6 +49,30 @@ namespace Unity.Netcode
|
|||||||
return false;
|
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);
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
var messageVersion = new MessageVersionData();
|
||||||
|
messageVersion.Deserialize(reader);
|
||||||
|
networkManager.MessagingSystem.SetVersion(context.SenderId, messageVersion.Hash, messageVersion.Version);
|
||||||
|
|
||||||
|
// 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(ConnectionRequestMessage))
|
||||||
|
{
|
||||||
|
receivedMessageVersion = messageVersion.Version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ============================================================
|
||||||
|
// END FORBIDDEN SEGMENT
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
if (networkManager.NetworkConfig.ConnectionApproval)
|
if (networkManager.NetworkConfig.ConnectionApproval)
|
||||||
{
|
{
|
||||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(ConfigHash) + FastBufferWriter.GetWriteSize<int>()))
|
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(ConfigHash) + FastBufferWriter.GetWriteSize<int>()))
|
||||||
|
|||||||
@@ -2,15 +2,17 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct CreateObjectMessage : INetworkMessage
|
internal struct CreateObjectMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public NetworkObject.SceneObject ObjectInfo;
|
public NetworkObject.SceneObject ObjectInfo;
|
||||||
private FastBufferReader m_ReceivedNetworkVariableData;
|
private FastBufferReader m_ReceivedNetworkVariableData;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
ObjectInfo.Serialize(writer);
|
ObjectInfo.Serialize(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
@@ -21,7 +23,7 @@ namespace Unity.Netcode
|
|||||||
ObjectInfo.Deserialize(reader);
|
ObjectInfo.Deserialize(reader);
|
||||||
if (!networkManager.NetworkConfig.ForceSamePrefabs && !networkManager.SpawnManager.HasPrefab(ObjectInfo))
|
if (!networkManager.NetworkConfig.ForceSamePrefabs && !networkManager.SpawnManager.HasPrefab(ObjectInfo))
|
||||||
{
|
{
|
||||||
networkManager.DeferredMessageManager.DeferMessage(IDeferredMessageManager.TriggerType.OnAddPrefab, ObjectInfo.Header.Hash, reader, ref context);
|
networkManager.DeferredMessageManager.DeferMessage(IDeferredMessageManager.TriggerType.OnAddPrefab, ObjectInfo.Hash, reader, ref context);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_ReceivedNetworkVariableData = reader;
|
m_ReceivedNetworkVariableData = reader;
|
||||||
|
|||||||
@@ -2,15 +2,18 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct DestroyObjectMessage : INetworkMessage, INetworkSerializeByMemcpy
|
internal struct DestroyObjectMessage : INetworkMessage, INetworkSerializeByMemcpy
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public ulong NetworkObjectId;
|
public ulong NetworkObjectId;
|
||||||
public bool DestroyGameObject;
|
public bool DestroyGameObject;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(this);
|
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
|
||||||
|
writer.WriteValueSafe(DestroyGameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
@@ -18,7 +21,8 @@ namespace Unity.Netcode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.ReadValueSafe(out this);
|
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
|
||||||
|
reader.ReadValueSafe(out DestroyGameObject);
|
||||||
|
|
||||||
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
||||||
{
|
{
|
||||||
|
|||||||
23
Runtime/Messaging/Messages/MessageMetadata.cs
Normal file
23
Runtime/Messaging/Messages/MessageMetadata.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Conveys a version number on a remote node for the given message (identified by its hash)
|
||||||
|
/// </summary>
|
||||||
|
internal struct MessageVersionData
|
||||||
|
{
|
||||||
|
public uint Hash;
|
||||||
|
public int Version;
|
||||||
|
|
||||||
|
public void Serialize(FastBufferWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(Hash);
|
||||||
|
BytePacker.WriteValueBitPacked(writer, Version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize(FastBufferReader reader)
|
||||||
|
{
|
||||||
|
reader.ReadValueSafe(out Hash);
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out Version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Runtime/Messaging/Messages/MessageMetadata.cs.meta
Normal file
3
Runtime/Messaging/Messages/MessageMetadata.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 754d727b316b4263a2fa0d4c54fdad52
|
||||||
|
timeCreated: 1666895514
|
||||||
@@ -2,18 +2,20 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct NamedMessage : INetworkMessage
|
internal struct NamedMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public ulong Hash;
|
public ulong Hash;
|
||||||
public FastBufferWriter SendData;
|
public FastBufferWriter SendData;
|
||||||
|
|
||||||
private FastBufferReader m_ReceiveData;
|
private FastBufferReader m_ReceiveData;
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
public unsafe void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(Hash);
|
writer.WriteValueSafe(Hash);
|
||||||
writer.WriteBytesSafe(SendData.GetUnsafePtr(), SendData.Length);
|
writer.WriteBytesSafe(SendData.GetUnsafePtr(), SendData.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out Hash);
|
reader.ReadValueSafe(out Hash);
|
||||||
m_ReceiveData = reader;
|
m_ReceiveData = reader;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal struct NetworkVariableDeltaMessage : INetworkMessage
|
internal struct NetworkVariableDeltaMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public ulong NetworkObjectId;
|
public ulong NetworkObjectId;
|
||||||
public ushort NetworkBehaviourIndex;
|
public ushort NetworkBehaviourIndex;
|
||||||
|
|
||||||
@@ -21,15 +23,15 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private FastBufferReader m_ReceivedNetworkVariableData;
|
private FastBufferReader m_ReceivedNetworkVariableData;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
|
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
|
||||||
{
|
{
|
||||||
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteValue(NetworkObjectId);
|
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
|
||||||
writer.WriteValue(NetworkBehaviourIndex);
|
BytePacker.WriteValueBitPacked(writer, NetworkBehaviourIndex);
|
||||||
|
|
||||||
for (int i = 0; i < NetworkBehaviour.NetworkVariableFields.Count; i++)
|
for (int i = 0; i < NetworkBehaviour.NetworkVariableFields.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -38,7 +40,7 @@ namespace Unity.Netcode
|
|||||||
// This var does not belong to the currently iterating delivery group.
|
// This var does not belong to the currently iterating delivery group.
|
||||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe((ushort)0);
|
BytePacker.WriteValueBitPacked(writer, (ushort)0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -66,7 +68,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (!shouldWrite)
|
if (!shouldWrite)
|
||||||
{
|
{
|
||||||
BytePacker.WriteValueBitPacked(writer, 0);
|
BytePacker.WriteValueBitPacked(writer, (ushort)0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -110,15 +112,10 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
|
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
|
||||||
{
|
ByteUnpacker.ReadValueBitPacked(reader, out NetworkBehaviourIndex);
|
||||||
throw new OverflowException($"Not enough data in the buffer to read {nameof(NetworkVariableDeltaMessage)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.ReadValue(out NetworkObjectId);
|
|
||||||
reader.ReadValue(out NetworkBehaviourIndex);
|
|
||||||
|
|
||||||
m_ReceivedNetworkVariableData = reader;
|
m_ReceivedNetworkVariableData = reader;
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Upon connecting, the host sends a series of OrderingMessage to the client so that it can make sure both sides
|
|
||||||
/// have the same message types in the same positions in
|
|
||||||
/// - MessagingSystem.m_MessageHandlers
|
|
||||||
/// - MessagingSystem.m_ReverseTypeMap
|
|
||||||
/// even if one side has extra messages (compilation, version, patch, or platform differences, etc...)
|
|
||||||
///
|
|
||||||
/// The ConnectionRequestedMessage, ConnectionApprovedMessage and OrderingMessage are prioritized at the beginning
|
|
||||||
/// of the mapping, to guarantee they can be exchanged before the two sides share their ordering
|
|
||||||
/// The sorting used in also stable so that even if MessageType names share hashes, it will work most of the time
|
|
||||||
/// </summary>
|
|
||||||
internal struct OrderingMessage : INetworkMessage
|
|
||||||
{
|
|
||||||
public int Order;
|
|
||||||
public uint Hash;
|
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
|
||||||
{
|
|
||||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(Order) + FastBufferWriter.GetWriteSize(Hash)))
|
|
||||||
{
|
|
||||||
throw new OverflowException($"Not enough space in the buffer to write {nameof(OrderingMessage)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteValue(Order);
|
|
||||||
writer.WriteValue(Hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
|
||||||
{
|
|
||||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(Order) + FastBufferWriter.GetWriteSize(Hash)))
|
|
||||||
{
|
|
||||||
throw new OverflowException($"Not enough data in the buffer to read {nameof(OrderingMessage)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.ReadValue(out Order);
|
|
||||||
reader.ReadValue(out Hash);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(ref NetworkContext context)
|
|
||||||
{
|
|
||||||
((NetworkManager)context.SystemOwner).MessagingSystem.ReorderMessage(Order, Hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,18 +4,34 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct ParentSyncMessage : INetworkMessage
|
internal struct ParentSyncMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public ulong NetworkObjectId;
|
public ulong NetworkObjectId;
|
||||||
|
|
||||||
public bool WorldPositionStays;
|
private byte m_BitField;
|
||||||
|
|
||||||
|
public bool WorldPositionStays
|
||||||
|
{
|
||||||
|
get => ByteUtility.GetBit(m_BitField, 0);
|
||||||
|
set => ByteUtility.SetBit(ref m_BitField, 0, value);
|
||||||
|
}
|
||||||
|
|
||||||
//If(Metadata.IsReparented)
|
//If(Metadata.IsReparented)
|
||||||
public bool IsLatestParentSet;
|
public bool IsLatestParentSet
|
||||||
|
{
|
||||||
|
get => ByteUtility.GetBit(m_BitField, 1);
|
||||||
|
set => ByteUtility.SetBit(ref m_BitField, 1, value);
|
||||||
|
}
|
||||||
|
|
||||||
//If(IsLatestParentSet)
|
//If(IsLatestParentSet)
|
||||||
public ulong? LatestParent;
|
public ulong? LatestParent;
|
||||||
|
|
||||||
// Is set when the parent should be removed (similar to IsReparented functionality but only for removing the parent)
|
// Is set when the parent should be removed (similar to IsReparented functionality but only for removing the parent)
|
||||||
public bool RemoveParent;
|
public bool RemoveParent
|
||||||
|
{
|
||||||
|
get => ByteUtility.GetBit(m_BitField, 2);
|
||||||
|
set => ByteUtility.SetBit(ref m_BitField, 2, value);
|
||||||
|
}
|
||||||
|
|
||||||
// These additional properties are used to synchronize clients with the current position,
|
// These additional properties are used to synchronize clients with the current position,
|
||||||
// rotation, and scale after parenting/de-parenting (world/local space relative). This
|
// rotation, and scale after parenting/de-parenting (world/local space relative). This
|
||||||
@@ -25,18 +41,15 @@ namespace Unity.Netcode
|
|||||||
public Quaternion Rotation;
|
public Quaternion Rotation;
|
||||||
public Vector3 Scale;
|
public Vector3 Scale;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
BytePacker.WriteValuePacked(writer, NetworkObjectId);
|
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
|
||||||
writer.WriteValueSafe(RemoveParent);
|
writer.WriteValueSafe(m_BitField);
|
||||||
writer.WriteValueSafe(WorldPositionStays);
|
|
||||||
if (!RemoveParent)
|
if (!RemoveParent)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(IsLatestParentSet);
|
|
||||||
|
|
||||||
if (IsLatestParentSet)
|
if (IsLatestParentSet)
|
||||||
{
|
{
|
||||||
BytePacker.WriteValueBitPacked(writer, (ulong)LatestParent);
|
BytePacker.WriteValueBitPacked(writer, LatestParent.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +59,7 @@ namespace Unity.Netcode
|
|||||||
writer.WriteValueSafe(Scale);
|
writer.WriteValueSafe(Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
@@ -54,13 +67,10 @@ namespace Unity.Netcode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteUnpacker.ReadValuePacked(reader, out NetworkObjectId);
|
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
|
||||||
reader.ReadValueSafe(out RemoveParent);
|
reader.ReadValueSafe(out m_BitField);
|
||||||
reader.ReadValueSafe(out WorldPositionStays);
|
|
||||||
if (!RemoveParent)
|
if (!RemoveParent)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out IsLatestParentSet);
|
|
||||||
|
|
||||||
if (IsLatestParentSet)
|
if (IsLatestParentSet)
|
||||||
{
|
{
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out ulong latestParent);
|
ByteUnpacker.ReadValueBitPacked(reader, out ulong latestParent);
|
||||||
|
|||||||
@@ -8,24 +8,17 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
public static unsafe void Serialize(ref FastBufferWriter writer, ref RpcMetadata metadata, ref FastBufferWriter payload)
|
public static unsafe void Serialize(ref FastBufferWriter writer, ref RpcMetadata metadata, ref FastBufferWriter payload)
|
||||||
{
|
{
|
||||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize<RpcMetadata>() + payload.Length))
|
BytePacker.WriteValueBitPacked(writer, metadata.NetworkObjectId);
|
||||||
{
|
BytePacker.WriteValueBitPacked(writer, metadata.NetworkBehaviourId);
|
||||||
throw new OverflowException("Not enough space in the buffer to store RPC data.");
|
BytePacker.WriteValueBitPacked(writer, metadata.NetworkRpcMethodId);
|
||||||
}
|
writer.WriteBytesSafe(payload.GetUnsafePtr(), payload.Length);
|
||||||
|
|
||||||
writer.WriteValue(metadata);
|
|
||||||
writer.WriteBytes(payload.GetUnsafePtr(), payload.Length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe bool Deserialize(ref FastBufferReader reader, ref NetworkContext context, ref RpcMetadata metadata, ref FastBufferReader payload)
|
public static unsafe bool Deserialize(ref FastBufferReader reader, ref NetworkContext context, ref RpcMetadata metadata, ref FastBufferReader payload)
|
||||||
{
|
{
|
||||||
int metadataSize = FastBufferWriter.GetWriteSize<RpcMetadata>();
|
ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkObjectId);
|
||||||
if (!reader.TryBeginRead(metadataSize))
|
ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkBehaviourId);
|
||||||
{
|
ByteUnpacker.ReadValueBitPacked(reader, out metadata.NetworkRpcMethodId);
|
||||||
throw new InvalidOperationException("Not enough data in the buffer to read RPC meta.");
|
|
||||||
}
|
|
||||||
|
|
||||||
reader.ReadValue(out metadata);
|
|
||||||
|
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(metadata.NetworkObjectId))
|
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(metadata.NetworkObjectId))
|
||||||
@@ -46,7 +39,7 @@ namespace Unity.Netcode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
payload = new FastBufferReader(reader.GetUnsafePtr() + metadataSize, Allocator.None, reader.Length - metadataSize);
|
payload = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.None, reader.Length - reader.Position);
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (NetworkManager.__rpc_name_table.TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName))
|
if (NetworkManager.__rpc_name_table.TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName))
|
||||||
@@ -92,17 +85,19 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal struct ServerRpcMessage : INetworkMessage
|
internal struct ServerRpcMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public RpcMetadata Metadata;
|
public RpcMetadata Metadata;
|
||||||
|
|
||||||
public FastBufferWriter WriteBuffer;
|
public FastBufferWriter WriteBuffer;
|
||||||
public FastBufferReader ReadBuffer;
|
public FastBufferReader ReadBuffer;
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
public unsafe void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer);
|
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer);
|
||||||
}
|
}
|
||||||
@@ -125,17 +120,19 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal struct ClientRpcMessage : INetworkMessage
|
internal struct ClientRpcMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public RpcMetadata Metadata;
|
public RpcMetadata Metadata;
|
||||||
|
|
||||||
public FastBufferWriter WriteBuffer;
|
public FastBufferWriter WriteBuffer;
|
||||||
public FastBufferReader ReadBuffer;
|
public FastBufferReader ReadBuffer;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer);
|
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ namespace Unity.Netcode
|
|||||||
// like most of the other messages when we have some more time and can come back and refactor this.
|
// like most of the other messages when we have some more time and can come back and refactor this.
|
||||||
internal struct SceneEventMessage : INetworkMessage
|
internal struct SceneEventMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public SceneEventData EventData;
|
public SceneEventData EventData;
|
||||||
|
|
||||||
private FastBufferReader m_ReceivedData;
|
private FastBufferReader m_ReceivedData;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
EventData.Serialize(writer);
|
EventData.Serialize(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
m_ReceivedData = reader;
|
m_ReceivedData = reader;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct ServerLogMessage : INetworkMessage
|
internal struct ServerLogMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public NetworkLog.LogType LogType;
|
public NetworkLog.LogType LogType;
|
||||||
// It'd be lovely to be able to replace this with FixedString or NativeArray...
|
// It'd be lovely to be able to replace this with FixedString or NativeArray...
|
||||||
// But it's not really practical. On the sending side, the user is likely to want
|
// But it's not really practical. On the sending side, the user is likely to want
|
||||||
@@ -11,13 +13,13 @@ namespace Unity.Netcode
|
|||||||
public string Message;
|
public string Message;
|
||||||
|
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(LogType);
|
writer.WriteValueSafe(LogType);
|
||||||
BytePacker.WriteValuePacked(writer, Message);
|
BytePacker.WriteValuePacked(writer, Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (networkManager.IsServer && networkManager.NetworkConfig.EnableNetworkLogs)
|
if (networkManager.IsServer && networkManager.NetworkConfig.EnableNetworkLogs)
|
||||||
|
|||||||
@@ -2,21 +2,23 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct TimeSyncMessage : INetworkMessage, INetworkSerializeByMemcpy
|
internal struct TimeSyncMessage : INetworkMessage, INetworkSerializeByMemcpy
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public int Tick;
|
public int Tick;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(this);
|
BytePacker.WriteValueBitPacked(writer, Tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
reader.ReadValueSafe(out this);
|
ByteUnpacker.ReadValueBitPacked(reader, out Tick);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,17 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct UnnamedMessage : INetworkMessage
|
internal struct UnnamedMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
|
public int Version => 0;
|
||||||
|
|
||||||
public FastBufferWriter SendData;
|
public FastBufferWriter SendData;
|
||||||
private FastBufferReader m_ReceivedData;
|
private FastBufferReader m_ReceivedData;
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
public unsafe void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteBytesSafe(SendData.GetUnsafePtr(), SendData.Length);
|
writer.WriteBytesSafe(SendData.GetUnsafePtr(), SendData.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
m_ReceivedData = reader;
|
m_ReceivedData = reader;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal delegate void MessageHandler(FastBufferReader reader, ref NetworkContext context, MessagingSystem system);
|
internal delegate void MessageHandler(FastBufferReader reader, ref NetworkContext context, MessagingSystem system);
|
||||||
|
internal delegate int VersionGetter();
|
||||||
|
|
||||||
private NativeList<ReceiveQueueItem> m_IncomingMessageQueue = new NativeList<ReceiveQueueItem>(16, Allocator.Persistent);
|
private NativeList<ReceiveQueueItem> m_IncomingMessageQueue = new NativeList<ReceiveQueueItem>(16, Allocator.Persistent);
|
||||||
|
|
||||||
@@ -56,6 +57,11 @@ namespace Unity.Netcode
|
|||||||
private Dictionary<Type, uint> m_MessageTypes = new Dictionary<Type, uint>();
|
private Dictionary<Type, uint> m_MessageTypes = new Dictionary<Type, uint>();
|
||||||
private Dictionary<ulong, NativeList<SendQueueItem>> m_SendQueues = new Dictionary<ulong, NativeList<SendQueueItem>>();
|
private Dictionary<ulong, NativeList<SendQueueItem>> m_SendQueues = new Dictionary<ulong, NativeList<SendQueueItem>>();
|
||||||
|
|
||||||
|
// This is m_PerClientMessageVersion[clientId][messageType] = version
|
||||||
|
private Dictionary<ulong, Dictionary<Type, int>> m_PerClientMessageVersions = new Dictionary<ulong, Dictionary<Type, int>>();
|
||||||
|
private Dictionary<uint, Type> m_MessagesByHash = new Dictionary<uint, Type>();
|
||||||
|
private Dictionary<Type, int> m_LocalVersions = new Dictionary<Type, int>();
|
||||||
|
|
||||||
private List<INetworkHooks> m_Hooks = new List<INetworkHooks>();
|
private List<INetworkHooks> m_Hooks = new List<INetworkHooks>();
|
||||||
|
|
||||||
private uint m_HighMessageType;
|
private uint m_HighMessageType;
|
||||||
@@ -74,12 +80,13 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
||||||
public const int FRAGMENTED_MESSAGE_MAX_SIZE = BytePacker.BitPackedIntMax;
|
public const int FRAGMENTED_MESSAGE_MAX_SIZE = int.MaxValue;
|
||||||
|
|
||||||
internal struct MessageWithHandler
|
internal struct MessageWithHandler
|
||||||
{
|
{
|
||||||
public Type MessageType;
|
public Type MessageType;
|
||||||
public MessageHandler Handler;
|
public MessageHandler Handler;
|
||||||
|
public VersionGetter GetVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal List<MessageWithHandler> PrioritizeMessageOrder(List<MessageWithHandler> allowedTypes)
|
internal List<MessageWithHandler> PrioritizeMessageOrder(List<MessageWithHandler> allowedTypes)
|
||||||
@@ -90,9 +97,8 @@ namespace Unity.Netcode
|
|||||||
// Those are the messages that must be delivered in order to allow re-ordering the others later
|
// Those are the messages that must be delivered in order to allow re-ordering the others later
|
||||||
foreach (var t in allowedTypes)
|
foreach (var t in allowedTypes)
|
||||||
{
|
{
|
||||||
if (t.MessageType.FullName == "Unity.Netcode.ConnectionRequestMessage" ||
|
if (t.MessageType.FullName == typeof(ConnectionRequestMessage).FullName ||
|
||||||
t.MessageType.FullName == "Unity.Netcode.ConnectionApprovedMessage" ||
|
t.MessageType.FullName == typeof(ConnectionApprovedMessage).FullName)
|
||||||
t.MessageType.FullName == "Unity.Netcode.OrderingMessage")
|
|
||||||
{
|
{
|
||||||
prioritizedTypes.Add(t);
|
prioritizedTypes.Add(t);
|
||||||
}
|
}
|
||||||
@@ -100,9 +106,8 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
foreach (var t in allowedTypes)
|
foreach (var t in allowedTypes)
|
||||||
{
|
{
|
||||||
if (t.MessageType.FullName != "Unity.Netcode.ConnectionRequestMessage" &&
|
if (t.MessageType.FullName != typeof(ConnectionRequestMessage).FullName &&
|
||||||
t.MessageType.FullName != "Unity.Netcode.ConnectionApprovedMessage" &&
|
t.MessageType.FullName != typeof(ConnectionApprovedMessage).FullName)
|
||||||
t.MessageType.FullName != "Unity.Netcode.OrderingMessage")
|
|
||||||
{
|
{
|
||||||
prioritizedTypes.Add(t);
|
prioritizedTypes.Add(t);
|
||||||
}
|
}
|
||||||
@@ -189,7 +194,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
m_MessageHandlers[m_HighMessageType] = messageWithHandler.Handler;
|
m_MessageHandlers[m_HighMessageType] = messageWithHandler.Handler;
|
||||||
m_ReverseTypeMap[m_HighMessageType] = messageWithHandler.MessageType;
|
m_ReverseTypeMap[m_HighMessageType] = messageWithHandler.MessageType;
|
||||||
|
m_MessagesByHash[XXHash.Hash32(messageWithHandler.MessageType.FullName)] = messageWithHandler.MessageType;
|
||||||
m_MessageTypes[messageWithHandler.MessageType] = m_HighMessageType++;
|
m_MessageTypes[messageWithHandler.MessageType] = m_HighMessageType++;
|
||||||
|
m_LocalVersions[messageWithHandler.MessageType] = messageWithHandler.GetVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetLocalVersion(Type messageType)
|
||||||
|
{
|
||||||
|
return m_LocalVersions[messageType];
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void HandleIncomingData(ulong clientId, ArraySegment<byte> data, float receiveTime)
|
internal void HandleIncomingData(ulong clientId, ArraySegment<byte> data, float receiveTime)
|
||||||
@@ -270,68 +282,53 @@ namespace Unity.Netcode
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moves the handler for the type having hash `targetHash` to the `desiredOrder` position, in the handler list
|
internal Type GetMessageForHash(uint messageHash)
|
||||||
// This allows the server to tell the client which id it is using for which message and make sure the right
|
|
||||||
// message is used when deserializing.
|
|
||||||
internal void ReorderMessage(int desiredOrder, uint targetHash)
|
|
||||||
{
|
{
|
||||||
if (desiredOrder < 0)
|
if (!m_MessagesByHash.ContainsKey(messageHash))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("ReorderMessage desiredOrder must be positive");
|
return null;
|
||||||
}
|
}
|
||||||
|
return m_MessagesByHash[messageHash];
|
||||||
|
}
|
||||||
|
|
||||||
if (desiredOrder < m_ReverseTypeMap.Length &&
|
internal void SetVersion(ulong clientId, uint messageHash, int version)
|
||||||
XXHash.Hash32(m_ReverseTypeMap[desiredOrder].FullName) == targetHash)
|
{
|
||||||
|
if (!m_MessagesByHash.ContainsKey(messageHash))
|
||||||
{
|
{
|
||||||
// matching positions and hashes. All good.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var messageType = m_MessagesByHash[messageHash];
|
||||||
|
|
||||||
Debug.Log($"Unexpected hash for {desiredOrder}");
|
if (!m_PerClientMessageVersions.ContainsKey(clientId))
|
||||||
|
|
||||||
// Since the message at `desiredOrder` is not the expected one,
|
|
||||||
// insert an empty placeholder and move the messages down
|
|
||||||
var typesAsList = new List<Type>(m_ReverseTypeMap);
|
|
||||||
|
|
||||||
typesAsList.Insert(desiredOrder, null);
|
|
||||||
var handlersAsList = new List<MessageHandler>(m_MessageHandlers);
|
|
||||||
handlersAsList.Insert(desiredOrder, null);
|
|
||||||
|
|
||||||
// we added a dummy message, bump the end up
|
|
||||||
m_HighMessageType++;
|
|
||||||
|
|
||||||
// Here, we rely on the server telling us about all messages, in order.
|
|
||||||
// So, we know the handlers before desiredOrder are correct.
|
|
||||||
// We start at desiredOrder to not shift them when we insert.
|
|
||||||
int position = desiredOrder;
|
|
||||||
bool found = false;
|
|
||||||
while (position < typesAsList.Count)
|
|
||||||
{
|
{
|
||||||
if (typesAsList[position] != null &&
|
m_PerClientMessageVersions[clientId] = new Dictionary<Type, int>();
|
||||||
XXHash.Hash32(typesAsList[position].FullName) == targetHash)
|
}
|
||||||
|
|
||||||
|
m_PerClientMessageVersions[clientId][messageType] = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetServerMessageOrder(NativeArray<uint> messagesInIdOrder)
|
||||||
|
{
|
||||||
|
var oldHandlers = m_MessageHandlers;
|
||||||
|
var oldTypes = m_MessageTypes;
|
||||||
|
m_ReverseTypeMap = new Type[messagesInIdOrder.Length];
|
||||||
|
m_MessageHandlers = new MessageHandler[messagesInIdOrder.Length];
|
||||||
|
m_MessageTypes = new Dictionary<Type, uint>();
|
||||||
|
|
||||||
|
for (var i = 0; i < messagesInIdOrder.Length; ++i)
|
||||||
|
{
|
||||||
|
if (!m_MessagesByHash.ContainsKey(messagesInIdOrder[i]))
|
||||||
{
|
{
|
||||||
found = true;
|
continue;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
var messageType = m_MessagesByHash[messagesInIdOrder[i]];
|
||||||
position++;
|
var oldId = oldTypes[messageType];
|
||||||
|
var handler = oldHandlers[oldId];
|
||||||
|
var newId = (uint)i;
|
||||||
|
m_MessageTypes[messageType] = newId;
|
||||||
|
m_MessageHandlers[newId] = handler;
|
||||||
|
m_ReverseTypeMap[newId] = messageType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found)
|
|
||||||
{
|
|
||||||
// Copy the handler and type to the right index
|
|
||||||
|
|
||||||
typesAsList[desiredOrder] = typesAsList[position];
|
|
||||||
handlersAsList[desiredOrder] = handlersAsList[position];
|
|
||||||
typesAsList.RemoveAt(position);
|
|
||||||
handlersAsList.RemoveAt(position);
|
|
||||||
|
|
||||||
// we removed a copy after moving a message, reduce the high message index
|
|
||||||
m_HighMessageType--;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ReverseTypeMap = typesAsList.ToArray();
|
|
||||||
m_MessageHandlers = handlersAsList.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleMessage(in MessageHeader header, FastBufferReader reader, ulong senderId, float timestamp, int serializedHeaderSize)
|
public void HandleMessage(in MessageHeader header, FastBufferReader reader, ulong senderId, float timestamp, int serializedHeaderSize)
|
||||||
@@ -433,7 +430,7 @@ namespace Unity.Netcode
|
|||||||
m_SendQueues.Remove(clientId);
|
m_SendQueues.Remove(clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void CleanupDisconnectedClient(ulong clientId)
|
private void CleanupDisconnectedClient(ulong clientId)
|
||||||
{
|
{
|
||||||
var queue = m_SendQueues[clientId];
|
var queue = m_SendQueues[clientId];
|
||||||
for (var i = 0; i < queue.Length; ++i)
|
for (var i = 0; i < queue.Length; ++i)
|
||||||
@@ -444,10 +441,67 @@ namespace Unity.Netcode
|
|||||||
queue.Dispose();
|
queue.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void CleanupDisconnectedClients()
|
||||||
|
{
|
||||||
|
var removeList = new NativeList<ulong>(Allocator.Temp);
|
||||||
|
foreach (var clientId in m_PerClientMessageVersions.Keys)
|
||||||
|
{
|
||||||
|
if (!m_SendQueues.ContainsKey(clientId))
|
||||||
|
{
|
||||||
|
removeList.Add(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var clientId in removeList)
|
||||||
|
{
|
||||||
|
m_PerClientMessageVersions.Remove(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CreateMessageAndGetVersion<T>() where T : INetworkMessage, new()
|
||||||
|
{
|
||||||
|
return new T().Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int GetMessageVersion(Type type, ulong clientId, bool forReceive = false)
|
||||||
|
{
|
||||||
|
if (!m_PerClientMessageVersions.TryGetValue(clientId, out var versionMap))
|
||||||
|
{
|
||||||
|
if (forReceive)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Trying to receive {type.Name} from client {clientId} which is not in a connected state.");
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Trying to send {type.Name} to client {clientId} which is not in a connected state.");
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!versionMap.TryGetValue(type, out var messageVersion))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return messageVersion;
|
||||||
|
}
|
||||||
|
|
||||||
public static void ReceiveMessage<T>(FastBufferReader reader, ref NetworkContext context, MessagingSystem system) where T : INetworkMessage, new()
|
public static void ReceiveMessage<T>(FastBufferReader reader, ref NetworkContext context, MessagingSystem system) where T : INetworkMessage, new()
|
||||||
{
|
{
|
||||||
var message = new T();
|
var message = new T();
|
||||||
if (message.Deserialize(reader, ref context))
|
var messageVersion = 0;
|
||||||
|
// Special cases because these are the messages that carry the version info - thus the version info isn't
|
||||||
|
// populated yet when we get these. The first part of these messages always has to be the version data
|
||||||
|
// and can't change.
|
||||||
|
if (typeof(T) != typeof(ConnectionRequestMessage) && typeof(T) != typeof(ConnectionApprovedMessage) && typeof(T) != typeof(DisconnectReasonMessage))
|
||||||
|
{
|
||||||
|
messageVersion = system.GetMessageVersion(typeof(T), context.SenderId, true);
|
||||||
|
if (messageVersion < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.Deserialize(reader, ref context, messageVersion))
|
||||||
{
|
{
|
||||||
for (var hookIdx = 0; hookIdx < system.m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < system.m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
@@ -485,16 +539,47 @@ namespace Unity.Netcode
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? FRAGMENTED_MESSAGE_MAX_SIZE : NON_FRAGMENTED_MESSAGE_MAX_SIZE;
|
var largestSerializedSize = 0;
|
||||||
|
var sentMessageVersions = new NativeHashSet<int>(clientIds.Count, Allocator.Temp);
|
||||||
|
for (var i = 0; i < clientIds.Count; ++i)
|
||||||
|
{
|
||||||
|
var messageVersion = 0;
|
||||||
|
// Special case because this is the message that carries the version info - thus the version info isn't
|
||||||
|
// populated yet when we get this. The first part of this message always has to be the version data
|
||||||
|
// and can't change.
|
||||||
|
if (typeof(TMessageType) != typeof(ConnectionRequestMessage))
|
||||||
|
{
|
||||||
|
messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]);
|
||||||
|
if (messageVersion < 0)
|
||||||
|
{
|
||||||
|
// Client doesn't know this message exists, don't send it at all.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
using var tmpSerializer = new FastBufferWriter(NON_FRAGMENTED_MESSAGE_MAX_SIZE - FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp, maxSize - FastBufferWriter.GetWriteSize<MessageHeader>());
|
if (sentMessageVersions.Contains(messageVersion))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
message.Serialize(tmpSerializer);
|
sentMessageVersions.Add(messageVersion);
|
||||||
|
|
||||||
return SendPreSerializedMessage(tmpSerializer, maxSize, ref message, delivery, clientIds);
|
var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? FRAGMENTED_MESSAGE_MAX_SIZE : NON_FRAGMENTED_MESSAGE_MAX_SIZE;
|
||||||
|
|
||||||
|
using var tmpSerializer = new FastBufferWriter(NON_FRAGMENTED_MESSAGE_MAX_SIZE - FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp, maxSize - FastBufferWriter.GetWriteSize<MessageHeader>());
|
||||||
|
|
||||||
|
message.Serialize(tmpSerializer, messageVersion);
|
||||||
|
|
||||||
|
var size = SendPreSerializedMessage(tmpSerializer, maxSize, ref message, delivery, clientIds, messageVersion);
|
||||||
|
largestSerializedSize = size > largestSerializedSize ? size : largestSerializedSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
sentMessageVersions.Dispose();
|
||||||
|
|
||||||
|
return largestSerializedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter tmpSerializer, int maxSize, ref TMessageType message, NetworkDelivery delivery, in IReadOnlyList<ulong> clientIds)
|
internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter tmpSerializer, int maxSize, ref TMessageType message, NetworkDelivery delivery, in IReadOnlyList<ulong> clientIds, int messageVersionFilter)
|
||||||
where TMessageType : INetworkMessage
|
where TMessageType : INetworkMessage
|
||||||
{
|
{
|
||||||
using var headerSerializer = new FastBufferWriter(FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp);
|
using var headerSerializer = new FastBufferWriter(FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp);
|
||||||
@@ -509,6 +594,25 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (var i = 0; i < clientIds.Count; ++i)
|
for (var i = 0; i < clientIds.Count; ++i)
|
||||||
{
|
{
|
||||||
|
var messageVersion = 0;
|
||||||
|
// Special case because this is the message that carries the version info - thus the version info isn't
|
||||||
|
// populated yet when we get this. The first part of this message always has to be the version data
|
||||||
|
// and can't change.
|
||||||
|
if (typeof(TMessageType) != typeof(ConnectionRequestMessage))
|
||||||
|
{
|
||||||
|
messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]);
|
||||||
|
if (messageVersion < 0)
|
||||||
|
{
|
||||||
|
// Client doesn't know this message exists, don't send it at all.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageVersion != messageVersionFilter)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var clientId = clientIds[i];
|
var clientId = clientIds[i];
|
||||||
|
|
||||||
if (!CanSend(clientId, typeof(TMessageType), delivery))
|
if (!CanSend(clientId, typeof(TMessageType), delivery))
|
||||||
@@ -559,8 +663,22 @@ namespace Unity.Netcode
|
|||||||
internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter tmpSerializer, int maxSize, ref TMessageType message, NetworkDelivery delivery, ulong clientId)
|
internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter tmpSerializer, int maxSize, ref TMessageType message, NetworkDelivery delivery, ulong clientId)
|
||||||
where TMessageType : INetworkMessage
|
where TMessageType : INetworkMessage
|
||||||
{
|
{
|
||||||
|
var messageVersion = 0;
|
||||||
|
// Special case because this is the message that carries the version info - thus the version info isn't
|
||||||
|
// populated yet when we get this. The first part of this message always has to be the version data
|
||||||
|
// and can't change.
|
||||||
|
if (typeof(TMessageType) != typeof(ConnectionRequestMessage))
|
||||||
|
{
|
||||||
|
messageVersion = GetMessageVersion(typeof(TMessageType), clientId);
|
||||||
|
if (messageVersion < 0)
|
||||||
|
{
|
||||||
|
// Client doesn't know this message exists, don't send it at all.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ulong* clientIds = stackalloc ulong[] { clientId };
|
ulong* clientIds = stackalloc ulong[] { clientId };
|
||||||
return SendPreSerializedMessage(tmpSerializer, maxSize, ref message, delivery, new PointerListWrapper<ulong>(clientIds, 1));
|
return SendPreSerializedMessage(tmpSerializer, maxSize, ref message, delivery, new PointerListWrapper<ulong>(clientIds, 1), messageVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct PointerListWrapper<T> : IReadOnlyList<T>
|
private struct PointerListWrapper<T> : IReadOnlyList<T>
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ namespace Unity.Netcode
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool Contains(T item)
|
public bool Contains(T item)
|
||||||
{
|
{
|
||||||
int index = NativeArrayExtensions.IndexOf(m_List, item);
|
int index = m_List.IndexOf(item);
|
||||||
return index != -1;
|
return index != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,7 +426,7 @@ namespace Unity.Netcode
|
|||||||
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
|
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = NativeArrayExtensions.IndexOf(m_List, item);
|
int index = m_List.IndexOf(item);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
@@ -20,6 +22,96 @@ namespace Unity.Netcode
|
|||||||
public void Read(FastBufferReader reader, ref T value);
|
public void Read(FastBufferReader reader, ref T value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Packing serializer for shorts
|
||||||
|
/// </summary>
|
||||||
|
internal class ShortSerializer : INetworkVariableSerializer<short>
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref short value)
|
||||||
|
{
|
||||||
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref short value)
|
||||||
|
{
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Packing serializer for shorts
|
||||||
|
/// </summary>
|
||||||
|
internal class UshortSerializer : INetworkVariableSerializer<ushort>
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref ushort value)
|
||||||
|
{
|
||||||
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref ushort value)
|
||||||
|
{
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Packing serializer for ints
|
||||||
|
/// </summary>
|
||||||
|
internal class IntSerializer : INetworkVariableSerializer<int>
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref int value)
|
||||||
|
{
|
||||||
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref int value)
|
||||||
|
{
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Packing serializer for ints
|
||||||
|
/// </summary>
|
||||||
|
internal class UintSerializer : INetworkVariableSerializer<uint>
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref uint value)
|
||||||
|
{
|
||||||
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref uint value)
|
||||||
|
{
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Packing serializer for longs
|
||||||
|
/// </summary>
|
||||||
|
internal class LongSerializer : INetworkVariableSerializer<long>
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref long value)
|
||||||
|
{
|
||||||
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref long value)
|
||||||
|
{
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Packing serializer for longs
|
||||||
|
/// </summary>
|
||||||
|
internal class UlongSerializer : INetworkVariableSerializer<ulong>
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref ulong value)
|
||||||
|
{
|
||||||
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref ulong value)
|
||||||
|
{
|
||||||
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Basic serializer for unmanaged types.
|
/// Basic serializer for unmanaged types.
|
||||||
/// This covers primitives, built-in unity types, and IForceSerializeByMemcpy
|
/// This covers primitives, built-in unity types, and IForceSerializeByMemcpy
|
||||||
@@ -188,6 +280,26 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class NetworkVariableSerializationTypes
|
public static class NetworkVariableSerializationTypes
|
||||||
{
|
{
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
#endif
|
||||||
|
internal static void InitializeIntegerSerialization()
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<short>.Serializer = new ShortSerializer();
|
||||||
|
NetworkVariableSerialization<short>.AreEqual = NetworkVariableSerialization<short>.ValueEquals;
|
||||||
|
NetworkVariableSerialization<ushort>.Serializer = new UshortSerializer();
|
||||||
|
NetworkVariableSerialization<ushort>.AreEqual = NetworkVariableSerialization<ushort>.ValueEquals;
|
||||||
|
NetworkVariableSerialization<int>.Serializer = new IntSerializer();
|
||||||
|
NetworkVariableSerialization<int>.AreEqual = NetworkVariableSerialization<int>.ValueEquals;
|
||||||
|
NetworkVariableSerialization<uint>.Serializer = new UintSerializer();
|
||||||
|
NetworkVariableSerialization<uint>.AreEqual = NetworkVariableSerialization<uint>.ValueEquals;
|
||||||
|
NetworkVariableSerialization<long>.Serializer = new LongSerializer();
|
||||||
|
NetworkVariableSerialization<long>.AreEqual = NetworkVariableSerialization<long>.ValueEquals;
|
||||||
|
NetworkVariableSerialization<ulong>.Serializer = new UlongSerializer();
|
||||||
|
NetworkVariableSerialization<ulong>.AreEqual = NetworkVariableSerialization<ulong>.ValueEquals;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registeres an unmanaged type that will be serialized by a direct memcpy into a buffer
|
/// Registeres an unmanaged type that will be serialized by a direct memcpy into a buffer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -2018,7 +2018,11 @@ namespace Unity.Netcode
|
|||||||
ScenePlacedObjects.Clear();
|
ScenePlacedObjects.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID);
|
||||||
|
#else
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Just add every NetworkObject found that isn't already in the list
|
// Just add every NetworkObject found that isn't already in the list
|
||||||
// With additive scenes, we can have multiple in-scene placed NetworkObjects with the same GlobalObjectIdHash value
|
// With additive scenes, we can have multiple in-scene placed NetworkObjects with the same GlobalObjectIdHash value
|
||||||
|
|||||||
@@ -269,7 +269,12 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
m_DespawnedInSceneObjectsSync.Clear();
|
m_DespawnedInSceneObjectsSync.Clear();
|
||||||
// Find all active and non-active in-scene placed NetworkObjects
|
// Find all active and non-active in-scene placed NetworkObjects
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var inSceneNetworkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.InstanceID).Where((c) => c.NetworkManager == m_NetworkManager);
|
||||||
|
#else
|
||||||
var inSceneNetworkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>(includeInactive: true).Where((c) => c.NetworkManager == m_NetworkManager);
|
var inSceneNetworkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>(includeInactive: true).Where((c) => c.NetworkManager == m_NetworkManager);
|
||||||
|
|
||||||
|
#endif
|
||||||
foreach (var sobj in inSceneNetworkObjects)
|
foreach (var sobj in inSceneNetworkObjects)
|
||||||
{
|
{
|
||||||
if (sobj.IsSceneObject.HasValue && sobj.IsSceneObject.Value && !sobj.IsSpawned)
|
if (sobj.IsSceneObject.HasValue && sobj.IsSceneObject.Value && !sobj.IsSpawned)
|
||||||
@@ -380,7 +385,7 @@ namespace Unity.Netcode
|
|||||||
writer.WriteValueSafe(SceneEventType);
|
writer.WriteValueSafe(SceneEventType);
|
||||||
|
|
||||||
// Write the scene loading mode
|
// Write the scene loading mode
|
||||||
writer.WriteValueSafe(LoadSceneMode);
|
writer.WriteValueSafe((byte)LoadSceneMode);
|
||||||
|
|
||||||
// Write the scene event progress Guid
|
// Write the scene event progress Guid
|
||||||
if (SceneEventType != SceneEventType.Synchronize)
|
if (SceneEventType != SceneEventType.Synchronize)
|
||||||
@@ -444,13 +449,13 @@ namespace Unity.Netcode
|
|||||||
int totalBytes = 0;
|
int totalBytes = 0;
|
||||||
|
|
||||||
// Write the number of NetworkObjects we are serializing
|
// Write the number of NetworkObjects we are serializing
|
||||||
BytePacker.WriteValuePacked(writer, m_NetworkObjectsSync.Count);
|
writer.WriteValueSafe(m_NetworkObjectsSync.Count);
|
||||||
|
|
||||||
// Serialize all NetworkObjects that are spawned
|
// Serialize all NetworkObjects that are spawned
|
||||||
for (var i = 0; i < m_NetworkObjectsSync.Count; ++i)
|
for (var i = 0; i < m_NetworkObjectsSync.Count; ++i)
|
||||||
{
|
{
|
||||||
var noStart = writer.Position;
|
var noStart = writer.Position;
|
||||||
var sceneObject = m_NetworkObjectsSync[i].GetMessageSceneObject(TargetClientId);
|
var sceneObject = m_NetworkObjectsSync[i].GetMessageSceneObject(TargetClientId);
|
||||||
BytePacker.WriteValuePacked(writer, m_NetworkObjectsSync[i].GetSceneOriginHandle());
|
|
||||||
sceneObject.Serialize(writer);
|
sceneObject.Serialize(writer);
|
||||||
var noStop = writer.Position;
|
var noStop = writer.Position;
|
||||||
totalBytes += (int)(noStop - noStart);
|
totalBytes += (int)(noStop - noStart);
|
||||||
@@ -462,8 +467,8 @@ namespace Unity.Netcode
|
|||||||
for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i)
|
for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i)
|
||||||
{
|
{
|
||||||
var noStart = writer.Position;
|
var noStart = writer.Position;
|
||||||
BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
|
writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
|
||||||
BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
|
writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
|
||||||
var noStop = writer.Position;
|
var noStop = writer.Position;
|
||||||
totalBytes += (int)(noStop - noStart);
|
totalBytes += (int)(noStop - noStart);
|
||||||
}
|
}
|
||||||
@@ -497,8 +502,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (keyValuePairBySceneHandle.Value.Observers.Contains(TargetClientId))
|
if (keyValuePairBySceneHandle.Value.Observers.Contains(TargetClientId))
|
||||||
{
|
{
|
||||||
// Write our server relative scene handle for the NetworkObject being serialized
|
|
||||||
writer.WriteValueSafe(keyValuePairBySceneHandle.Key);
|
|
||||||
// Serialize the NetworkObject
|
// Serialize the NetworkObject
|
||||||
var sceneObject = keyValuePairBySceneHandle.Value.GetMessageSceneObject(TargetClientId);
|
var sceneObject = keyValuePairBySceneHandle.Value.GetMessageSceneObject(TargetClientId);
|
||||||
sceneObject.Serialize(writer);
|
sceneObject.Serialize(writer);
|
||||||
@@ -512,8 +515,8 @@ namespace Unity.Netcode
|
|||||||
// Write the scene handle and GlobalObjectIdHash value
|
// Write the scene handle and GlobalObjectIdHash value
|
||||||
for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i)
|
for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i)
|
||||||
{
|
{
|
||||||
BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
|
writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
|
||||||
BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
|
writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
var tailPosition = writer.Position;
|
var tailPosition = writer.Position;
|
||||||
@@ -533,7 +536,8 @@ namespace Unity.Netcode
|
|||||||
internal void Deserialize(FastBufferReader reader)
|
internal void Deserialize(FastBufferReader reader)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out SceneEventType);
|
reader.ReadValueSafe(out SceneEventType);
|
||||||
reader.ReadValueSafe(out LoadSceneMode);
|
reader.ReadValueSafe(out byte loadSceneMode);
|
||||||
|
LoadSceneMode = (LoadSceneMode)loadSceneMode;
|
||||||
|
|
||||||
if (SceneEventType != SceneEventType.Synchronize)
|
if (SceneEventType != SceneEventType.Synchronize)
|
||||||
{
|
{
|
||||||
@@ -624,13 +628,15 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (ushort i = 0; i < newObjectsCount; i++)
|
for (ushort i = 0; i < newObjectsCount; i++)
|
||||||
{
|
{
|
||||||
InternalBuffer.ReadValueSafe(out int sceneHandle);
|
|
||||||
// Set our relative scene to the NetworkObject
|
|
||||||
m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneHandle);
|
|
||||||
|
|
||||||
// Deserialize the NetworkObject
|
|
||||||
var sceneObject = new NetworkObject.SceneObject();
|
var sceneObject = new NetworkObject.SceneObject();
|
||||||
sceneObject.Deserialize(InternalBuffer);
|
sceneObject.Deserialize(InternalBuffer);
|
||||||
|
|
||||||
|
if (sceneObject.IsSceneObject)
|
||||||
|
{
|
||||||
|
// Set our relative scene to the NetworkObject
|
||||||
|
m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle);
|
||||||
|
}
|
||||||
|
|
||||||
NetworkObject.AddSceneObject(sceneObject, InternalBuffer, m_NetworkManager);
|
NetworkObject.AddSceneObject(sceneObject, InternalBuffer, m_NetworkManager);
|
||||||
}
|
}
|
||||||
// Now deserialize the despawned in-scene placed NetworkObjects list (if any)
|
// Now deserialize the despawned in-scene placed NetworkObjects list (if any)
|
||||||
@@ -656,7 +662,11 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (networkObjectsToRemove.Length > 0)
|
if (networkObjectsToRemove.Length > 0)
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(UnityEngine.FindObjectsSortMode.InstanceID);
|
||||||
|
#else
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
||||||
|
#endif
|
||||||
var networkObjectIdToNetworkObject = new Dictionary<ulong, NetworkObject>();
|
var networkObjectIdToNetworkObject = new Dictionary<ulong, NetworkObject>();
|
||||||
foreach (var networkObject in networkObjects)
|
foreach (var networkObject in networkObjects)
|
||||||
{
|
{
|
||||||
@@ -771,8 +781,8 @@ namespace Unity.Netcode
|
|||||||
for (int i = 0; i < despawnedObjectsCount; i++)
|
for (int i = 0; i < despawnedObjectsCount; i++)
|
||||||
{
|
{
|
||||||
// We just need to get the scene
|
// We just need to get the scene
|
||||||
ByteUnpacker.ReadValuePacked(InternalBuffer, out int networkSceneHandle);
|
InternalBuffer.ReadValueSafe(out int networkSceneHandle);
|
||||||
ByteUnpacker.ReadValuePacked(InternalBuffer, out uint globalObjectIdHash);
|
InternalBuffer.ReadValueSafe(out uint globalObjectIdHash);
|
||||||
var sceneRelativeNetworkObjects = new Dictionary<uint, NetworkObject>();
|
var sceneRelativeNetworkObjects = new Dictionary<uint, NetworkObject>();
|
||||||
if (!sceneCache.ContainsKey(networkSceneHandle))
|
if (!sceneCache.ContainsKey(networkSceneHandle))
|
||||||
{
|
{
|
||||||
@@ -784,8 +794,14 @@ namespace Unity.Netcode
|
|||||||
var objectRelativeScene = m_NetworkManager.SceneManager.ScenesLoaded[localSceneHandle];
|
var objectRelativeScene = m_NetworkManager.SceneManager.ScenesLoaded[localSceneHandle];
|
||||||
|
|
||||||
// Find all active and non-active in-scene placed NetworkObjects
|
// Find all active and non-active in-scene placed NetworkObjects
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var inSceneNetworkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.InstanceID).Where((c) =>
|
||||||
|
c.GetSceneOriginHandle() == localSceneHandle && (c.IsSceneObject != false)).ToList();
|
||||||
|
#else
|
||||||
var inSceneNetworkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>(includeInactive: true).Where((c) =>
|
var inSceneNetworkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>(includeInactive: true).Where((c) =>
|
||||||
c.GetSceneOriginHandle() == localSceneHandle && (c.IsSceneObject != false)).ToList();
|
c.GetSceneOriginHandle() == localSceneHandle && (c.IsSceneObject != false)).ToList();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
foreach (var inSceneObject in inSceneNetworkObjects)
|
foreach (var inSceneObject in inSceneNetworkObjects)
|
||||||
{
|
{
|
||||||
@@ -847,24 +863,26 @@ namespace Unity.Netcode
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Process all spawned NetworkObjects for this network session
|
// Process all spawned NetworkObjects for this network session
|
||||||
ByteUnpacker.ReadValuePacked(InternalBuffer, out int newObjectsCount);
|
InternalBuffer.ReadValueSafe(out int newObjectsCount);
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < newObjectsCount; i++)
|
for (int i = 0; i < newObjectsCount; i++)
|
||||||
{
|
{
|
||||||
// We want to make sure for each NetworkObject we have the appropriate scene selected as the scene that is
|
|
||||||
// currently being synchronized. This assures in-scene placed NetworkObjects will use the right NetworkObject
|
|
||||||
// from the list of populated <see cref="NetworkSceneManager.ScenePlacedObjects"/>
|
|
||||||
ByteUnpacker.ReadValuePacked(InternalBuffer, out int handle);
|
|
||||||
m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(handle);
|
|
||||||
|
|
||||||
var sceneObject = new NetworkObject.SceneObject();
|
var sceneObject = new NetworkObject.SceneObject();
|
||||||
sceneObject.Deserialize(InternalBuffer);
|
sceneObject.Deserialize(InternalBuffer);
|
||||||
|
|
||||||
var spawnedNetworkObject = NetworkObject.AddSceneObject(sceneObject, InternalBuffer, networkManager);
|
// If the sceneObject is in-scene placed, then set the scene being synchronized
|
||||||
if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject))
|
if (sceneObject.IsSceneObject)
|
||||||
{
|
{
|
||||||
m_NetworkObjectsSync.Add(spawnedNetworkObject);
|
m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle);
|
||||||
|
}
|
||||||
|
var spawnedNetworkObject = NetworkObject.AddSceneObject(sceneObject, InternalBuffer, networkManager);
|
||||||
|
|
||||||
|
// If we failed to deserialize the NetowrkObject then don't add null to the list
|
||||||
|
if (spawnedNetworkObject != null)
|
||||||
|
{
|
||||||
|
if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject))
|
||||||
|
{
|
||||||
|
m_NetworkObjectsSync.Add(spawnedNetworkObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,19 +108,37 @@ namespace Unity.Netcode
|
|||||||
internal List<ulong> GetClientsWithStatus(bool completedSceneEvent)
|
internal List<ulong> GetClientsWithStatus(bool completedSceneEvent)
|
||||||
{
|
{
|
||||||
var clients = new List<ulong>();
|
var clients = new List<ulong>();
|
||||||
foreach (var clientStatus in ClientsProcessingSceneEvent)
|
if (completedSceneEvent)
|
||||||
{
|
{
|
||||||
if (clientStatus.Value == completedSceneEvent)
|
// If we are the host, then add the host-client to the list
|
||||||
|
// of clients that completed if the AsyncOperation is done.
|
||||||
|
if (m_NetworkManager.IsHost && m_AsyncOperation.isDone)
|
||||||
{
|
{
|
||||||
clients.Add(clientStatus.Key);
|
clients.Add(m_NetworkManager.LocalClientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all clients that completed the scene event
|
||||||
|
foreach (var clientStatus in ClientsProcessingSceneEvent)
|
||||||
|
{
|
||||||
|
if (clientStatus.Value == completedSceneEvent)
|
||||||
|
{
|
||||||
|
clients.Add(clientStatus.Key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// If we are getting the list of clients that have not completed the
|
|
||||||
// scene event, then add any clients that disconnected during this
|
|
||||||
// scene event.
|
|
||||||
if (!completedSceneEvent)
|
|
||||||
{
|
{
|
||||||
|
// If we are the host, then add the host-client to the list
|
||||||
|
// of clients that did not complete if the AsyncOperation is
|
||||||
|
// not done.
|
||||||
|
if (m_NetworkManager.IsHost && !m_AsyncOperation.isDone)
|
||||||
|
{
|
||||||
|
clients.Add(m_NetworkManager.LocalClientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are getting the list of clients that have not completed the
|
||||||
|
// scene event, then add any clients that disconnected during this
|
||||||
|
// scene event.
|
||||||
clients.AddRange(ClientsThatDisconnected);
|
clients.AddRange(ClientsThatDisconnected);
|
||||||
}
|
}
|
||||||
return clients;
|
return clients;
|
||||||
@@ -138,6 +156,11 @@ namespace Unity.Netcode
|
|||||||
// Track the clients that were connected when we started this event
|
// Track the clients that were connected when we started this event
|
||||||
foreach (var connectedClientId in networkManager.ConnectedClientsIds)
|
foreach (var connectedClientId in networkManager.ConnectedClientsIds)
|
||||||
{
|
{
|
||||||
|
// Ignore the host client
|
||||||
|
if (NetworkManager.ServerClientId == connectedClientId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
ClientsProcessingSceneEvent.Add(connectedClientId, false);
|
ClientsProcessingSceneEvent.Add(connectedClientId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +241,10 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the local scene event's AsyncOperation status
|
// Return the local scene event's AsyncOperation status
|
||||||
return m_AsyncOperation.isDone;
|
// Note: Integration tests process scene loading through a queue
|
||||||
|
// and the AsyncOperation could not be assigned for several
|
||||||
|
// network tick periods. Return false if that is the case.
|
||||||
|
return m_AsyncOperation == null ? false : m_AsyncOperation.isDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, float value)
|
public static void WriteValuePacked(FastBufferWriter writer, float value)
|
||||||
{
|
{
|
||||||
WriteUInt32Packed(writer, ToUint(value));
|
WriteValueBitPacked(writer, ToUint(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -61,7 +61,7 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, double value)
|
public static void WriteValuePacked(FastBufferWriter writer, double value)
|
||||||
{
|
{
|
||||||
WriteUInt64Packed(writer, ToUlong(value));
|
WriteValueBitPacked(writer, ToUlong(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -98,7 +98,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">Value to write</param>
|
/// <param name="value">Value to write</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, short value) => WriteUInt32Packed(writer, (ushort)Arithmetic.ZigZagEncode(value));
|
public static void WriteValuePacked(FastBufferWriter writer, short value) => WriteValueBitPacked(writer, value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write an unsigned short (UInt16) as a varint to the buffer.
|
/// Write an unsigned short (UInt16) as a varint to the buffer.
|
||||||
@@ -109,7 +109,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">Value to write</param>
|
/// <param name="value">Value to write</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, ushort value) => WriteUInt32Packed(writer, value);
|
public static void WriteValuePacked(FastBufferWriter writer, ushort value) => WriteValueBitPacked(writer, value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a two-byte character as a varint to the buffer.
|
/// Write a two-byte character as a varint to the buffer.
|
||||||
@@ -120,7 +120,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="c">Value to write</param>
|
/// <param name="c">Value to write</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, char c) => WriteUInt32Packed(writer, c);
|
public static void WriteValuePacked(FastBufferWriter writer, char c) => WriteValueBitPacked(writer, c);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a signed int (Int32) as a ZigZag encoded varint to the buffer.
|
/// Write a signed int (Int32) as a ZigZag encoded varint to the buffer.
|
||||||
@@ -128,7 +128,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">Value to write</param>
|
/// <param name="value">Value to write</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, int value) => WriteUInt32Packed(writer, (uint)Arithmetic.ZigZagEncode(value));
|
public static void WriteValuePacked(FastBufferWriter writer, int value) => WriteValueBitPacked(writer, value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write an unsigned int (UInt32) to the buffer.
|
/// Write an unsigned int (UInt32) to the buffer.
|
||||||
@@ -136,7 +136,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">Value to write</param>
|
/// <param name="value">Value to write</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, uint value) => WriteUInt32Packed(writer, value);
|
public static void WriteValuePacked(FastBufferWriter writer, uint value) => WriteValueBitPacked(writer, value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write an unsigned long (UInt64) to the buffer.
|
/// Write an unsigned long (UInt64) to the buffer.
|
||||||
@@ -144,7 +144,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">Value to write</param>
|
/// <param name="value">Value to write</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, ulong value) => WriteUInt64Packed(writer, value);
|
public static void WriteValuePacked(FastBufferWriter writer, ulong value) => WriteValueBitPacked(writer, value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a signed long (Int64) as a ZigZag encoded varint to the buffer.
|
/// Write a signed long (Int64) as a ZigZag encoded varint to the buffer.
|
||||||
@@ -152,7 +152,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">Value to write</param>
|
/// <param name="value">Value to write</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void WriteValuePacked(FastBufferWriter writer, long value) => WriteUInt64Packed(writer, Arithmetic.ZigZagEncode(value));
|
public static void WriteValuePacked(FastBufferWriter writer, long value) => WriteValueBitPacked(writer, value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convenience method that writes two packed Vector3 from the ray to the buffer
|
/// Convenience method that writes two packed Vector3 from the ray to the buffer
|
||||||
@@ -282,231 +282,183 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum serializable value for a BitPacked ushort (minimum for unsigned is 0)
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const ushort BitPackedUshortMax = (1 << 15) - 1;
|
public const ushort BitPackedUshortMax = (1 << 15) - 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum serializable value for a BitPacked short
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const short BitPackedShortMax = (1 << 14) - 1;
|
public const short BitPackedShortMax = (1 << 14) - 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum serializable value size for a BitPacked ushort
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const short BitPackedShortMin = -(1 << 14);
|
public const short BitPackedShortMin = -(1 << 14);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum serializable value for a BitPacked uint (minimum for unsigned is 0)
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const uint BitPackedUintMax = (1 << 30) - 1;
|
public const uint BitPackedUintMax = (1 << 30) - 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum serializable value for a BitPacked int
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int BitPackedIntMax = (1 << 29) - 1;
|
public const int BitPackedIntMax = (1 << 29) - 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum serializable value size for a BitPacked int
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int BitPackedIntMin = -(1 << 29);
|
public const int BitPackedIntMin = -(1 << 29);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum serializable value for a BitPacked ulong (minimum for unsigned is 0)
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const ulong BitPackedULongMax = (1L << 61) - 1;
|
public const ulong BitPackedULongMax = (1L << 61) - 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum serializable value for a BitPacked long
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const long BitPackedLongMax = (1L << 60) - 1;
|
public const long BitPackedLongMax = (1L << 60) - 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Minimum serializable value size for a BitPacked long
|
/// Obsolete value that no longer carries meaning. Do not use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const long BitPackedLongMin = -(1L << 60);
|
public const long BitPackedLongMin = -(1L << 60);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a 14-bit signed short to the buffer in a bit-encoded packed format.
|
/// Writes a 16-bit signed short to the buffer in a bit-encoded packed format.
|
||||||
/// The first bit indicates whether the value is 1 byte or 2.
|
/// Zig-zag encoding is used to move the sign bit to the least significant bit, so that negative values
|
||||||
/// The sign bit takes up another bit.
|
/// are still able to be compressed.
|
||||||
/// That leaves 14 bits for the value.
|
/// The first two bits indicate whether the value is 1, 2, or 3 bytes.
|
||||||
/// A value greater than 2^14-1 or less than -2^14 will throw an exception in editor and development builds.
|
/// If the value uses 14 bits or less, the remaining 14 bits contain the value.
|
||||||
/// In release builds builds the exception is not thrown and the value is truncated by losing its two
|
/// For performance, reasons, if the value is 15 bits or more, there will be six 0 bits, followed
|
||||||
/// most significant bits after zig-zag encoding.
|
/// by the original unmodified 16-bit value in the next 2 bytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">The value to pack</param>
|
/// <param name="value">The value to pack</param>
|
||||||
public static void WriteValueBitPacked(FastBufferWriter writer, short value) => WriteValueBitPacked(writer, (ushort)Arithmetic.ZigZagEncode(value));
|
public static void WriteValueBitPacked(FastBufferWriter writer, short value) => WriteValueBitPacked(writer, (ushort)Arithmetic.ZigZagEncode(value));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a 15-bit unsigned short to the buffer in a bit-encoded packed format.
|
/// Writes a 16-bit unsigned short to the buffer in a bit-encoded packed format.
|
||||||
/// The first bit indicates whether the value is 1 byte or 2.
|
/// The first two bits indicate whether the value is 1, 2, or 3 bytes.
|
||||||
/// That leaves 15 bits for the value.
|
/// If the value uses 14 bits or less, the remaining 14 bits contain the value.
|
||||||
/// A value greater than 2^15-1 will throw an exception in editor and development builds.
|
/// For performance, reasons, if the value is 15 bits or more, there will be six 0 bits, followed
|
||||||
/// In release builds builds the exception is not thrown and the value is truncated by losing its
|
/// by the original unmodified 16-bit value in the next 2 bytes.
|
||||||
/// most significant bit.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">The value to pack</param>
|
/// <param name="value">The value to pack</param>
|
||||||
public static void WriteValueBitPacked(FastBufferWriter writer, ushort value)
|
public static void WriteValueBitPacked(FastBufferWriter writer, ushort value)
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
if (value > (1 << 14) - 1)
|
||||||
if (value >= BitPackedUshortMax)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException("BitPacked ushorts must be <= 15 bits");
|
if (!writer.TryBeginWriteInternal(3))
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (value <= 0b0111_1111)
|
|
||||||
{
|
|
||||||
if (!writer.TryBeginWriteInternal(1))
|
|
||||||
{
|
{
|
||||||
throw new OverflowException("Writing past the end of the buffer");
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
}
|
}
|
||||||
writer.WriteByte((byte)(value << 1));
|
writer.WriteByte(3);
|
||||||
|
writer.WriteValue(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!writer.TryBeginWriteInternal(2))
|
|
||||||
{
|
|
||||||
throw new OverflowException("Writing past the end of the buffer");
|
|
||||||
}
|
|
||||||
writer.WriteValue((ushort)((value << 1) | 0b1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a 29-bit signed int to the buffer in a bit-encoded packed format.
|
|
||||||
/// The first two bits indicate whether the value is 1, 2, 3, or 4 bytes.
|
|
||||||
/// The sign bit takes up another bit.
|
|
||||||
/// That leaves 29 bits for the value.
|
|
||||||
/// A value greater than 2^29-1 or less than -2^29 will throw an exception in editor and development builds.
|
|
||||||
/// In release builds builds the exception is not thrown and the value is truncated by losing its three
|
|
||||||
/// most significant bits after zig-zag encoding.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer">The writer to write to</param>
|
|
||||||
/// <param name="value">The value to pack</param>
|
|
||||||
public static void WriteValueBitPacked(FastBufferWriter writer, int value) => WriteValueBitPacked(writer, (uint)Arithmetic.ZigZagEncode(value));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes a 30-bit unsigned int to the buffer in a bit-encoded packed format.
|
|
||||||
/// The first two bits indicate whether the value is 1, 2, 3, or 4 bytes.
|
|
||||||
/// That leaves 30 bits for the value.
|
|
||||||
/// A value greater than 2^30-1 will throw an exception in editor and development builds.
|
|
||||||
/// In release builds builds the exception is not thrown and the value is truncated by losing its two
|
|
||||||
/// most significant bits.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer">The writer to write to</param>
|
|
||||||
/// <param name="value">The value to pack</param>
|
|
||||||
public static void WriteValueBitPacked(FastBufferWriter writer, uint value)
|
|
||||||
{
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
|
||||||
if (value > BitPackedUintMax)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("BitPacked uints must be <= 30 bits");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
value <<= 2;
|
value <<= 2;
|
||||||
var numBytes = BitCounter.GetUsedByteCount(value);
|
var numBytes = BitCounter.GetUsedByteCount(value);
|
||||||
if (!writer.TryBeginWriteInternal(numBytes))
|
if (!writer.TryBeginWriteInternal(numBytes))
|
||||||
{
|
{
|
||||||
throw new OverflowException("Writing past the end of the buffer");
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
}
|
}
|
||||||
writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes);
|
writer.WritePartialValue(value | (ushort)(numBytes), numBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a 60-bit signed long to the buffer in a bit-encoded packed format.
|
/// Writes a 32-bit signed int to the buffer in a bit-encoded packed format.
|
||||||
/// The first three bits indicate whether the value is 1, 2, 3, 4, 5, 6, 7, or 8 bytes.
|
/// Zig-zag encoding is used to move the sign bit to the least significant bit, so that negative values
|
||||||
/// The sign bit takes up another bit.
|
/// are still able to be compressed.
|
||||||
/// That leaves 60 bits for the value.
|
/// The first three bits indicate whether the value is 1, 2, 3, 4, or 5 bytes.
|
||||||
/// A value greater than 2^60-1 or less than -2^60 will throw an exception in editor and development builds.
|
/// If the value uses 29 bits or less, the remaining 29 bits contain the value.
|
||||||
/// In release builds builds the exception is not thrown and the value is truncated by losing its four
|
/// For performance, reasons, if the value is 30 bits or more, there will be five 0 bits, followed
|
||||||
/// most significant bits after zig-zag encoding.
|
/// by the original unmodified 32-bit value in the next 4 bytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">The value to pack</param>
|
/// <param name="value">The value to pack</param>
|
||||||
public static void WriteValueBitPacked(FastBufferWriter writer, long value) => WriteValueBitPacked(writer, Arithmetic.ZigZagEncode(value));
|
public static void WriteValueBitPacked(FastBufferWriter writer, int value) => WriteValueBitPacked(writer, (uint)Arithmetic.ZigZagEncode(value));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a 61-bit unsigned long to the buffer in a bit-encoded packed format.
|
/// Writes a 32-bit unsigned int to the buffer in a bit-encoded packed format.
|
||||||
/// The first three bits indicate whether the value is 1, 2, 3, 4, 5, 6, 7, or 8 bytes.
|
/// The first three bits indicate whether the value is 1, 2, 3, 4, or 5 bytes.
|
||||||
/// That leaves 31 bits for the value.
|
/// If the value uses 29 bits or less, the remaining 29 bits contain the value.
|
||||||
/// A value greater than 2^61-1 will throw an exception in editor and development builds.
|
/// For performance, reasons, if the value is 30 bits or more, there will be five 0 bits, followed
|
||||||
/// In release builds builds the exception is not thrown and the value is truncated by losing its three
|
/// by the original unmodified 32-bit value in the next 4 bytes.
|
||||||
/// most significant bits.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="writer">The writer to write to</param>
|
/// <param name="writer">The writer to write to</param>
|
||||||
/// <param name="value">The value to pack</param>
|
/// <param name="value">The value to pack</param>
|
||||||
public static void WriteValueBitPacked(FastBufferWriter writer, ulong value)
|
public static void WriteValueBitPacked(FastBufferWriter writer, uint value)
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
if (value > (1 << 29) - 1)
|
||||||
if (value > BitPackedULongMax)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException("BitPacked ulongs must be <= 61 bits");
|
if (!writer.TryBeginWriteInternal(5))
|
||||||
|
{
|
||||||
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
|
}
|
||||||
|
writer.WriteByte(5);
|
||||||
|
writer.WriteValue(value);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
value <<= 3;
|
value <<= 3;
|
||||||
var numBytes = BitCounter.GetUsedByteCount(value);
|
var numBytes = BitCounter.GetUsedByteCount(value);
|
||||||
if (!writer.TryBeginWriteInternal(numBytes))
|
if (!writer.TryBeginWriteInternal(numBytes))
|
||||||
{
|
{
|
||||||
throw new OverflowException("Writing past the end of the buffer");
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
}
|
}
|
||||||
writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes);
|
writer.WritePartialValue(value | (uint)(numBytes), numBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a 64-bit signed long to the buffer in a bit-encoded packed format.
|
||||||
|
/// Zig-zag encoding is used to move the sign bit to the least significant bit, so that negative values
|
||||||
|
/// are still able to be compressed.
|
||||||
|
/// The first four bits indicate whether the value is 1, 2, 3, 4, 5, 6, 7, 8, or 9 bytes.
|
||||||
|
/// If the value uses 60 bits or less, the remaining 60 bits contain the value.
|
||||||
|
/// For performance, reasons, if the value is 61 bits or more, there will be four 0 bits, followed
|
||||||
|
/// by the original unmodified 64-bit value in the next 8 bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The writer to write to</param>
|
||||||
|
/// <param name="value">The value to pack</param>
|
||||||
|
public static void WriteValueBitPacked(FastBufferWriter writer, long value) => WriteValueBitPacked(writer, Arithmetic.ZigZagEncode(value));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a 64-bit unsigned long to the buffer in a bit-encoded packed format.
|
||||||
|
/// The first four bits indicate whether the value is 1, 2, 3, 4, 5, 6, 7, 8, or 9 bytes.
|
||||||
|
/// If the value uses 60 bits or less, the remaining 60 bits contain the value.
|
||||||
|
/// For performance, reasons, if the value is 61 bits or more, there will be four 0 bits, followed
|
||||||
|
/// by the original unmodified 64-bit value in the next 8 bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The writer to write to</param>
|
||||||
|
/// <param name="value">The value to pack</param>
|
||||||
|
public static void WriteValueBitPacked(FastBufferWriter writer, ulong value)
|
||||||
|
{
|
||||||
|
if (value > (1L << 60) - 1)
|
||||||
|
{
|
||||||
|
if (!writer.TryBeginWriteInternal(9))
|
||||||
|
{
|
||||||
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
|
}
|
||||||
|
writer.WriteByte(9);
|
||||||
|
writer.WriteValue(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
value <<= 4;
|
||||||
|
var numBytes = BitCounter.GetUsedByteCount(value);
|
||||||
|
if (!writer.TryBeginWriteInternal(numBytes))
|
||||||
|
{
|
||||||
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
|
}
|
||||||
|
writer.WritePartialValue(value | (uint)(numBytes), numBytes);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static void WriteUInt64Packed(FastBufferWriter writer, ulong value)
|
|
||||||
{
|
|
||||||
if (value <= 240)
|
|
||||||
{
|
|
||||||
writer.WriteByteSafe((byte)value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value <= 2287)
|
|
||||||
{
|
|
||||||
writer.WriteByteSafe((byte)(((value - 240) >> 8) + 241));
|
|
||||||
writer.WriteByteSafe((byte)(value - 240));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var writeBytes = BitCounter.GetUsedByteCount(value);
|
|
||||||
|
|
||||||
if (!writer.TryBeginWriteInternal(writeBytes + 1))
|
|
||||||
{
|
|
||||||
throw new OverflowException("Writing past the end of the buffer");
|
|
||||||
}
|
|
||||||
writer.WriteByte((byte)(247 + writeBytes));
|
|
||||||
writer.WritePartialValue(value, writeBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looks like the same code as WriteUInt64Packed?
|
|
||||||
// It's actually different because it will call the more efficient 32-bit version
|
|
||||||
// of BytewiseUtility.GetUsedByteCount().
|
|
||||||
private static void WriteUInt32Packed(FastBufferWriter writer, uint value)
|
|
||||||
{
|
|
||||||
if (value <= 240)
|
|
||||||
{
|
|
||||||
writer.WriteByteSafe((byte)value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value <= 2287)
|
|
||||||
{
|
|
||||||
writer.WriteByteSafe((byte)(((value - 240) >> 8) + 241));
|
|
||||||
writer.WriteByteSafe((byte)(value - 240));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var writeBytes = BitCounter.GetUsedByteCount(value);
|
|
||||||
|
|
||||||
if (!writer.TryBeginWriteInternal(writeBytes + 1))
|
|
||||||
{
|
|
||||||
throw new OverflowException("Writing past the end of the buffer");
|
|
||||||
}
|
|
||||||
writer.WriteByte((byte)(247 + writeBytes));
|
|
||||||
writer.WritePartialValue(value, writeBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static unsafe uint ToUint<T>(T value) where T : unmanaged
|
private static unsafe uint ToUint<T>(T value) where T : unmanaged
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ByteUnpacker
|
public static class ByteUnpacker
|
||||||
{
|
{
|
||||||
|
|
||||||
#if UNITY_NETCODE_DEBUG_NO_PACKING
|
#if UNITY_NETCODE_DEBUG_NO_PACKING
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -58,7 +57,7 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out float value)
|
public static void ReadValuePacked(FastBufferReader reader, out float value)
|
||||||
{
|
{
|
||||||
ReadUInt32Packed(reader, out uint asUInt);
|
ReadValueBitPacked(reader, out uint asUInt);
|
||||||
value = ToSingle(asUInt);
|
value = ToSingle(asUInt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +69,7 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out double value)
|
public static void ReadValuePacked(FastBufferReader reader, out double value)
|
||||||
{
|
{
|
||||||
ReadUInt64Packed(reader, out ulong asULong);
|
ReadValueBitPacked(reader, out ulong asULong);
|
||||||
value = ToDouble(asULong);
|
value = ToDouble(asULong);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,11 +108,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="reader">The reader to read from</param>
|
/// <param name="reader">The reader to read from</param>
|
||||||
/// <param name="value">Value to read</param>
|
/// <param name="value">Value to read</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out short value)
|
public static void ReadValuePacked(FastBufferReader reader, out short value) => ReadValueBitPacked(reader, out value);
|
||||||
{
|
|
||||||
ReadUInt32Packed(reader, out uint readValue);
|
|
||||||
value = (short)Arithmetic.ZigZagDecode(readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an unsigned short (UInt16) as a varint from the stream.
|
/// Read an unsigned short (UInt16) as a varint from the stream.
|
||||||
@@ -121,11 +116,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="reader">The reader to read from</param>
|
/// <param name="reader">The reader to read from</param>
|
||||||
/// <param name="value">Value to read</param>
|
/// <param name="value">Value to read</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out ushort value)
|
public static void ReadValuePacked(FastBufferReader reader, out ushort value) => ReadValueBitPacked(reader, out value);
|
||||||
{
|
|
||||||
ReadUInt32Packed(reader, out uint readValue);
|
|
||||||
value = (ushort)readValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a two-byte character as a varint from the stream.
|
/// Read a two-byte character as a varint from the stream.
|
||||||
@@ -135,7 +126,7 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out char c)
|
public static void ReadValuePacked(FastBufferReader reader, out char c)
|
||||||
{
|
{
|
||||||
ReadUInt32Packed(reader, out uint readValue);
|
ReadValueBitPacked(reader, out ushort readValue);
|
||||||
c = (char)readValue;
|
c = (char)readValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,11 +136,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="reader">The reader to read from</param>
|
/// <param name="reader">The reader to read from</param>
|
||||||
/// <param name="value">Value to read</param>
|
/// <param name="value">Value to read</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out int value)
|
public static void ReadValuePacked(FastBufferReader reader, out int value) => ReadValueBitPacked(reader, out value);
|
||||||
{
|
|
||||||
ReadUInt32Packed(reader, out uint readValue);
|
|
||||||
value = (int)Arithmetic.ZigZagDecode(readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an unsigned int (UInt32) from the stream.
|
/// Read an unsigned int (UInt32) from the stream.
|
||||||
@@ -157,7 +144,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="reader">The reader to read from</param>
|
/// <param name="reader">The reader to read from</param>
|
||||||
/// <param name="value">Value to read</param>
|
/// <param name="value">Value to read</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out uint value) => ReadUInt32Packed(reader, out value);
|
public static void ReadValuePacked(FastBufferReader reader, out uint value) => ReadValueBitPacked(reader, out value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an unsigned long (UInt64) from the stream.
|
/// Read an unsigned long (UInt64) from the stream.
|
||||||
@@ -165,7 +152,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="reader">The reader to read from</param>
|
/// <param name="reader">The reader to read from</param>
|
||||||
/// <param name="value">Value to read</param>
|
/// <param name="value">Value to read</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out ulong value) => ReadUInt64Packed(reader, out value);
|
public static void ReadValuePacked(FastBufferReader reader, out ulong value) => ReadValueBitPacked(reader, out value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a signed long (Int64) as a ZigZag encoded varint from the stream.
|
/// Read a signed long (Int64) as a ZigZag encoded varint from the stream.
|
||||||
@@ -175,8 +162,7 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void ReadValuePacked(FastBufferReader reader, out long value)
|
public static void ReadValuePacked(FastBufferReader reader, out long value)
|
||||||
{
|
{
|
||||||
ReadUInt64Packed(reader, out ulong readValue);
|
ReadValueBitPacked(reader, out value);
|
||||||
value = Arithmetic.ZigZagDecode(readValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -341,7 +327,9 @@ namespace Unity.Netcode
|
|||||||
ushort returnValue = 0;
|
ushort returnValue = 0;
|
||||||
byte* ptr = ((byte*)&returnValue);
|
byte* ptr = ((byte*)&returnValue);
|
||||||
byte* data = reader.GetUnsafePtrAtCurrentPosition();
|
byte* data = reader.GetUnsafePtrAtCurrentPosition();
|
||||||
int numBytes = (data[0] & 0b1) + 1;
|
// Mask out the first two bits - they contain the total byte count
|
||||||
|
// (1, 2, or 3)
|
||||||
|
int numBytes = (data[0] & 0b11);
|
||||||
if (!reader.TryBeginReadInternal(numBytes))
|
if (!reader.TryBeginReadInternal(numBytes))
|
||||||
{
|
{
|
||||||
throw new OverflowException("Reading past the end of the buffer");
|
throw new OverflowException("Reading past the end of the buffer");
|
||||||
@@ -350,17 +338,23 @@ namespace Unity.Netcode
|
|||||||
switch (numBytes)
|
switch (numBytes)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
// First byte contains no data, it's just a marker. The data is in the remaining two bytes.
|
||||||
|
ptr[0] = data[1];
|
||||||
|
ptr[1] = data[2];
|
||||||
|
value = returnValue;
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
throw new InvalidOperationException("Could not read bit-packed value: impossible byte count");
|
throw new InvalidOperationException("Could not read bit-packed value: impossible byte count");
|
||||||
}
|
}
|
||||||
|
|
||||||
value = (ushort)(returnValue >> 1);
|
value = (ushort)(returnValue >> 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -386,7 +380,8 @@ namespace Unity.Netcode
|
|||||||
uint returnValue = 0;
|
uint returnValue = 0;
|
||||||
byte* ptr = ((byte*)&returnValue);
|
byte* ptr = ((byte*)&returnValue);
|
||||||
byte* data = reader.GetUnsafePtrAtCurrentPosition();
|
byte* data = reader.GetUnsafePtrAtCurrentPosition();
|
||||||
int numBytes = (data[0] & 0b11) + 1;
|
// Mask out the first three bits - they contain the total byte count (1-5)
|
||||||
|
int numBytes = (data[0] & 0b111);
|
||||||
if (!reader.TryBeginReadInternal(numBytes))
|
if (!reader.TryBeginReadInternal(numBytes))
|
||||||
{
|
{
|
||||||
throw new OverflowException("Reading past the end of the buffer");
|
throw new OverflowException("Reading past the end of the buffer");
|
||||||
@@ -395,26 +390,34 @@ namespace Unity.Netcode
|
|||||||
switch (numBytes)
|
switch (numBytes)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
*(ptr + 2) = *(data + 2);
|
ptr[2] = data[2];
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
*(ptr + 2) = *(data + 2);
|
ptr[2] = data[2];
|
||||||
*(ptr + 3) = *(data + 3);
|
ptr[3] = data[3];
|
||||||
break;
|
break;
|
||||||
|
case 5:
|
||||||
|
// First byte contains no data, it's just a marker. The data is in the remaining two bytes.
|
||||||
|
ptr[0] = data[1];
|
||||||
|
ptr[1] = data[2];
|
||||||
|
ptr[2] = data[3];
|
||||||
|
ptr[3] = data[4];
|
||||||
|
value = returnValue;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = returnValue >> 2;
|
value = returnValue >> 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -440,7 +443,8 @@ namespace Unity.Netcode
|
|||||||
ulong returnValue = 0;
|
ulong returnValue = 0;
|
||||||
byte* ptr = ((byte*)&returnValue);
|
byte* ptr = ((byte*)&returnValue);
|
||||||
byte* data = reader.GetUnsafePtrAtCurrentPosition();
|
byte* data = reader.GetUnsafePtrAtCurrentPosition();
|
||||||
int numBytes = (data[0] & 0b111) + 1;
|
// Mask out the first four bits - they contain the total byte count (1-9)
|
||||||
|
int numBytes = (data[0] & 0b1111);
|
||||||
if (!reader.TryBeginReadInternal(numBytes))
|
if (!reader.TryBeginReadInternal(numBytes))
|
||||||
{
|
{
|
||||||
throw new OverflowException("Reading past the end of the buffer");
|
throw new OverflowException("Reading past the end of the buffer");
|
||||||
@@ -449,109 +453,74 @@ namespace Unity.Netcode
|
|||||||
switch (numBytes)
|
switch (numBytes)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
*(ptr + 2) = *(data + 2);
|
ptr[2] = data[2];
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
*(ptr + 2) = *(data + 2);
|
ptr[2] = data[2];
|
||||||
*(ptr + 3) = *(data + 3);
|
ptr[3] = data[3];
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
*(ptr + 2) = *(data + 2);
|
ptr[2] = data[2];
|
||||||
*(ptr + 3) = *(data + 3);
|
ptr[3] = data[3];
|
||||||
*(ptr + 4) = *(data + 4);
|
ptr[4] = data[4];
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
*(ptr + 2) = *(data + 2);
|
ptr[2] = data[2];
|
||||||
*(ptr + 3) = *(data + 3);
|
ptr[3] = data[3];
|
||||||
*(ptr + 4) = *(data + 4);
|
ptr[4] = data[4];
|
||||||
*(ptr + 5) = *(data + 5);
|
ptr[5] = data[5];
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
*(ptr + 2) = *(data + 2);
|
ptr[2] = data[2];
|
||||||
*(ptr + 3) = *(data + 3);
|
ptr[3] = data[3];
|
||||||
*(ptr + 4) = *(data + 4);
|
ptr[4] = data[4];
|
||||||
*(ptr + 5) = *(data + 5);
|
ptr[5] = data[5];
|
||||||
*(ptr + 6) = *(data + 6);
|
ptr[6] = data[6];
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
*ptr = *data;
|
ptr[0] = data[0];
|
||||||
*(ptr + 1) = *(data + 1);
|
ptr[1] = data[1];
|
||||||
*(ptr + 2) = *(data + 2);
|
ptr[2] = data[2];
|
||||||
*(ptr + 3) = *(data + 3);
|
ptr[3] = data[3];
|
||||||
*(ptr + 4) = *(data + 4);
|
ptr[4] = data[4];
|
||||||
*(ptr + 5) = *(data + 5);
|
ptr[5] = data[5];
|
||||||
*(ptr + 6) = *(data + 6);
|
ptr[6] = data[6];
|
||||||
*(ptr + 7) = *(data + 7);
|
ptr[7] = data[7];
|
||||||
break;
|
break;
|
||||||
|
case 9:
|
||||||
|
// First byte contains no data, it's just a marker. The data is in the remaining two bytes.
|
||||||
|
ptr[0] = data[1];
|
||||||
|
ptr[1] = data[2];
|
||||||
|
ptr[2] = data[3];
|
||||||
|
ptr[3] = data[4];
|
||||||
|
ptr[4] = data[5];
|
||||||
|
ptr[5] = data[6];
|
||||||
|
ptr[6] = data[7];
|
||||||
|
ptr[7] = data[8];
|
||||||
|
value = returnValue;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = returnValue >> 3;
|
value = returnValue >> 4;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
private static void ReadUInt64Packed(FastBufferReader reader, out ulong value)
|
|
||||||
{
|
|
||||||
reader.ReadByteSafe(out byte firstByte);
|
|
||||||
if (firstByte <= 240)
|
|
||||||
{
|
|
||||||
value = firstByte;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstByte <= 248)
|
|
||||||
{
|
|
||||||
reader.ReadByteSafe(out byte secondByte);
|
|
||||||
value = 240UL + ((firstByte - 241UL) << 8) + secondByte;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var numBytes = firstByte - 247;
|
|
||||||
if (!reader.TryBeginReadInternal(numBytes))
|
|
||||||
{
|
|
||||||
throw new OverflowException("Reading past the end of the buffer");
|
|
||||||
}
|
|
||||||
reader.ReadPartialValue(out value, numBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReadUInt32Packed(FastBufferReader reader, out uint value)
|
|
||||||
{
|
|
||||||
reader.ReadByteSafe(out byte firstByte);
|
|
||||||
if (firstByte <= 240)
|
|
||||||
{
|
|
||||||
value = firstByte;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstByte <= 248)
|
|
||||||
{
|
|
||||||
reader.ReadByteSafe(out byte secondByte);
|
|
||||||
value = 240U + ((firstByte - 241U) << 8) + secondByte;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var numBytes = firstByte - 247;
|
|
||||||
if (!reader.TryBeginReadInternal(numBytes))
|
|
||||||
{
|
|
||||||
throw new OverflowException("Reading past the end of the buffer");
|
|
||||||
}
|
|
||||||
reader.ReadPartialValue(out value, numBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static unsafe float ToSingle<T>(T value) where T : unmanaged
|
private static unsafe float ToSingle<T>(T value) where T : unmanaged
|
||||||
|
|||||||
58
Runtime/Serialization/ByteUtility.cs
Normal file
58
Runtime/Serialization/ByteUtility.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
internal class ByteUtility
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static unsafe byte ToByte(bool b) => *(byte*)&b;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static bool GetBit(byte bitField, ushort bitPosition)
|
||||||
|
{
|
||||||
|
return (bitField & (1 << bitPosition)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void SetBit(ref byte bitField, ushort bitPosition, bool value)
|
||||||
|
{
|
||||||
|
bitField = (byte)((bitField & ~(1 << bitPosition)) | (ToByte(value) << bitPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static bool GetBit(ushort bitField, ushort bitPosition)
|
||||||
|
{
|
||||||
|
return (bitField & (1 << bitPosition)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void SetBit(ref ushort bitField, ushort bitPosition, bool value)
|
||||||
|
{
|
||||||
|
bitField = (ushort)((bitField & ~(1 << bitPosition)) | (ToByte(value) << bitPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static bool GetBit(uint bitField, ushort bitPosition)
|
||||||
|
{
|
||||||
|
return (bitField & (1 << bitPosition)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void SetBit(ref uint bitField, ushort bitPosition, bool value)
|
||||||
|
{
|
||||||
|
bitField = (uint)((bitField & ~(1 << bitPosition)) | ((uint)ToByte(value) << bitPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static bool GetBit(ulong bitField, ushort bitPosition)
|
||||||
|
{
|
||||||
|
return (bitField & (ulong)(1 << bitPosition)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal static void SetBit(ref ulong bitField, ushort bitPosition, bool value)
|
||||||
|
{
|
||||||
|
bitField = ((bitField & (ulong)~(1 << bitPosition)) | ((ulong)ToByte(value) << bitPosition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Runtime/Serialization/ByteUtility.cs.meta
Normal file
3
Runtime/Serialization/ByteUtility.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 25bb0dd7157c423b8cfe0ecf06e15ae5
|
||||||
|
timeCreated: 1666711082
|
||||||
@@ -65,7 +65,7 @@ namespace Unity.Netcode
|
|||||||
ReaderHandle* readerHandle = null;
|
ReaderHandle* readerHandle = null;
|
||||||
if (copyAllocator == Allocator.None)
|
if (copyAllocator == Allocator.None)
|
||||||
{
|
{
|
||||||
readerHandle = (ReaderHandle*)UnsafeUtility.Malloc(sizeof(ReaderHandle) + length, UnsafeUtility.AlignOf<byte>(), internalAllocator);
|
readerHandle = (ReaderHandle*)UnsafeUtility.Malloc(sizeof(ReaderHandle), UnsafeUtility.AlignOf<byte>(), internalAllocator);
|
||||||
readerHandle->BufferPointer = buffer;
|
readerHandle->BufferPointer = buffer;
|
||||||
readerHandle->Position = offset;
|
readerHandle->Position = offset;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,6 +270,9 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
networkObject.OwnerClientId = clientId;
|
networkObject.OwnerClientId = clientId;
|
||||||
|
|
||||||
|
networkObject.MarkVariablesDirty(true);
|
||||||
|
NetworkManager.BehaviourUpdater.AddForUpdate(networkObject);
|
||||||
|
|
||||||
// Server adds entries for all client ownership
|
// Server adds entries for all client ownership
|
||||||
UpdateOwnershipTable(networkObject, networkObject.OwnerClientId);
|
UpdateOwnershipTable(networkObject, networkObject.OwnerClientId);
|
||||||
|
|
||||||
@@ -291,13 +294,13 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal bool HasPrefab(NetworkObject.SceneObject sceneObject)
|
internal bool HasPrefab(NetworkObject.SceneObject sceneObject)
|
||||||
{
|
{
|
||||||
if (!NetworkManager.NetworkConfig.EnableSceneManagement || !sceneObject.Header.IsSceneObject)
|
if (!NetworkManager.NetworkConfig.EnableSceneManagement || !sceneObject.IsSceneObject)
|
||||||
{
|
{
|
||||||
if (NetworkManager.PrefabHandler.ContainsHandler(sceneObject.Header.Hash))
|
if (NetworkManager.PrefabHandler.ContainsHandler(sceneObject.Hash))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (NetworkManager.NetworkConfig.NetworkPrefabOverrideLinks.TryGetValue(sceneObject.Header.Hash, out var networkPrefab))
|
if (NetworkManager.NetworkConfig.NetworkPrefabOverrideLinks.TryGetValue(sceneObject.Hash, out var networkPrefab))
|
||||||
{
|
{
|
||||||
switch (networkPrefab.Override)
|
switch (networkPrefab.Override)
|
||||||
{
|
{
|
||||||
@@ -312,7 +315,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var networkObject = NetworkManager.SceneManager.GetSceneRelativeInSceneNetworkObject(sceneObject.Header.Hash, sceneObject.NetworkSceneHandle);
|
var networkObject = NetworkManager.SceneManager.GetSceneRelativeInSceneNetworkObject(sceneObject.Hash, sceneObject.NetworkSceneHandle);
|
||||||
return networkObject != null;
|
return networkObject != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,22 +329,22 @@ namespace Unity.Netcode
|
|||||||
internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneObject)
|
internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneObject)
|
||||||
{
|
{
|
||||||
NetworkObject networkObject = null;
|
NetworkObject networkObject = null;
|
||||||
var globalObjectIdHash = sceneObject.Header.Hash;
|
var globalObjectIdHash = sceneObject.Hash;
|
||||||
var position = sceneObject.Header.HasTransform ? sceneObject.Transform.Position : default;
|
var position = sceneObject.HasTransform ? sceneObject.Transform.Position : default;
|
||||||
var rotation = sceneObject.Header.HasTransform ? sceneObject.Transform.Rotation : default;
|
var rotation = sceneObject.HasTransform ? sceneObject.Transform.Rotation : default;
|
||||||
var scale = sceneObject.Header.HasTransform ? sceneObject.Transform.Scale : default;
|
var scale = sceneObject.HasTransform ? sceneObject.Transform.Scale : default;
|
||||||
var parentNetworkId = sceneObject.Header.HasParent ? sceneObject.ParentObjectId : default;
|
var parentNetworkId = sceneObject.HasParent ? sceneObject.ParentObjectId : default;
|
||||||
var worldPositionStays = sceneObject.Header.HasParent ? sceneObject.WorldPositionStays : true;
|
var worldPositionStays = (!sceneObject.HasParent) || sceneObject.WorldPositionStays;
|
||||||
var isSpawnedByPrefabHandler = false;
|
var isSpawnedByPrefabHandler = false;
|
||||||
|
|
||||||
// If scene management is disabled or the NetworkObject was dynamically spawned
|
// If scene management is disabled or the NetworkObject was dynamically spawned
|
||||||
if (!NetworkManager.NetworkConfig.EnableSceneManagement || !sceneObject.Header.IsSceneObject)
|
if (!NetworkManager.NetworkConfig.EnableSceneManagement || !sceneObject.IsSceneObject)
|
||||||
{
|
{
|
||||||
// If the prefab hash has a registered INetworkPrefabInstanceHandler derived class
|
// If the prefab hash has a registered INetworkPrefabInstanceHandler derived class
|
||||||
if (NetworkManager.PrefabHandler.ContainsHandler(globalObjectIdHash))
|
if (NetworkManager.PrefabHandler.ContainsHandler(globalObjectIdHash))
|
||||||
{
|
{
|
||||||
// Let the handler spawn the NetworkObject
|
// Let the handler spawn the NetworkObject
|
||||||
networkObject = NetworkManager.PrefabHandler.HandleNetworkPrefabSpawn(globalObjectIdHash, sceneObject.Header.OwnerClientId, position, rotation);
|
networkObject = NetworkManager.PrefabHandler.HandleNetworkPrefabSpawn(globalObjectIdHash, sceneObject.OwnerClientId, position, rotation);
|
||||||
networkObject.NetworkManagerOwner = NetworkManager;
|
networkObject.NetworkManagerOwner = NetworkManager;
|
||||||
isSpawnedByPrefabHandler = true;
|
isSpawnedByPrefabHandler = true;
|
||||||
}
|
}
|
||||||
@@ -402,12 +405,12 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (networkObject != null)
|
if (networkObject != null)
|
||||||
{
|
{
|
||||||
// SPECIAL CASE:
|
// SPECIAL CASE FOR IN-SCENE PLACED: (only when the parent has a NetworkObject)
|
||||||
// This is a special case scenario where a late joining client has joined and loaded one or
|
// This is a special case scenario where a late joining client has joined and loaded one or
|
||||||
// more scenes that contain nested in-scene placed NetworkObject children yet the server's
|
// more scenes that contain nested in-scene placed NetworkObject children yet the server's
|
||||||
// synchronization information does not indicate the NetworkObject in question has a parent.
|
// synchronization information does not indicate the NetworkObject in question has a parent.
|
||||||
// Under this scenario, we want to remove the parent before spawning and setting the transform values.
|
// Under this scenario, we want to remove the parent before spawning and setting the transform values.
|
||||||
if (sceneObject.Header.IsSceneObject && !sceneObject.Header.HasParent && networkObject.transform.parent != null)
|
if (sceneObject.IsSceneObject && !sceneObject.HasParent && networkObject.transform.parent != null)
|
||||||
{
|
{
|
||||||
// if the in-scene placed NetworkObject has a parent NetworkObject but the synchronization information does not
|
// if the in-scene placed NetworkObject has a parent NetworkObject but the synchronization information does not
|
||||||
// include parenting, then we need to force the removal of that parent
|
// include parenting, then we need to force the removal of that parent
|
||||||
@@ -421,9 +424,11 @@ namespace Unity.Netcode
|
|||||||
// Set the transform unless we were spawned by a prefab handler
|
// Set the transform unless we were spawned by a prefab handler
|
||||||
// Note: prefab handlers are provided the position and rotation
|
// Note: prefab handlers are provided the position and rotation
|
||||||
// but it is up to the user to set those values
|
// but it is up to the user to set those values
|
||||||
if (sceneObject.Header.HasTransform && !isSpawnedByPrefabHandler)
|
if (sceneObject.HasTransform && !isSpawnedByPrefabHandler)
|
||||||
{
|
{
|
||||||
if (worldPositionStays)
|
// If world position stays is true or we have auto object parent synchronization disabled
|
||||||
|
// then we want to apply the position and rotation values world space relative
|
||||||
|
if (worldPositionStays || !networkObject.AutoObjectParentSync)
|
||||||
{
|
{
|
||||||
networkObject.transform.position = position;
|
networkObject.transform.position = position;
|
||||||
networkObject.transform.rotation = rotation;
|
networkObject.transform.rotation = rotation;
|
||||||
@@ -441,22 +446,31 @@ namespace Unity.Netcode
|
|||||||
// the network prefab used to represent the player.
|
// the network prefab used to represent the player.
|
||||||
// Note: not doing this would set the player's scale to zero since
|
// Note: not doing this would set the player's scale to zero since
|
||||||
// that is the default value of Vector3.
|
// that is the default value of Vector3.
|
||||||
if (!sceneObject.Header.IsPlayerObject)
|
if (!sceneObject.IsPlayerObject)
|
||||||
{
|
{
|
||||||
|
// Since scale is always applied to local space scale, we do the transform
|
||||||
|
// space logic during serialization such that it works out whether AutoObjectParentSync
|
||||||
|
// is enabled or not (see NetworkObject.SceneObject)
|
||||||
networkObject.transform.localScale = scale;
|
networkObject.transform.localScale = scale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sceneObject.Header.HasParent)
|
if (sceneObject.HasParent)
|
||||||
{
|
{
|
||||||
// Go ahead and set network parenting properties
|
// Go ahead and set network parenting properties, if the latest parent is not set then pass in null
|
||||||
networkObject.SetNetworkParenting(parentNetworkId, worldPositionStays);
|
// (we always want to set worldPositionStays)
|
||||||
|
ulong? parentId = null;
|
||||||
|
if (sceneObject.IsLatestParentSet)
|
||||||
|
{
|
||||||
|
parentId = parentNetworkId;
|
||||||
|
}
|
||||||
|
networkObject.SetNetworkParenting(parentId, worldPositionStays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Dynamically spawned NetworkObjects that occur during a LoadSceneMode.Single load scene event are migrated into the DDOL
|
// Dynamically spawned NetworkObjects that occur during a LoadSceneMode.Single load scene event are migrated into the DDOL
|
||||||
// until the scene is loaded. They are then migrated back into the newly loaded and currently active scene.
|
// until the scene is loaded. They are then migrated back into the newly loaded and currently active scene.
|
||||||
if (!sceneObject.Header.IsSceneObject && NetworkSceneManager.IsSpawnedObjectsPendingInDontDestroyOnLoad)
|
if (!sceneObject.IsSceneObject && NetworkSceneManager.IsSpawnedObjectsPendingInDontDestroyOnLoad)
|
||||||
{
|
{
|
||||||
UnityEngine.Object.DontDestroyOnLoad(networkObject.gameObject);
|
UnityEngine.Object.DontDestroyOnLoad(networkObject.gameObject);
|
||||||
}
|
}
|
||||||
@@ -490,8 +504,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ran on both server and client
|
// Ran on both server and client
|
||||||
internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject,
|
internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject, bool destroyWithScene)
|
||||||
FastBufferReader variableData, bool destroyWithScene)
|
|
||||||
{
|
{
|
||||||
if (networkObject == null)
|
if (networkObject == null)
|
||||||
{
|
{
|
||||||
@@ -503,9 +516,7 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is already spawned");
|
throw new SpawnStateException("Object is already spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
networkObject.SetNetworkVariableData(variableData);
|
SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.NetworkObjectId, sceneObject.IsSceneObject, sceneObject.IsPlayerObject, sceneObject.OwnerClientId, destroyWithScene);
|
||||||
|
|
||||||
SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Header.NetworkObjectId, sceneObject.Header.IsSceneObject, sceneObject.Header.IsPlayerObject, sceneObject.Header.OwnerClientId, destroyWithScene);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene)
|
private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene)
|
||||||
@@ -647,7 +658,11 @@ namespace Unity.Netcode
|
|||||||
// Makes scene objects ready to be reused
|
// Makes scene objects ready to be reused
|
||||||
internal void ServerResetShudownStateForSceneObjects()
|
internal void ServerResetShudownStateForSceneObjects()
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID).Where((c) => c.IsSceneObject != null && c.IsSceneObject == true);
|
||||||
|
#else
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsSceneObject != null && c.IsSceneObject == true);
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsSceneObject != null && c.IsSceneObject == true);
|
||||||
|
#endif
|
||||||
foreach (var sobj in networkObjects)
|
foreach (var sobj in networkObjects)
|
||||||
{
|
{
|
||||||
sobj.IsSpawned = false;
|
sobj.IsSpawned = false;
|
||||||
@@ -678,7 +693,11 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void DespawnAndDestroyNetworkObjects()
|
internal void DespawnAndDestroyNetworkObjects()
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID);
|
||||||
|
#else
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
||||||
|
#endif
|
||||||
|
|
||||||
for (int i = 0; i < networkObjects.Length; i++)
|
for (int i = 0; i < networkObjects.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -708,7 +727,11 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void DestroySceneObjects()
|
internal void DestroySceneObjects()
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID);
|
||||||
|
#else
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
||||||
|
#endif
|
||||||
|
|
||||||
for (int i = 0; i < networkObjects.Length; i++)
|
for (int i = 0; i < networkObjects.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -735,7 +758,11 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void ServerSpawnSceneObjectsOnStartSweep()
|
internal void ServerSpawnSceneObjectsOnStartSweep()
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID);
|
||||||
|
#else
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
||||||
|
#endif
|
||||||
var networkObjectsToSpawn = new List<NetworkObject>();
|
var networkObjectsToSpawn = new List<NetworkObject>();
|
||||||
|
|
||||||
for (int i = 0; i < networkObjects.Length; i++)
|
for (int i = 0; i < networkObjects.Length; i++)
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ using System.Collections.Generic;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using NetcodeNetworkEvent = Unity.Netcode.NetworkEvent;
|
using NetcodeNetworkEvent = Unity.Netcode.NetworkEvent;
|
||||||
using TransportNetworkEvent = Unity.Networking.Transport.NetworkEvent;
|
using TransportNetworkEvent = Unity.Networking.Transport.NetworkEvent;
|
||||||
|
using Unity.Burst;
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
|
using Unity.Jobs;
|
||||||
using Unity.Networking.Transport;
|
using Unity.Networking.Transport;
|
||||||
using Unity.Networking.Transport.Relay;
|
using Unity.Networking.Transport.Relay;
|
||||||
using Unity.Networking.Transport.Utilities;
|
using Unity.Networking.Transport.Utilities;
|
||||||
@@ -51,50 +53,48 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ErrorUtilities
|
public static class ErrorUtilities
|
||||||
{
|
{
|
||||||
private const string k_NetworkSuccess = "Success";
|
private static readonly FixedString128Bytes k_NetworkSuccess = "Success";
|
||||||
private const string k_NetworkIdMismatch = "NetworkId is invalid, likely caused by stale connection {0}.";
|
private static readonly FixedString128Bytes k_NetworkIdMismatch = "Invalid connection ID {0}.";
|
||||||
private const string k_NetworkVersionMismatch = "NetworkVersion is invalid, likely caused by stale connection {0}.";
|
private static readonly FixedString128Bytes k_NetworkVersionMismatch = "Connection ID is invalid. Likely caused by sending on stale connection {0}.";
|
||||||
private const string k_NetworkStateMismatch = "Sending data while connecting on connection {0} is not allowed.";
|
private static readonly FixedString128Bytes k_NetworkStateMismatch = "Connection state is invalid. Likely caused by sending on connection {0} which is stale or still connecting.";
|
||||||
private const string k_NetworkPacketOverflow = "Unable to allocate packet due to buffer overflow.";
|
private static readonly FixedString128Bytes k_NetworkPacketOverflow = "Packet is too large to be allocated by the transport.";
|
||||||
private const string k_NetworkSendQueueFull = "Currently unable to queue packet as there is too many in-flight packets. This could be because the send queue size ('Max Send Queue Size') is too small.";
|
private static readonly FixedString128Bytes k_NetworkSendQueueFull = "Unable to queue packet in the transport. Likely caused by send queue size ('Max Send Queue Size') being too small.";
|
||||||
private const string k_NetworkHeaderInvalid = "Invalid Unity Transport Protocol header.";
|
|
||||||
private const string k_NetworkDriverParallelForErr = "The parallel network driver needs to process a single unique connection per job, processing a single connection multiple times in a parallel for is not supported.";
|
|
||||||
private const string k_NetworkSendHandleInvalid = "Invalid NetworkInterface Send Handle. Likely caused by pipeline send data corruption.";
|
|
||||||
private const string k_NetworkArgumentMismatch = "Invalid NetworkEndpoint Arguments.";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert error code to human readable error message.
|
/// Convert a UTP error code to human-readable error message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="error">Status code of the error</param>
|
/// <param name="error">UTP error code.</param>
|
||||||
/// <param name="connectionId">Subject connection ID of the error</param>
|
/// <param name="connectionId">ID of the connection on which the error occurred.</param>
|
||||||
/// <returns>Human readable error message.</returns>
|
/// <returns>Human-readable error message.</returns>
|
||||||
public static string ErrorToString(Networking.Transport.Error.StatusCode error, ulong connectionId)
|
public static string ErrorToString(Networking.Transport.Error.StatusCode error, ulong connectionId)
|
||||||
{
|
{
|
||||||
switch (error)
|
return ErrorToString((int)error, connectionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string ErrorToString(int error, ulong connectionId)
|
||||||
|
{
|
||||||
|
return ErrorToFixedString(error, connectionId).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static FixedString128Bytes ErrorToFixedString(int error, ulong connectionId)
|
||||||
|
{
|
||||||
|
switch ((Networking.Transport.Error.StatusCode)error)
|
||||||
{
|
{
|
||||||
case Networking.Transport.Error.StatusCode.Success:
|
case Networking.Transport.Error.StatusCode.Success:
|
||||||
return k_NetworkSuccess;
|
return k_NetworkSuccess;
|
||||||
case Networking.Transport.Error.StatusCode.NetworkIdMismatch:
|
case Networking.Transport.Error.StatusCode.NetworkIdMismatch:
|
||||||
return string.Format(k_NetworkIdMismatch, connectionId);
|
return FixedString.Format(k_NetworkIdMismatch, connectionId);
|
||||||
case Networking.Transport.Error.StatusCode.NetworkVersionMismatch:
|
case Networking.Transport.Error.StatusCode.NetworkVersionMismatch:
|
||||||
return string.Format(k_NetworkVersionMismatch, connectionId);
|
return FixedString.Format(k_NetworkVersionMismatch, connectionId);
|
||||||
case Networking.Transport.Error.StatusCode.NetworkStateMismatch:
|
case Networking.Transport.Error.StatusCode.NetworkStateMismatch:
|
||||||
return string.Format(k_NetworkStateMismatch, connectionId);
|
return FixedString.Format(k_NetworkStateMismatch, connectionId);
|
||||||
case Networking.Transport.Error.StatusCode.NetworkPacketOverflow:
|
case Networking.Transport.Error.StatusCode.NetworkPacketOverflow:
|
||||||
return k_NetworkPacketOverflow;
|
return k_NetworkPacketOverflow;
|
||||||
case Networking.Transport.Error.StatusCode.NetworkSendQueueFull:
|
case Networking.Transport.Error.StatusCode.NetworkSendQueueFull:
|
||||||
return k_NetworkSendQueueFull;
|
return k_NetworkSendQueueFull;
|
||||||
case Networking.Transport.Error.StatusCode.NetworkHeaderInvalid:
|
default:
|
||||||
return k_NetworkHeaderInvalid;
|
return FixedString.Format("Unknown error code {0}.", error);
|
||||||
case Networking.Transport.Error.StatusCode.NetworkDriverParallelForErr:
|
|
||||||
return k_NetworkDriverParallelForErr;
|
|
||||||
case Networking.Transport.Error.StatusCode.NetworkSendHandleInvalid:
|
|
||||||
return k_NetworkSendHandleInvalid;
|
|
||||||
case Networking.Transport.Error.StatusCode.NetworkArgumentMismatch:
|
|
||||||
return k_NetworkArgumentMismatch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"Unknown ErrorCode {Enum.GetName(typeof(Networking.Transport.Error.StatusCode), error)}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,53 +676,72 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BurstCompile]
|
||||||
|
private struct SendBatchedMessagesJob : IJob
|
||||||
|
{
|
||||||
|
public NetworkDriver.Concurrent Driver;
|
||||||
|
public SendTarget Target;
|
||||||
|
public BatchedSendQueue Queue;
|
||||||
|
public NetworkPipeline ReliablePipeline;
|
||||||
|
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
var clientId = Target.ClientId;
|
||||||
|
var connection = ParseClientId(clientId);
|
||||||
|
var pipeline = Target.NetworkPipeline;
|
||||||
|
|
||||||
|
while (!Queue.IsEmpty)
|
||||||
|
{
|
||||||
|
var result = Driver.BeginSend(pipeline, connection, out var writer);
|
||||||
|
if (result != (int)Networking.Transport.Error.StatusCode.Success)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error sending message: {ErrorUtilities.ErrorToFixedString(result, clientId)}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't attempt to send entire payloads over the reliable pipeline. Instead we
|
||||||
|
// fragment it manually. This is safe and easy to do since the reliable pipeline
|
||||||
|
// basically implements a stream, so as long as we separate the different messages
|
||||||
|
// in the stream (the send queue does that automatically) we are sure they'll be
|
||||||
|
// reassembled properly at the other end. This allows us to lift the limit of ~44KB
|
||||||
|
// on reliable payloads (because of the reliable window size).
|
||||||
|
var written = pipeline == ReliablePipeline ? Queue.FillWriterWithBytes(ref writer) : Queue.FillWriterWithMessages(ref writer);
|
||||||
|
|
||||||
|
result = Driver.EndSend(writer);
|
||||||
|
if (result == written)
|
||||||
|
{
|
||||||
|
// Batched message was sent successfully. Remove it from the queue.
|
||||||
|
Queue.Consume(written);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Some error occured. If it's just the UTP queue being full, then don't log
|
||||||
|
// anything since that's okay (the unsent message(s) are still in the queue
|
||||||
|
// and we'll retry sending them later). Otherwise log the error and remove the
|
||||||
|
// message from the queue (we don't want to resend it again since we'll likely
|
||||||
|
// just get the same error again).
|
||||||
|
if (result != (int)Networking.Transport.Error.StatusCode.NetworkSendQueueFull)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error sending the message: {ErrorUtilities.ErrorToFixedString(result, clientId)}");
|
||||||
|
Queue.Consume(written);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send as many batched messages from the queue as possible.
|
// Send as many batched messages from the queue as possible.
|
||||||
private void SendBatchedMessages(SendTarget sendTarget, BatchedSendQueue queue)
|
private void SendBatchedMessages(SendTarget sendTarget, BatchedSendQueue queue)
|
||||||
{
|
{
|
||||||
var clientId = sendTarget.ClientId;
|
new SendBatchedMessagesJob
|
||||||
var connection = ParseClientId(clientId);
|
|
||||||
var pipeline = sendTarget.NetworkPipeline;
|
|
||||||
|
|
||||||
while (!queue.IsEmpty)
|
|
||||||
{
|
{
|
||||||
var result = m_Driver.BeginSend(pipeline, connection, out var writer);
|
Driver = m_Driver.ToConcurrent(),
|
||||||
if (result != (int)Networking.Transport.Error.StatusCode.Success)
|
Target = sendTarget,
|
||||||
{
|
Queue = queue,
|
||||||
Debug.LogError("Error sending the message: " +
|
ReliablePipeline = m_ReliableSequencedPipeline
|
||||||
ErrorUtilities.ErrorToString((Networking.Transport.Error.StatusCode)result, clientId));
|
}.Run();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't attempt to send entire payloads over the reliable pipeline. Instead we
|
|
||||||
// fragment it manually. This is safe and easy to do since the reliable pipeline
|
|
||||||
// basically implements a stream, so as long as we separate the different messages
|
|
||||||
// in the stream (the send queue does that automatically) we are sure they'll be
|
|
||||||
// reassembled properly at the other end. This allows us to lift the limit of ~44KB
|
|
||||||
// on reliable payloads (because of the reliable window size).
|
|
||||||
var written = pipeline == m_ReliableSequencedPipeline ? queue.FillWriterWithBytes(ref writer) : queue.FillWriterWithMessages(ref writer);
|
|
||||||
|
|
||||||
result = m_Driver.EndSend(writer);
|
|
||||||
if (result == written)
|
|
||||||
{
|
|
||||||
// Batched message was sent successfully. Remove it from the queue.
|
|
||||||
queue.Consume(written);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Some error occured. If it's just the UTP queue being full, then don't log
|
|
||||||
// anything since that's okay (the unsent message(s) are still in the queue
|
|
||||||
// and we'll retry sending the later). Otherwise log the error and remove the
|
|
||||||
// message from the queue (we don't want to resend it again since we'll likely
|
|
||||||
// just get the same error again).
|
|
||||||
if (result != (int)Networking.Transport.Error.StatusCode.NetworkSendQueueFull)
|
|
||||||
{
|
|
||||||
Debug.LogError("Error sending the message: " + ErrorUtilities.ErrorToString((Networking.Transport.Error.StatusCode)result, clientId));
|
|
||||||
queue.Consume(written);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AcceptConnection()
|
private bool AcceptConnection()
|
||||||
@@ -1441,7 +1460,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
{
|
{
|
||||||
if (m_ProtocolType == ProtocolType.RelayUnityTransport)
|
if (m_ProtocolType == ProtocolType.RelayUnityTransport)
|
||||||
{
|
{
|
||||||
if (m_RelayServerData.IsSecure != 0)
|
if (m_RelayServerData.IsSecure == 0)
|
||||||
{
|
{
|
||||||
// log an error because we have mismatched configuration
|
// log an error because we have mismatched configuration
|
||||||
Debug.LogError("Mismatched security configuration, between Relay and local NetworkManager settings");
|
Debug.LogError("Mismatched security configuration, between Relay and local NetworkManager settings");
|
||||||
@@ -1452,36 +1471,30 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try
|
if (NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
if (NetworkManager.IsServer)
|
if (String.IsNullOrEmpty(m_ServerCertificate) || String.IsNullOrEmpty(m_ServerPrivateKey))
|
||||||
{
|
{
|
||||||
if (m_ServerCertificate.Length == 0 || m_ServerPrivateKey.Length == 0)
|
throw new Exception("In order to use encrypted communications, when hosting, you must set the server certificate and key.");
|
||||||
{
|
}
|
||||||
throw new Exception("In order to use encrypted communications, when hosting, you must set the server certificate and key.");
|
|
||||||
}
|
m_NetworkSettings.WithSecureServerParameters(m_ServerCertificate, m_ServerPrivateKey);
|
||||||
m_NetworkSettings.WithSecureServerParameters(m_ServerCertificate, m_ServerPrivateKey);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (String.IsNullOrEmpty(m_ServerCommonName))
|
||||||
|
{
|
||||||
|
throw new Exception("In order to use encrypted communications, clients must set the server common name.");
|
||||||
|
}
|
||||||
|
else if (String.IsNullOrEmpty(m_ClientCaCertificate))
|
||||||
|
{
|
||||||
|
m_NetworkSettings.WithSecureClientParameters(m_ServerCommonName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_ServerCommonName.Length == 0)
|
m_NetworkSettings.WithSecureClientParameters(m_ClientCaCertificate, m_ServerCommonName);
|
||||||
{
|
|
||||||
throw new Exception("In order to use encrypted communications, clients must set the server common name.");
|
|
||||||
}
|
|
||||||
else if (m_ClientCaCertificate == null)
|
|
||||||
{
|
|
||||||
m_NetworkSettings.WithSecureClientParameters(m_ServerCommonName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_NetworkSettings.WithSecureClientParameters(m_ClientCaCertificate, m_ServerCommonName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
Debug.LogException(e, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
63
TestHelpers/Runtime/DebugNetworkHooks.cs
Normal file
63
TestHelpers/Runtime/DebugNetworkHooks.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.TestHelpers.Runtime
|
||||||
|
{
|
||||||
|
internal class DebugNetworkHooks : INetworkHooks
|
||||||
|
{
|
||||||
|
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
Debug.Log($"Sending message of type {typeof(T).Name} to {clientId} ({messageSizeBytes} bytes)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
||||||
|
{
|
||||||
|
Debug.Log($"Receiving message of type {messageType.Name} from {senderId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
|
||||||
|
{
|
||||||
|
Debug.Log($"==> Sending a batch of {messageCount} messages ({batchSizeInBytes} bytes) to {clientId} ({delivery})");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
|
||||||
|
{
|
||||||
|
Debug.Log($"<== Receiving a batch of {messageCount} messages ({batchSizeInBytes} bytes) from {senderId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnVerifyCanReceive(ulong senderId, Type messageType, FastBufferReader messageContent, ref NetworkContext context)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
Debug.Log($"Handling a message of type {typeof(T).Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
TestHelpers/Runtime/DebugNetworkHooks.cs.meta
Normal file
3
TestHelpers/Runtime/DebugNetworkHooks.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b5f8098e713443eeba116a25de551cf8
|
||||||
|
timeCreated: 1666626883
|
||||||
@@ -147,7 +147,12 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
private static void ProcessInSceneObjects(Scene scene, NetworkManager networkManager)
|
private static void ProcessInSceneObjects(Scene scene, NetworkManager networkManager)
|
||||||
{
|
{
|
||||||
// Get all in-scene placed NeworkObjects that were instantiated when this scene loaded
|
// Get all in-scene placed NeworkObjects that were instantiated when this scene loaded
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var inSceneNetworkObjects = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID).Where((c) => c.IsSceneObject != false && c.GetSceneOriginHandle() == scene.handle);
|
||||||
|
#else
|
||||||
var inSceneNetworkObjects = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsSceneObject != false && c.GetSceneOriginHandle() == scene.handle);
|
var inSceneNetworkObjects = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsSceneObject != false && c.GetSceneOriginHandle() == scene.handle);
|
||||||
|
#endif
|
||||||
|
|
||||||
foreach (var sobj in inSceneNetworkObjects)
|
foreach (var sobj in inSceneNetworkObjects)
|
||||||
{
|
{
|
||||||
if (sobj.NetworkManagerOwner != networkManager)
|
if (sobj.NetworkManagerOwner != networkManager)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
||||||
{
|
{
|
||||||
if (!CurrentMessageHasTriggerdAHook && IsWaiting && (HandleCheck == null || HandleCheck.Invoke(messageType)))
|
if (!CurrentMessageHasTriggerdAHook && IsWaiting && ReceiptCheck != null && ReceiptCheck.Invoke(messageType))
|
||||||
{
|
{
|
||||||
IsWaiting = false;
|
IsWaiting = false;
|
||||||
CurrentMessageHasTriggerdAHook = true;
|
CurrentMessageHasTriggerdAHook = true;
|
||||||
@@ -92,7 +92,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
{
|
{
|
||||||
if (!CurrentMessageHasTriggerdAHook && IsWaiting && (HandleCheck == null || HandleCheck.Invoke(message)))
|
if (!CurrentMessageHasTriggerdAHook && IsWaiting && HandleCheck != null && HandleCheck.Invoke(message))
|
||||||
{
|
{
|
||||||
IsWaiting = false;
|
IsWaiting = false;
|
||||||
CurrentMessageHasTriggerdAHook = true;
|
CurrentMessageHasTriggerdAHook = true;
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
if (AllMessagesReceived)
|
if (AllMessagesReceived)
|
||||||
{
|
{
|
||||||
return AllMessagesReceived;
|
foreach (var entry in m_MessageHookEntries)
|
||||||
|
{
|
||||||
|
entry.RemoveHook();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AllMessagesReceived;
|
return AllMessagesReceived;
|
||||||
@@ -110,6 +113,11 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void RemoveHook()
|
||||||
|
{
|
||||||
|
m_NetworkManager.MessagingSystem.Unhook(MessageHooks);
|
||||||
|
}
|
||||||
|
|
||||||
internal void AssignMessageType(Type type)
|
internal void AssignMessageType(Type type)
|
||||||
{
|
{
|
||||||
MessageType = type.Name;
|
MessageType = type.Name;
|
||||||
|
|||||||
@@ -274,16 +274,6 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
CreateServerAndClients(NumberOfClients);
|
CreateServerAndClients(NumberOfClients);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnNewClientCreated(NetworkManager networkManager)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnNewClientStartedAndConnected(NetworkManager networkManager)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddRemoveNetworkManager(NetworkManager networkManager, bool addNetworkManager)
|
private void AddRemoveNetworkManager(NetworkManager networkManager, bool addNetworkManager)
|
||||||
{
|
{
|
||||||
var clientNetworkManagersList = new List<NetworkManager>(m_ClientNetworkManagers);
|
var clientNetworkManagersList = new List<NetworkManager>(m_ClientNetworkManagers);
|
||||||
@@ -299,6 +289,37 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
m_NumberOfClients = clientNetworkManagersList.Count;
|
m_NumberOfClients = clientNetworkManagersList.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CreateAndStartNewClient Only
|
||||||
|
/// Invoked when the newly created client has been created
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnNewClientCreated(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CreateAndStartNewClient Only
|
||||||
|
/// Invoked when the newly created client has been created and started
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnNewClientStarted(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CreateAndStartNewClient Only
|
||||||
|
/// Invoked when the newly created client has been created, started, and connected
|
||||||
|
/// to the server-host.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnNewClientStartedAndConnected(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This will create, start, and connect a new client while in the middle of an
|
||||||
|
/// integration test.
|
||||||
|
/// </summary>
|
||||||
protected IEnumerator CreateAndStartNewClient()
|
protected IEnumerator CreateAndStartNewClient()
|
||||||
{
|
{
|
||||||
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length);
|
var networkManager = NetcodeIntegrationTestHelpers.CreateNewClient(m_ClientNetworkManagers.Length);
|
||||||
@@ -309,9 +330,20 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
OnNewClientCreated(networkManager);
|
OnNewClientCreated(networkManager);
|
||||||
|
|
||||||
NetcodeIntegrationTestHelpers.StartOneClient(networkManager);
|
NetcodeIntegrationTestHelpers.StartOneClient(networkManager);
|
||||||
|
|
||||||
|
if (LogAllMessages)
|
||||||
|
{
|
||||||
|
networkManager.MessagingSystem.Hook(new DebugNetworkHooks());
|
||||||
|
}
|
||||||
|
|
||||||
AddRemoveNetworkManager(networkManager, true);
|
AddRemoveNetworkManager(networkManager, true);
|
||||||
|
|
||||||
|
OnNewClientStarted(networkManager);
|
||||||
|
|
||||||
// Wait for the new client to connect
|
// Wait for the new client to connect
|
||||||
yield return WaitForClientsConnectedOrTimeOut();
|
yield return WaitForClientsConnectedOrTimeOut();
|
||||||
|
|
||||||
|
OnNewClientStartedAndConnected(networkManager);
|
||||||
if (s_GlobalTimeoutHelper.TimedOut)
|
if (s_GlobalTimeoutHelper.TimedOut)
|
||||||
{
|
{
|
||||||
AddRemoveNetworkManager(networkManager, false);
|
AddRemoveNetworkManager(networkManager, false);
|
||||||
@@ -322,6 +354,9 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
VerboseDebug($"[{networkManager.name}] Created and connected!");
|
VerboseDebug($"[{networkManager.name}] Created and connected!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This will stop a client while in the middle of an integration test
|
||||||
|
/// </summary>
|
||||||
protected IEnumerator StopOneClient(NetworkManager networkManager, bool destroy = false)
|
protected IEnumerator StopOneClient(NetworkManager networkManager, bool destroy = false)
|
||||||
{
|
{
|
||||||
NetcodeIntegrationTestHelpers.StopOneClient(networkManager, destroy);
|
NetcodeIntegrationTestHelpers.StopOneClient(networkManager, destroy);
|
||||||
@@ -401,8 +436,19 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
networkManager.name = $"NetworkManager - Client - {networkManager.LocalClientId}";
|
networkManager.name = $"NetworkManager - Client - {networkManager.LocalClientId}";
|
||||||
Assert.NotNull(networkManager.LocalClient.PlayerObject, $"{nameof(StartServerAndClients)} detected that client {networkManager.LocalClientId} does not have an assigned player NetworkObject!");
|
Assert.NotNull(networkManager.LocalClient.PlayerObject, $"{nameof(StartServerAndClients)} detected that client {networkManager.LocalClientId} does not have an assigned player NetworkObject!");
|
||||||
|
|
||||||
|
// Go ahead and create an entry for this new client
|
||||||
|
if (!m_PlayerNetworkObjects.ContainsKey(networkManager.LocalClientId))
|
||||||
|
{
|
||||||
|
m_PlayerNetworkObjects.Add(networkManager.LocalClientId, new Dictionary<ulong, NetworkObject>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
// Get all player instances for the current client NetworkManager instance
|
// Get all player instances for the current client NetworkManager instance
|
||||||
var clientPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == networkManager.LocalClientId);
|
var clientPlayerClones = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.None).Where((c) => c.IsPlayerObject && c.OwnerClientId == networkManager.LocalClientId).ToList();
|
||||||
|
#else
|
||||||
|
// Get all player instances for the current client NetworkManager instance
|
||||||
|
var clientPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == networkManager.LocalClientId).ToList();
|
||||||
|
#endif
|
||||||
// Add this player instance to each client player entry
|
// Add this player instance to each client player entry
|
||||||
foreach (var playerNetworkObject in clientPlayerClones)
|
foreach (var playerNetworkObject in clientPlayerClones)
|
||||||
{
|
{
|
||||||
@@ -416,6 +462,20 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
m_PlayerNetworkObjects[playerNetworkObject.NetworkManager.LocalClientId].Add(networkManager.LocalClientId, playerNetworkObject);
|
m_PlayerNetworkObjects[playerNetworkObject.NetworkManager.LocalClientId].Add(networkManager.LocalClientId, playerNetworkObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
// For late joining clients, add the remaining (if any) cloned versions of each client's player
|
||||||
|
clientPlayerClones = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.None).Where((c) => c.IsPlayerObject && c.NetworkManager == networkManager).ToList();
|
||||||
|
#else
|
||||||
|
// For late joining clients, add the remaining (if any) cloned versions of each client's player
|
||||||
|
clientPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.NetworkManager == networkManager).ToList();
|
||||||
|
#endif
|
||||||
|
foreach (var playerNetworkObject in clientPlayerClones)
|
||||||
|
{
|
||||||
|
if (!m_PlayerNetworkObjects[networkManager.LocalClientId].ContainsKey(playerNetworkObject.OwnerClientId))
|
||||||
|
{
|
||||||
|
m_PlayerNetworkObjects[networkManager.LocalClientId].Add(playerNetworkObject.OwnerClientId, playerNetworkObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ClientNetworkManagerPostStartInit()
|
protected void ClientNetworkManagerPostStartInit()
|
||||||
@@ -428,7 +488,11 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
}
|
}
|
||||||
if (m_UseHost)
|
if (m_UseHost)
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var clientSideServerPlayerClones = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.None).Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
|
||||||
|
#else
|
||||||
var clientSideServerPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
|
var clientSideServerPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
|
||||||
|
#endif
|
||||||
foreach (var playerNetworkObject in clientSideServerPlayerClones)
|
foreach (var playerNetworkObject in clientSideServerPlayerClones)
|
||||||
{
|
{
|
||||||
// When the server is not the host this needs to be done
|
// When the server is not the host this needs to be done
|
||||||
@@ -444,6 +508,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual bool LogAllMessages => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This starts the server and clients as long as <see cref="CanStartServerAndClients"/>
|
/// This starts the server and clients as long as <see cref="CanStartServerAndClients"/>
|
||||||
/// returns true.
|
/// returns true.
|
||||||
@@ -462,6 +528,11 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
Assert.Fail("Failed to start instances");
|
Assert.Fail("Failed to start instances");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LogAllMessages)
|
||||||
|
{
|
||||||
|
EnableMessageLogging();
|
||||||
|
}
|
||||||
|
|
||||||
RegisterSceneManagerHandler();
|
RegisterSceneManagerHandler();
|
||||||
|
|
||||||
// Notification that the server and clients have been started
|
// Notification that the server and clients have been started
|
||||||
@@ -477,8 +548,13 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
if (m_UseHost || m_ServerNetworkManager.IsHost)
|
if (m_UseHost || m_ServerNetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
|
||||||
|
var serverPlayerClones = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.None).Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
|
||||||
|
#else
|
||||||
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
|
// Add the server player instance to all m_ClientSidePlayerNetworkObjects entries
|
||||||
var serverPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
|
var serverPlayerClones = Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsPlayerObject && c.OwnerClientId == m_ServerNetworkManager.LocalClientId);
|
||||||
|
#endif
|
||||||
foreach (var playerNetworkObject in serverPlayerClones)
|
foreach (var playerNetworkObject in serverPlayerClones)
|
||||||
{
|
{
|
||||||
if (!m_PlayerNetworkObjects.ContainsKey(playerNetworkObject.NetworkManager.LocalClientId))
|
if (!m_PlayerNetworkObjects.ContainsKey(playerNetworkObject.NetworkManager.LocalClientId))
|
||||||
@@ -668,7 +744,11 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected void DestroySceneNetworkObjects()
|
protected void DestroySceneNetworkObjects()
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkObjects = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID);
|
||||||
|
#else
|
||||||
var networkObjects = Object.FindObjectsOfType<NetworkObject>();
|
var networkObjects = Object.FindObjectsOfType<NetworkObject>();
|
||||||
|
#endif
|
||||||
foreach (var networkObject in networkObjects)
|
foreach (var networkObject in networkObjects)
|
||||||
{
|
{
|
||||||
// This can sometimes be null depending upon order of operations
|
// This can sometimes be null depending upon order of operations
|
||||||
@@ -692,6 +772,18 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For debugging purposes, this will turn on verbose logging of all messages and batches sent and received
|
||||||
|
/// </summary>
|
||||||
|
protected void EnableMessageLogging()
|
||||||
|
{
|
||||||
|
m_ServerNetworkManager.MessagingSystem.Hook(new DebugNetworkHooks());
|
||||||
|
foreach (var client in m_ClientNetworkManagers)
|
||||||
|
{
|
||||||
|
client.MessagingSystem.Hook(new DebugNetworkHooks());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Waits for the function condition to return true or it will time out.
|
/// Waits for the function condition to return true or it will time out.
|
||||||
/// This will operate at the current m_ServerNetworkManager.NetworkConfig.TickRate
|
/// This will operate at the current m_ServerNetworkManager.NetworkConfig.TickRate
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public const string DefaultBuildScenePath = "Tests/Editor/Build/BuildTestScene.unity";
|
public const string DefaultBuildScenePath = "Tests/Editor/Build/BuildTestScene.unity";
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
[Ignore("Disabling this test on release/1.2.0 branch due to Burst failures caused when running with upm ci")]
|
||||||
public void BasicBuildTest()
|
public void BasicBuildTest()
|
||||||
{
|
{
|
||||||
var execAssembly = Assembly.GetExecutingAssembly();
|
var execAssembly = Assembly.GetExecutingAssembly();
|
||||||
|
|||||||
56
Tests/Editor/DisconnectMessageTests.cs
Normal file
56
Tests/Editor/DisconnectMessageTests.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Collections;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.EditorTests
|
||||||
|
{
|
||||||
|
public class DisconnectMessageTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void EmptyDisconnectReason()
|
||||||
|
{
|
||||||
|
var networkContext = new NetworkContext();
|
||||||
|
var writer = new FastBufferWriter(20, Allocator.Temp, 20);
|
||||||
|
var msg = new DisconnectReasonMessage();
|
||||||
|
msg.Reason = string.Empty;
|
||||||
|
msg.Serialize(writer, msg.Version);
|
||||||
|
|
||||||
|
var fbr = new FastBufferReader(writer, Allocator.Temp);
|
||||||
|
var recvMsg = new DisconnectReasonMessage();
|
||||||
|
recvMsg.Deserialize(fbr, ref networkContext, msg.Version);
|
||||||
|
|
||||||
|
Assert.IsEmpty(recvMsg.Reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void DisconnectReason()
|
||||||
|
{
|
||||||
|
var networkContext = new NetworkContext();
|
||||||
|
var writer = new FastBufferWriter(20, Allocator.Temp, 20);
|
||||||
|
var msg = new DisconnectReasonMessage();
|
||||||
|
msg.Reason = "Foo";
|
||||||
|
msg.Serialize(writer, msg.Version);
|
||||||
|
|
||||||
|
var fbr = new FastBufferReader(writer, Allocator.Temp);
|
||||||
|
var recvMsg = new DisconnectReasonMessage();
|
||||||
|
recvMsg.Deserialize(fbr, ref networkContext, msg.Version);
|
||||||
|
|
||||||
|
Assert.AreEqual("Foo", recvMsg.Reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void DisconnectReasonTooLong()
|
||||||
|
{
|
||||||
|
var networkContext = new NetworkContext();
|
||||||
|
var writer = new FastBufferWriter(20, Allocator.Temp, 20);
|
||||||
|
var msg = new DisconnectReasonMessage();
|
||||||
|
msg.Reason = "ThisStringIsWayLongerThanTwentyBytes";
|
||||||
|
msg.Serialize(writer, msg.Version);
|
||||||
|
|
||||||
|
var fbr = new FastBufferReader(writer, Allocator.Temp);
|
||||||
|
var recvMsg = new DisconnectReasonMessage();
|
||||||
|
recvMsg.Deserialize(fbr, ref networkContext, msg.Version);
|
||||||
|
|
||||||
|
Assert.IsEmpty(recvMsg.Reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Tests/Editor/DisconnectMessageTests.cs.meta
Normal file
11
Tests/Editor/DisconnectMessageTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 55a1355c62fe14a118253f8bbee7c3cf
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -18,12 +18,12 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public static bool Handled;
|
public static bool Handled;
|
||||||
public static List<TestMessage> DeserializedValues = new List<TestMessage>();
|
public static List<TestMessage> DeserializedValues = new List<TestMessage>();
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(this);
|
writer.WriteValueSafe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
Deserialized = true;
|
Deserialized = true;
|
||||||
reader.ReadValueSafe(out this);
|
reader.ReadValueSafe(out this);
|
||||||
@@ -35,6 +35,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
Handled = true;
|
Handled = true;
|
||||||
DeserializedValues.Add(this);
|
DeserializedValues.Add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestMessageProvider : IMessageProvider
|
private class TestMessageProvider : IMessageProvider
|
||||||
@@ -46,7 +48,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
new MessagingSystem.MessageWithHandler
|
new MessagingSystem.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessage),
|
MessageType = typeof(TestMessage),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessage>
|
Handler = MessagingSystem.ReceiveMessage<TestMessage>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessage>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -62,6 +65,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
TestMessage.DeserializedValues.Clear();
|
TestMessage.DeserializedValues.Clear();
|
||||||
|
|
||||||
m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this, new TestMessageProvider());
|
m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this, new TestMessageProvider());
|
||||||
|
m_MessagingSystem.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
@@ -11,12 +10,12 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public int A;
|
public int A;
|
||||||
public int B;
|
public int B;
|
||||||
public int C;
|
public int C;
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValue(this);
|
writer.WriteValue(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -24,6 +23,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct TestMessageTwo : INetworkMessage, INetworkSerializeByMemcpy
|
private struct TestMessageTwo : INetworkMessage, INetworkSerializeByMemcpy
|
||||||
@@ -31,12 +32,12 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public int A;
|
public int A;
|
||||||
public int B;
|
public int B;
|
||||||
public int C;
|
public int C;
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValue(this);
|
writer.WriteValue(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -44,6 +45,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
private class TestMessageProviderOne : IMessageProvider
|
private class TestMessageProviderOne : IMessageProvider
|
||||||
{
|
{
|
||||||
@@ -54,12 +57,14 @@ namespace Unity.Netcode.EditorTests
|
|||||||
new MessagingSystem.MessageWithHandler
|
new MessagingSystem.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessageOne),
|
MessageType = typeof(TestMessageOne),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessageOne>
|
Handler = MessagingSystem.ReceiveMessage<TestMessageOne>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessageOne>
|
||||||
},
|
},
|
||||||
new MessagingSystem.MessageWithHandler
|
new MessagingSystem.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessageTwo),
|
MessageType = typeof(TestMessageTwo),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessageTwo>
|
Handler = MessagingSystem.ReceiveMessage<TestMessageTwo>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessageTwo>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -70,12 +75,12 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public int A;
|
public int A;
|
||||||
public int B;
|
public int B;
|
||||||
public int C;
|
public int C;
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValue(this);
|
writer.WriteValue(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -83,6 +88,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
private class TestMessageProviderTwo : IMessageProvider
|
private class TestMessageProviderTwo : IMessageProvider
|
||||||
{
|
{
|
||||||
@@ -93,7 +100,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
new MessagingSystem.MessageWithHandler
|
new MessagingSystem.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessageThree),
|
MessageType = typeof(TestMessageThree),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessageThree>
|
Handler = MessagingSystem.ReceiveMessage<TestMessageThree>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessageThree>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -103,12 +111,12 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public int A;
|
public int A;
|
||||||
public int B;
|
public int B;
|
||||||
public int C;
|
public int C;
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
writer.WriteValue(this);
|
writer.WriteValue(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -116,6 +124,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
private class TestMessageProviderThree : IMessageProvider
|
private class TestMessageProviderThree : IMessageProvider
|
||||||
{
|
{
|
||||||
@@ -126,7 +136,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
new MessagingSystem.MessageWithHandler
|
new MessagingSystem.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessageFour),
|
MessageType = typeof(TestMessageFour),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessageFour>
|
Handler = MessagingSystem.ReceiveMessage<TestMessageFour>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessageFour>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -183,11 +194,11 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
internal class AAAEarlyLexicographicNetworkMessage : INetworkMessage
|
internal class AAAEarlyLexicographicNetworkMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -195,6 +206,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable IDE1006
|
#pragma warning disable IDE1006
|
||||||
@@ -212,18 +225,19 @@ namespace Unity.Netcode.EditorTests
|
|||||||
var messageWithHandler = new MessagingSystem.MessageWithHandler();
|
var messageWithHandler = new MessagingSystem.MessageWithHandler();
|
||||||
|
|
||||||
messageWithHandler.MessageType = typeof(zzzLateLexicographicNetworkMessage);
|
messageWithHandler.MessageType = typeof(zzzLateLexicographicNetworkMessage);
|
||||||
|
messageWithHandler.GetVersion = MessagingSystem.CreateMessageAndGetVersion<zzzLateLexicographicNetworkMessage>;
|
||||||
listMessages.Add(messageWithHandler);
|
listMessages.Add(messageWithHandler);
|
||||||
|
|
||||||
messageWithHandler.MessageType = typeof(ConnectionRequestMessage);
|
messageWithHandler.MessageType = typeof(ConnectionRequestMessage);
|
||||||
|
messageWithHandler.GetVersion = MessagingSystem.CreateMessageAndGetVersion<ConnectionRequestMessage>;
|
||||||
listMessages.Add(messageWithHandler);
|
listMessages.Add(messageWithHandler);
|
||||||
|
|
||||||
messageWithHandler.MessageType = typeof(ConnectionApprovedMessage);
|
messageWithHandler.MessageType = typeof(ConnectionApprovedMessage);
|
||||||
listMessages.Add(messageWithHandler);
|
messageWithHandler.GetVersion = MessagingSystem.CreateMessageAndGetVersion<ConnectionApprovedMessage>;
|
||||||
|
|
||||||
messageWithHandler.MessageType = typeof(OrderingMessage);
|
|
||||||
listMessages.Add(messageWithHandler);
|
listMessages.Add(messageWithHandler);
|
||||||
|
|
||||||
messageWithHandler.MessageType = typeof(AAAEarlyLexicographicNetworkMessage);
|
messageWithHandler.MessageType = typeof(AAAEarlyLexicographicNetworkMessage);
|
||||||
|
messageWithHandler.GetVersion = MessagingSystem.CreateMessageAndGetVersion<AAAEarlyLexicographicNetworkMessage>;
|
||||||
listMessages.Add(messageWithHandler);
|
listMessages.Add(messageWithHandler);
|
||||||
|
|
||||||
return listMessages;
|
return listMessages;
|
||||||
@@ -237,65 +251,16 @@ namespace Unity.Netcode.EditorTests
|
|||||||
var provider = new OrderingMessageProvider();
|
var provider = new OrderingMessageProvider();
|
||||||
using var messagingSystem = new MessagingSystem(sender, null, provider);
|
using var messagingSystem = new MessagingSystem(sender, null, provider);
|
||||||
|
|
||||||
// the 3 priority messages should appear first, in lexicographic order
|
// the 2 priority messages should appear first, in lexicographic order
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[0], typeof(ConnectionApprovedMessage));
|
Assert.AreEqual(messagingSystem.MessageTypes[0], typeof(ConnectionApprovedMessage));
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[1], typeof(ConnectionRequestMessage));
|
Assert.AreEqual(messagingSystem.MessageTypes[1], typeof(ConnectionRequestMessage));
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[2], typeof(OrderingMessage));
|
|
||||||
|
|
||||||
// the other should follow after
|
// the other should follow after
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(AAAEarlyLexicographicNetworkMessage));
|
Assert.AreEqual(messagingSystem.MessageTypes[2], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(zzzLateLexicographicNetworkMessage));
|
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
||||||
|
|
||||||
// there should not be any extras
|
// there should not be any extras
|
||||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
Assert.AreEqual(messagingSystem.MessageHandlerCount, 4);
|
||||||
|
|
||||||
// reorder the zzz one to position 3
|
|
||||||
messagingSystem.ReorderMessage(3, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
|
||||||
|
|
||||||
// the 3 priority messages should still appear first, in lexicographic order
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[0], typeof(ConnectionApprovedMessage));
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[1], typeof(ConnectionRequestMessage));
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[2], typeof(OrderingMessage));
|
|
||||||
|
|
||||||
// the other should follow after, but reordered
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(AAAEarlyLexicographicNetworkMessage));
|
|
||||||
|
|
||||||
// there should still not be any extras
|
|
||||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
|
||||||
|
|
||||||
// verify we get an exception when asking for an invalid position
|
|
||||||
try
|
|
||||||
{
|
|
||||||
messagingSystem.ReorderMessage(-1, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
|
||||||
Assert.Fail();
|
|
||||||
}
|
|
||||||
catch (ArgumentException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// reorder the zzz one to position 3, again, to check nothing bad happens
|
|
||||||
messagingSystem.ReorderMessage(3, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
|
||||||
|
|
||||||
// the two non-priority should not have moved
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(AAAEarlyLexicographicNetworkMessage));
|
|
||||||
|
|
||||||
// there should still not be any extras
|
|
||||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
|
||||||
|
|
||||||
// 4242 is a random hash that should not match anything
|
|
||||||
messagingSystem.ReorderMessage(3, 4242);
|
|
||||||
|
|
||||||
// that should result in an extra entry
|
|
||||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 6);
|
|
||||||
|
|
||||||
// with a null handler
|
|
||||||
Assert.AreEqual(messagingSystem.MessageHandlers[3], null);
|
|
||||||
|
|
||||||
// and it should have bumped the previous messages down
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(zzzLateLexicographicNetworkMessage));
|
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[5], typeof(AAAEarlyLexicographicNetworkMessage));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public int B;
|
public int B;
|
||||||
public int C;
|
public int C;
|
||||||
public static bool Serialized;
|
public static bool Serialized;
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
Serialized = true;
|
Serialized = true;
|
||||||
writer.WriteValueSafe(this);
|
writer.WriteValueSafe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestMessageSender : IMessageSender
|
private class TestMessageSender : IMessageSender
|
||||||
@@ -66,7 +68,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
new MessagingSystem.MessageWithHandler
|
new MessagingSystem.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessage),
|
MessageType = typeof(TestMessage),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessage>
|
Handler = MessagingSystem.ReceiveMessage<TestMessage>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessage>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Track messages sent
|
// Track messages sent
|
||||||
@@ -88,6 +91,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
m_TestMessageProvider = new TestMessageProvider();
|
m_TestMessageProvider = new TestMessageProvider();
|
||||||
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, m_TestMessageProvider);
|
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, m_TestMessageProvider);
|
||||||
m_MessagingSystem.ClientConnected(0);
|
m_MessagingSystem.ClientConnected(0);
|
||||||
|
m_MessagingSystem.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
@@ -256,7 +260,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
new MessagingSystem.MessageWithHandler
|
new MessagingSystem.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessage),
|
MessageType = typeof(TestMessage),
|
||||||
Handler = null
|
Handler = null,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessage>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
503
Tests/Editor/Messaging/MessageVersioningTests.cs
Normal file
503
Tests/Editor/Messaging/MessageVersioningTests.cs
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NUnit.Framework.Internal;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.EditorTests
|
||||||
|
{
|
||||||
|
public class MessageVersioningTests
|
||||||
|
{
|
||||||
|
public static int SentVersion;
|
||||||
|
public static int ReceivedVersion;
|
||||||
|
|
||||||
|
private const int k_DefaultB = 5;
|
||||||
|
private const int k_DefaultC = 10;
|
||||||
|
private const int k_DefaultD = 15;
|
||||||
|
private const long k_DefaultE = 20;
|
||||||
|
|
||||||
|
private struct VersionedTestMessage_v0 : INetworkMessage, INetworkSerializeByMemcpy
|
||||||
|
{
|
||||||
|
public int A;
|
||||||
|
public int B;
|
||||||
|
public int C;
|
||||||
|
public static bool Serialized;
|
||||||
|
public static bool Deserialized;
|
||||||
|
public static bool Handled;
|
||||||
|
public static List<VersionedTestMessage_v0> DeserializedValues = new List<VersionedTestMessage_v0>();
|
||||||
|
|
||||||
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
|
{
|
||||||
|
SentVersion = Version;
|
||||||
|
Serialized = true;
|
||||||
|
writer.WriteValueSafe(A);
|
||||||
|
writer.WriteValueSafe(B);
|
||||||
|
writer.WriteValueSafe(C);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
|
{
|
||||||
|
ReceivedVersion = Version;
|
||||||
|
Deserialized = true;
|
||||||
|
reader.ReadValueSafe(out A);
|
||||||
|
reader.ReadValueSafe(out B);
|
||||||
|
reader.ReadValueSafe(out C);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
DeserializedValues.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct VersionedTestMessage_v1 : INetworkMessage, INetworkSerializeByMemcpy
|
||||||
|
{
|
||||||
|
public int A;
|
||||||
|
public int B;
|
||||||
|
public int C;
|
||||||
|
public int D;
|
||||||
|
public static bool Serialized;
|
||||||
|
public static bool Deserialized;
|
||||||
|
public static bool Downgraded;
|
||||||
|
public static bool Upgraded;
|
||||||
|
public static bool Handled;
|
||||||
|
public static List<VersionedTestMessage_v1> DeserializedValues = new List<VersionedTestMessage_v1>();
|
||||||
|
|
||||||
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
|
{
|
||||||
|
if (targetVersion < Version)
|
||||||
|
{
|
||||||
|
Downgraded = true;
|
||||||
|
var v0 = new VersionedTestMessage_v0 { A = A, B = B, C = C };
|
||||||
|
v0.Serialize(writer, targetVersion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SentVersion = Version;
|
||||||
|
Serialized = true;
|
||||||
|
writer.WriteValueSafe(C);
|
||||||
|
writer.WriteValueSafe(D);
|
||||||
|
writer.WriteValueSafe(A);
|
||||||
|
writer.WriteValueSafe(B);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
|
{
|
||||||
|
if (receivedMessageVersion < Version)
|
||||||
|
{
|
||||||
|
var v0 = new VersionedTestMessage_v0();
|
||||||
|
v0.Deserialize(reader, ref context, receivedMessageVersion);
|
||||||
|
A = v0.A;
|
||||||
|
B = v0.B;
|
||||||
|
C = v0.C;
|
||||||
|
D = k_DefaultD;
|
||||||
|
Upgraded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ReceivedVersion = Version;
|
||||||
|
Deserialized = true;
|
||||||
|
reader.ReadValueSafe(out C);
|
||||||
|
reader.ReadValueSafe(out D);
|
||||||
|
reader.ReadValueSafe(out A);
|
||||||
|
reader.ReadValueSafe(out B);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
DeserializedValues.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Version => 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct VersionedTestMessage : INetworkMessage, INetworkSerializeByMemcpy
|
||||||
|
{
|
||||||
|
public int A;
|
||||||
|
public float D;
|
||||||
|
public long E;
|
||||||
|
public static bool Serialized;
|
||||||
|
public static bool Deserialized;
|
||||||
|
public static bool Downgraded;
|
||||||
|
public static bool Upgraded;
|
||||||
|
public static bool Handled;
|
||||||
|
public static List<VersionedTestMessage> DeserializedValues = new List<VersionedTestMessage>();
|
||||||
|
|
||||||
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
|
{
|
||||||
|
if (targetVersion < Version)
|
||||||
|
{
|
||||||
|
Downgraded = true;
|
||||||
|
var v1 = new VersionedTestMessage_v1 { A = A, B = k_DefaultB, C = k_DefaultC, D = (int)D };
|
||||||
|
v1.Serialize(writer, targetVersion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SentVersion = Version;
|
||||||
|
Serialized = true;
|
||||||
|
writer.WriteValueSafe(D);
|
||||||
|
writer.WriteValueSafe(A);
|
||||||
|
writer.WriteValueSafe(E);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
|
{
|
||||||
|
if (receivedMessageVersion < Version)
|
||||||
|
{
|
||||||
|
var v1 = new VersionedTestMessage_v1();
|
||||||
|
v1.Deserialize(reader, ref context, receivedMessageVersion);
|
||||||
|
A = v1.A;
|
||||||
|
D = (float)v1.D;
|
||||||
|
E = k_DefaultE;
|
||||||
|
Upgraded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ReceivedVersion = Version;
|
||||||
|
Deserialized = true;
|
||||||
|
reader.ReadValueSafe(out D);
|
||||||
|
reader.ReadValueSafe(out A);
|
||||||
|
reader.ReadValueSafe(out E);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
Handled = true;
|
||||||
|
DeserializedValues.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Version => 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestMessageProvider_v0 : IMessageProvider
|
||||||
|
{
|
||||||
|
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||||
|
{
|
||||||
|
return new List<MessagingSystem.MessageWithHandler>
|
||||||
|
{
|
||||||
|
new MessagingSystem.MessageWithHandler
|
||||||
|
{
|
||||||
|
MessageType = typeof(VersionedTestMessage_v0),
|
||||||
|
Handler = MessagingSystem.ReceiveMessage<VersionedTestMessage_v0>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<VersionedTestMessage_v0>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestMessageProvider_v1 : IMessageProvider
|
||||||
|
{
|
||||||
|
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||||
|
{
|
||||||
|
return new List<MessagingSystem.MessageWithHandler>
|
||||||
|
{
|
||||||
|
new MessagingSystem.MessageWithHandler
|
||||||
|
{
|
||||||
|
MessageType = typeof(VersionedTestMessage_v1),
|
||||||
|
Handler = MessagingSystem.ReceiveMessage<VersionedTestMessage_v1>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<VersionedTestMessage_v1>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestMessageProvider_v2 : IMessageProvider
|
||||||
|
{
|
||||||
|
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||||
|
{
|
||||||
|
return new List<MessagingSystem.MessageWithHandler>
|
||||||
|
{
|
||||||
|
new MessagingSystem.MessageWithHandler
|
||||||
|
{
|
||||||
|
MessageType = typeof(VersionedTestMessage),
|
||||||
|
Handler = MessagingSystem.ReceiveMessage<VersionedTestMessage>,
|
||||||
|
GetVersion = MessagingSystem.CreateMessageAndGetVersion<VersionedTestMessage>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestMessageSender : IMessageSender
|
||||||
|
{
|
||||||
|
public List<byte[]> MessageQueue = new List<byte[]>();
|
||||||
|
|
||||||
|
public void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData)
|
||||||
|
{
|
||||||
|
MessageQueue.Add(batchData.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessagingSystem m_MessagingSystem_v0;
|
||||||
|
private MessagingSystem m_MessagingSystem_v1;
|
||||||
|
private MessagingSystem m_MessagingSystem_v2;
|
||||||
|
private TestMessageSender m_MessageSender;
|
||||||
|
|
||||||
|
private void CreateFakeClients(MessagingSystem system, uint hash)
|
||||||
|
{
|
||||||
|
// Create three fake clients for each messaging system
|
||||||
|
// client 0 has version 0, client 1 has version 1, and client 2 has version 2
|
||||||
|
system.ClientConnected(0);
|
||||||
|
system.ClientConnected(1);
|
||||||
|
system.ClientConnected(2);
|
||||||
|
system.SetVersion(0, hash, 0);
|
||||||
|
system.SetVersion(1, hash, 1);
|
||||||
|
system.SetVersion(2, hash, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
VersionedTestMessage_v0.Serialized = false;
|
||||||
|
VersionedTestMessage_v0.Deserialized = false;
|
||||||
|
VersionedTestMessage_v0.Handled = false;
|
||||||
|
VersionedTestMessage_v0.DeserializedValues.Clear();
|
||||||
|
VersionedTestMessage_v1.Serialized = false;
|
||||||
|
VersionedTestMessage_v1.Deserialized = false;
|
||||||
|
VersionedTestMessage_v1.Downgraded = false;
|
||||||
|
VersionedTestMessage_v1.Upgraded = false;
|
||||||
|
VersionedTestMessage_v1.Handled = false;
|
||||||
|
VersionedTestMessage_v1.DeserializedValues.Clear();
|
||||||
|
VersionedTestMessage.Serialized = false;
|
||||||
|
VersionedTestMessage.Deserialized = false;
|
||||||
|
VersionedTestMessage.Downgraded = false;
|
||||||
|
VersionedTestMessage.Upgraded = false;
|
||||||
|
VersionedTestMessage.Handled = false;
|
||||||
|
VersionedTestMessage.DeserializedValues.Clear();
|
||||||
|
m_MessageSender = new TestMessageSender();
|
||||||
|
|
||||||
|
m_MessagingSystem_v0 = new MessagingSystem(m_MessageSender, this, new TestMessageProvider_v0());
|
||||||
|
m_MessagingSystem_v1 = new MessagingSystem(m_MessageSender, this, new TestMessageProvider_v1());
|
||||||
|
m_MessagingSystem_v2 = new MessagingSystem(m_MessageSender, this, new TestMessageProvider_v2());
|
||||||
|
|
||||||
|
CreateFakeClients(m_MessagingSystem_v0, XXHash.Hash32(typeof(VersionedTestMessage_v0).FullName));
|
||||||
|
CreateFakeClients(m_MessagingSystem_v1, XXHash.Hash32(typeof(VersionedTestMessage_v1).FullName));
|
||||||
|
CreateFakeClients(m_MessagingSystem_v2, XXHash.Hash32(typeof(VersionedTestMessage).FullName));
|
||||||
|
|
||||||
|
// Make sure that all three messages got the same IDs...
|
||||||
|
Assert.AreEqual(
|
||||||
|
m_MessagingSystem_v0.GetMessageType(typeof(VersionedTestMessage_v0)),
|
||||||
|
m_MessagingSystem_v1.GetMessageType(typeof(VersionedTestMessage_v1)));
|
||||||
|
Assert.AreEqual(
|
||||||
|
m_MessagingSystem_v0.GetMessageType(typeof(VersionedTestMessage_v0)),
|
||||||
|
m_MessagingSystem_v2.GetMessageType(typeof(VersionedTestMessage)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void TearDown()
|
||||||
|
{
|
||||||
|
m_MessagingSystem_v0.Dispose();
|
||||||
|
m_MessagingSystem_v1.Dispose();
|
||||||
|
m_MessagingSystem_v2.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private VersionedTestMessage_v0 GetMessage_v0()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
return new VersionedTestMessage_v0
|
||||||
|
{
|
||||||
|
A = random.Next(),
|
||||||
|
B = random.Next(),
|
||||||
|
C = random.Next(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private VersionedTestMessage_v1 GetMessage_v1()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
return new VersionedTestMessage_v1
|
||||||
|
{
|
||||||
|
A = random.Next(),
|
||||||
|
B = random.Next(),
|
||||||
|
C = random.Next(),
|
||||||
|
D = random.Next(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private VersionedTestMessage GetMessage_v2()
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
return new VersionedTestMessage
|
||||||
|
{
|
||||||
|
A = random.Next(),
|
||||||
|
D = (float)(random.NextDouble() * 10000),
|
||||||
|
E = ((long)random.Next() << 32) + random.Next()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CheckPostSendExpectations(int sourceLocalVersion, int remoteVersion)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(Math.Min(sourceLocalVersion, remoteVersion) == 0, VersionedTestMessage_v0.Serialized);
|
||||||
|
Assert.AreEqual(Math.Min(sourceLocalVersion, remoteVersion) == 1, VersionedTestMessage_v1.Serialized);
|
||||||
|
Assert.AreEqual(Math.Min(sourceLocalVersion, remoteVersion) == 2, VersionedTestMessage.Serialized);
|
||||||
|
Assert.AreEqual(sourceLocalVersion >= 1 && remoteVersion < 1, VersionedTestMessage_v1.Downgraded);
|
||||||
|
Assert.AreEqual(sourceLocalVersion >= 2 && remoteVersion < 2, VersionedTestMessage.Downgraded);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
||||||
|
Assert.AreEqual(Math.Min(sourceLocalVersion, remoteVersion), SentVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CheckPostReceiveExpectations(int sourceLocalVersion, int remoteVersion)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(SentVersion == 0, VersionedTestMessage_v0.Deserialized);
|
||||||
|
Assert.AreEqual(SentVersion == 1, VersionedTestMessage_v1.Deserialized);
|
||||||
|
Assert.AreEqual(SentVersion == 2, VersionedTestMessage.Deserialized);
|
||||||
|
Assert.AreEqual(remoteVersion >= 1 && sourceLocalVersion < 1, VersionedTestMessage_v1.Upgraded);
|
||||||
|
Assert.AreEqual(remoteVersion >= 2 && sourceLocalVersion < 2, VersionedTestMessage.Upgraded);
|
||||||
|
|
||||||
|
Assert.AreEqual((remoteVersion == 0 ? 1 : 0), VersionedTestMessage_v0.DeserializedValues.Count);
|
||||||
|
Assert.AreEqual((remoteVersion == 1 ? 1 : 0), VersionedTestMessage_v1.DeserializedValues.Count);
|
||||||
|
Assert.AreEqual((remoteVersion == 2 ? 1 : 0), VersionedTestMessage.DeserializedValues.Count);
|
||||||
|
|
||||||
|
Assert.AreEqual(SentVersion, ReceivedVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendMessageWithVersions<T>(T message, int fromVersion, int toVersion) where T : unmanaged, INetworkMessage
|
||||||
|
{
|
||||||
|
MessagingSystem sendSystem;
|
||||||
|
switch (fromVersion)
|
||||||
|
{
|
||||||
|
case 0: sendSystem = m_MessagingSystem_v0; break;
|
||||||
|
case 1: sendSystem = m_MessagingSystem_v1; break;
|
||||||
|
default: sendSystem = m_MessagingSystem_v2; break;
|
||||||
|
}
|
||||||
|
sendSystem.SendMessage(ref message, NetworkDelivery.Reliable, (ulong)toVersion);
|
||||||
|
sendSystem.ProcessSendQueues();
|
||||||
|
CheckPostSendExpectations(fromVersion, toVersion);
|
||||||
|
|
||||||
|
MessagingSystem receiveSystem;
|
||||||
|
switch (toVersion)
|
||||||
|
{
|
||||||
|
case 0: receiveSystem = m_MessagingSystem_v0; break;
|
||||||
|
case 1: receiveSystem = m_MessagingSystem_v1; break;
|
||||||
|
default: receiveSystem = m_MessagingSystem_v2; break;
|
||||||
|
}
|
||||||
|
receiveSystem.HandleIncomingData((ulong)fromVersion, new ArraySegment<byte>(m_MessageSender.MessageQueue[0]), 0.0f);
|
||||||
|
receiveSystem.ProcessIncomingMessageQueue();
|
||||||
|
CheckPostReceiveExpectations(fromVersion, toVersion);
|
||||||
|
|
||||||
|
m_MessageSender.MessageQueue.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV0ToV0_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v0();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 0, 0);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage_v0.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual(message.B, receivedMessage.B);
|
||||||
|
Assert.AreEqual(message.C, receivedMessage.C);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV0ToV1_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v0();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 0, 1);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage_v1.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual(message.B, receivedMessage.B);
|
||||||
|
Assert.AreEqual(message.C, receivedMessage.C);
|
||||||
|
Assert.AreEqual(k_DefaultD, receivedMessage.D);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV0ToV2_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v0();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 0, 2);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual((float)k_DefaultD, receivedMessage.D);
|
||||||
|
Assert.AreEqual(k_DefaultE, receivedMessage.E);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV1ToV0_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v1();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 1, 0);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage_v0.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual(message.B, receivedMessage.B);
|
||||||
|
Assert.AreEqual(message.C, receivedMessage.C);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV1ToV1_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v1();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 1, 1);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage_v1.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual(message.B, receivedMessage.B);
|
||||||
|
Assert.AreEqual(message.C, receivedMessage.C);
|
||||||
|
Assert.AreEqual(message.D, receivedMessage.D);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV1ToV2_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v1();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 1, 2);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual((float)message.D, receivedMessage.D);
|
||||||
|
Assert.AreEqual(k_DefaultE, receivedMessage.E);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV2ToV0_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v2();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 2, 0);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage_v0.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual(k_DefaultB, receivedMessage.B);
|
||||||
|
Assert.AreEqual(k_DefaultC, receivedMessage.C);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV2ToV1_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v2();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 2, 1);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage_v1.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual(k_DefaultB, receivedMessage.B);
|
||||||
|
Assert.AreEqual(k_DefaultC, receivedMessage.C);
|
||||||
|
Assert.AreEqual((int)message.D, receivedMessage.D);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenSendingV2ToV2_DataIsReceivedCorrectly()
|
||||||
|
{
|
||||||
|
var message = GetMessage_v2();
|
||||||
|
|
||||||
|
SendMessageWithVersions(message, 2, 2);
|
||||||
|
|
||||||
|
var receivedMessage = VersionedTestMessage.DeserializedValues[0];
|
||||||
|
Assert.AreEqual(message.A, receivedMessage.A);
|
||||||
|
Assert.AreEqual(message.D, receivedMessage.D);
|
||||||
|
Assert.AreEqual(message.E, receivedMessage.E);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Tests/Editor/Messaging/MessageVersioningTests.cs.meta
Normal file
3
Tests/Editor/Messaging/MessageVersioningTests.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: eac9a654aacb4faf91128c9ab6024543
|
||||||
|
timeCreated: 1667326658
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.TestTools;
|
using UnityEngine.TestTools;
|
||||||
@@ -31,19 +32,17 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void GetBehaviourIndexNone()
|
[TestCase(0)]
|
||||||
|
[TestCase(1)]
|
||||||
|
[TestCase(2)]
|
||||||
|
public void GetBehaviourIndexNone(int index)
|
||||||
{
|
{
|
||||||
var gameObject = new GameObject(nameof(GetBehaviourIndexNone));
|
var gameObject = new GameObject(nameof(GetBehaviourIndexNone));
|
||||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
var networkObject = gameObject.AddComponent<NetworkObject>();
|
||||||
|
|
||||||
// TODO: Maybe not hardcode message?
|
LogAssert.Expect(LogType.Error, new Regex(".*out of bounds.*"));
|
||||||
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
|
|
||||||
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
|
|
||||||
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
|
|
||||||
|
|
||||||
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(0), Is.Null);
|
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex((ushort)index), Is.Null);
|
||||||
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(1), Is.Null);
|
|
||||||
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(2), Is.Null);
|
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Object.DestroyImmediate(gameObject);
|
Object.DestroyImmediate(gameObject);
|
||||||
@@ -56,13 +55,10 @@ namespace Unity.Netcode.EditorTests
|
|||||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
var networkObject = gameObject.AddComponent<NetworkObject>();
|
||||||
var networkBehaviour = gameObject.AddComponent<EmptyNetworkBehaviour>();
|
var networkBehaviour = gameObject.AddComponent<EmptyNetworkBehaviour>();
|
||||||
|
|
||||||
// TODO: Maybe not hardcode message?
|
LogAssert.Expect(LogType.Error, new Regex(".*out of bounds.*"));
|
||||||
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
|
|
||||||
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
|
|
||||||
|
|
||||||
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(0), Is.EqualTo(networkBehaviour));
|
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(0), Is.EqualTo(networkBehaviour));
|
||||||
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(1), Is.Null);
|
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(1), Is.Null);
|
||||||
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(2), Is.Null);
|
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Object.DestroyImmediate(gameObject);
|
Object.DestroyImmediate(gameObject);
|
||||||
|
|||||||
@@ -76,116 +76,6 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private void CheckUnsignedPackedSize64(FastBufferWriter writer, ulong value)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (value <= 240)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(1, writer.Position);
|
|
||||||
}
|
|
||||||
else if (value <= 2287)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(2, writer.Position);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.AreEqual(BitCounter.GetUsedByteCount(value) + 1, writer.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckUnsignedPackedValue64(FastBufferWriter writer, ulong value)
|
|
||||||
{
|
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
|
||||||
using (reader)
|
|
||||||
{
|
|
||||||
ByteUnpacker.ReadValuePacked(reader, out ulong readValue);
|
|
||||||
Assert.AreEqual(readValue, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckUnsignedPackedSize32(FastBufferWriter writer, uint value)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (value <= 240)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(1, writer.Position);
|
|
||||||
}
|
|
||||||
else if (value <= 2287)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(2, writer.Position);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.AreEqual(BitCounter.GetUsedByteCount(value) + 1, writer.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckUnsignedPackedValue32(FastBufferWriter writer, uint value)
|
|
||||||
{
|
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
|
||||||
using (reader)
|
|
||||||
{
|
|
||||||
ByteUnpacker.ReadValuePacked(reader, out uint readValue);
|
|
||||||
Assert.AreEqual(readValue, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckSignedPackedSize64(FastBufferWriter writer, long value)
|
|
||||||
{
|
|
||||||
ulong asUlong = Arithmetic.ZigZagEncode(value);
|
|
||||||
|
|
||||||
if (asUlong <= 240)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(1, writer.Position);
|
|
||||||
}
|
|
||||||
else if (asUlong <= 2287)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(2, writer.Position);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.AreEqual(BitCounter.GetUsedByteCount(asUlong) + 1, writer.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckSignedPackedValue64(FastBufferWriter writer, long value)
|
|
||||||
{
|
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
|
||||||
using (reader)
|
|
||||||
{
|
|
||||||
ByteUnpacker.ReadValuePacked(reader, out long readValue);
|
|
||||||
Assert.AreEqual(readValue, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckSignedPackedSize32(FastBufferWriter writer, int value)
|
|
||||||
{
|
|
||||||
ulong asUlong = Arithmetic.ZigZagEncode(value);
|
|
||||||
|
|
||||||
if (asUlong <= 240)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(1, writer.Position);
|
|
||||||
}
|
|
||||||
else if (asUlong <= 2287)
|
|
||||||
{
|
|
||||||
Assert.AreEqual(2, writer.Position);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.AreEqual(BitCounter.GetUsedByteCount(asUlong) + 1, writer.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckSignedPackedValue32(FastBufferWriter writer, int value)
|
|
||||||
{
|
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
|
||||||
using (reader)
|
|
||||||
{
|
|
||||||
ByteUnpacker.ReadValuePacked(reader, out int readValue);
|
|
||||||
Assert.AreEqual(readValue, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe void VerifyBytewiseEquality<T>(T value, T otherValue) where T : unmanaged
|
private unsafe void VerifyBytewiseEquality<T>(T value, T otherValue) where T : unmanaged
|
||||||
{
|
{
|
||||||
byte* asBytePointer = (byte*)&value;
|
byte* asBytePointer = (byte*)&value;
|
||||||
@@ -229,164 +119,53 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GetByteCount64Bits(ulong value)
|
||||||
[Test]
|
|
||||||
public void TestPacking64BitsUnsigned()
|
|
||||||
{
|
{
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
|
||||||
|
|
||||||
using (writer)
|
if (value <= 0b0000_1111)
|
||||||
{
|
{
|
||||||
writer.TryBeginWrite(9);
|
return 1;
|
||||||
ulong value = 0;
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
Assert.AreEqual(1, writer.Position);
|
|
||||||
|
|
||||||
for (var i = 0; i < 64; ++i)
|
|
||||||
{
|
|
||||||
value = 1UL << i;
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
CheckUnsignedPackedSize64(writer, value);
|
|
||||||
CheckUnsignedPackedValue64(writer, value);
|
|
||||||
for (var j = 0; j < 8; ++j)
|
|
||||||
{
|
|
||||||
value = (1UL << i) | (1UL << j);
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
CheckUnsignedPackedSize64(writer, value);
|
|
||||||
CheckUnsignedPackedValue64(writer, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value <= 0b0000_1111_1111_1111)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 0b0000_1111_1111_1111_1111_1111)
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 0b0000_1111_1111_1111_1111_1111_1111_1111)
|
||||||
|
{
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 0b0000_1111_1111_1111_1111_1111_1111_1111_1111_1111)
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 0b0000_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)
|
||||||
|
{
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 0b0000_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)
|
||||||
|
{
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 0b0000_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)
|
||||||
|
{
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
private int GetByteCount32Bits(uint value)
|
||||||
public void TestPacking32BitsUnsigned()
|
|
||||||
{
|
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
|
||||||
|
|
||||||
using (writer)
|
|
||||||
{
|
|
||||||
writer.TryBeginWrite(9);
|
|
||||||
uint value = 0;
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
Assert.AreEqual(1, writer.Position);
|
|
||||||
|
|
||||||
for (var i = 0; i < 64; ++i)
|
|
||||||
{
|
|
||||||
value = 1U << i;
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
CheckUnsignedPackedSize32(writer, value);
|
|
||||||
CheckUnsignedPackedValue32(writer, value);
|
|
||||||
for (var j = 0; j < 8; ++j)
|
|
||||||
{
|
|
||||||
value = (1U << i) | (1U << j);
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
CheckUnsignedPackedSize32(writer, value);
|
|
||||||
CheckUnsignedPackedValue32(writer, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestPacking64BitsSigned()
|
|
||||||
{
|
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
|
||||||
|
|
||||||
using (writer)
|
|
||||||
{
|
|
||||||
writer.TryBeginWrite(9);
|
|
||||||
long value = 0;
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
Assert.AreEqual(1, writer.Position);
|
|
||||||
|
|
||||||
for (var i = 0; i < 64; ++i)
|
|
||||||
{
|
|
||||||
value = 1L << i;
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
CheckSignedPackedSize64(writer, value);
|
|
||||||
CheckSignedPackedValue64(writer, value);
|
|
||||||
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, -value);
|
|
||||||
CheckSignedPackedSize64(writer, -value);
|
|
||||||
CheckSignedPackedValue64(writer, -value);
|
|
||||||
for (var j = 0; j < 8; ++j)
|
|
||||||
{
|
|
||||||
value = (1L << i) | (1L << j);
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
CheckSignedPackedSize64(writer, value);
|
|
||||||
CheckSignedPackedValue64(writer, value);
|
|
||||||
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, -value);
|
|
||||||
CheckSignedPackedSize64(writer, -value);
|
|
||||||
CheckSignedPackedValue64(writer, -value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestPacking32BitsSigned()
|
|
||||||
{
|
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
|
||||||
|
|
||||||
using (writer)
|
|
||||||
{
|
|
||||||
writer.TryBeginWrite(5);
|
|
||||||
int value = 0;
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
Assert.AreEqual(1, writer.Position);
|
|
||||||
|
|
||||||
for (var i = 0; i < 64; ++i)
|
|
||||||
{
|
|
||||||
value = 1 << i;
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
CheckSignedPackedSize32(writer, value);
|
|
||||||
CheckSignedPackedValue32(writer, value);
|
|
||||||
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, -value);
|
|
||||||
CheckSignedPackedSize32(writer, -value);
|
|
||||||
CheckSignedPackedValue32(writer, -value);
|
|
||||||
for (var j = 0; j < 8; ++j)
|
|
||||||
{
|
|
||||||
value = (1 << i) | (1 << j);
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, value);
|
|
||||||
CheckSignedPackedSize32(writer, value);
|
|
||||||
CheckSignedPackedValue32(writer, value);
|
|
||||||
|
|
||||||
writer.Seek(0);
|
|
||||||
writer.Truncate();
|
|
||||||
BytePacker.WriteValuePacked(writer, -value);
|
|
||||||
CheckSignedPackedSize32(writer, -value);
|
|
||||||
CheckSignedPackedValue32(writer, -value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetByteCount61Bits(ulong value)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
if (value <= 0b0001_1111)
|
if (value <= 0b0001_1111)
|
||||||
@@ -409,57 +188,25 @@ namespace Unity.Netcode.EditorTests
|
|||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111)
|
return 5;
|
||||||
{
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)
|
|
||||||
{
|
|
||||||
return 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111)
|
|
||||||
{
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetByteCount30Bits(uint value)
|
private int GetByteCount16Bits(ushort value)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (value <= 0b0011_1111)
|
if (value <= 0b0011_1111)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value <= 0b0011_1111_1111_1111)
|
if (value <= 0b0011_1111_1111_1111)
|
||||||
{
|
{
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value <= 0b0011_1111_1111_1111_1111_1111)
|
return 3;
|
||||||
{
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetByteCount15Bits(ushort value)
|
private ulong Get64BitEncodedValue(FastBufferWriter writer)
|
||||||
{
|
|
||||||
|
|
||||||
if (value <= 0b0111_1111)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ulong Get61BitEncodedValue(FastBufferWriter writer)
|
|
||||||
{
|
{
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
@@ -469,7 +216,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long Get60BitSignedEncodedValue(FastBufferWriter writer)
|
private long Get64BitSignedEncodedValue(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
@@ -479,7 +226,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint Get30BitEncodedValue(FastBufferWriter writer)
|
private uint Get32BitEncodedValue(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
@@ -489,7 +236,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int Get29BitSignedEncodedValue(FastBufferWriter writer)
|
private int Get32BitSignedEncodedValue(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
@@ -499,7 +246,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ushort Get15BitEncodedValue(FastBufferWriter writer)
|
private ushort Get16BitEncodedValue(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
@@ -509,7 +256,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private short Get14BitSignedEncodedValue(FastBufferWriter writer)
|
private short Get16BitSignedEncodedValue(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
@@ -520,7 +267,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBitPacking61BitsUnsigned()
|
public void TestBitPacking64BitsUnsigned()
|
||||||
{
|
{
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
var writer = new FastBufferWriter(9, Allocator.Temp);
|
||||||
|
|
||||||
@@ -530,18 +277,18 @@ namespace Unity.Netcode.EditorTests
|
|||||||
ulong value = 0;
|
ulong value = 0;
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(1, writer.Position);
|
Assert.AreEqual(1, writer.Position);
|
||||||
Assert.AreEqual(0, writer.ToArray()[0] & 0b111);
|
Assert.AreEqual(1, writer.ToArray()[0] & 0b1111);
|
||||||
Assert.AreEqual(value, Get61BitEncodedValue(writer));
|
Assert.AreEqual(value, Get64BitEncodedValue(writer));
|
||||||
|
|
||||||
for (var i = 0; i < 61; ++i)
|
for (var i = 0; i < 64; ++i)
|
||||||
{
|
{
|
||||||
value = 1UL << i;
|
value = 1UL << i;
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount64Bits(value), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount61Bits(value) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount64Bits(value), writer.ToArray()[0] & 0b1111, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get61BitEncodedValue(writer));
|
Assert.AreEqual(value, Get64BitEncodedValue(writer));
|
||||||
|
|
||||||
for (var j = 0; j < 8; ++j)
|
for (var j = 0; j < 8; ++j)
|
||||||
{
|
{
|
||||||
@@ -549,18 +296,16 @@ namespace Unity.Netcode.EditorTests
|
|||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount64Bits(value), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount61Bits(value) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount64Bits(value), writer.ToArray()[0] & 0b1111, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get61BitEncodedValue(writer));
|
Assert.AreEqual(value, Get64BitEncodedValue(writer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Throws<ArgumentException>(() => { BytePacker.WriteValueBitPacked(writer, 1UL << 61); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBitPacking60BitsSigned()
|
public void TestBitPacking64BitsSigned()
|
||||||
{
|
{
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
var writer = new FastBufferWriter(9, Allocator.Temp);
|
||||||
|
|
||||||
@@ -570,28 +315,28 @@ namespace Unity.Netcode.EditorTests
|
|||||||
long value = 0;
|
long value = 0;
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(1, writer.Position);
|
Assert.AreEqual(1, writer.Position);
|
||||||
Assert.AreEqual(0, writer.ToArray()[0] & 0b111);
|
Assert.AreEqual(1, writer.ToArray()[0] & 0b1111);
|
||||||
Assert.AreEqual(value, Get60BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get64BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
for (var i = 0; i < 61; ++i)
|
for (var i = 0; i < 64; ++i)
|
||||||
{
|
{
|
||||||
value = 1U << i;
|
value = 1U << i;
|
||||||
ulong zzvalue = Arithmetic.ZigZagEncode(value);
|
ulong zzvalue = Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount64Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount64Bits(zzvalue), writer.ToArray()[0] & 0b1111, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get60BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get64BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
value = -value;
|
value = -value;
|
||||||
zzvalue = Arithmetic.ZigZagEncode(value);
|
zzvalue = Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount64Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount64Bits(zzvalue), writer.ToArray()[0] & 0b1111, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get60BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get64BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
for (var j = 0; j < 8; ++j)
|
for (var j = 0; j < 8; ++j)
|
||||||
{
|
{
|
||||||
@@ -600,27 +345,25 @@ namespace Unity.Netcode.EditorTests
|
|||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount64Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount64Bits(zzvalue), writer.ToArray()[0] & 0b1111, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get60BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get64BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
value = -value;
|
value = -value;
|
||||||
zzvalue = Arithmetic.ZigZagEncode(value);
|
zzvalue = Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount64Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount64Bits(zzvalue), writer.ToArray()[0] & 0b1111, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get60BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get64BitSignedEncodedValue(writer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Throws<ArgumentException>(() => { BytePacker.WriteValueBitPacked(writer, 1UL << 61); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBitPacking30BitsUnsigned()
|
public void TestBitPacking32BitsUnsigned()
|
||||||
{
|
{
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
var writer = new FastBufferWriter(9, Allocator.Temp);
|
||||||
|
|
||||||
@@ -630,18 +373,18 @@ namespace Unity.Netcode.EditorTests
|
|||||||
uint value = 0;
|
uint value = 0;
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(1, writer.Position);
|
Assert.AreEqual(1, writer.Position);
|
||||||
Assert.AreEqual(0, writer.ToArray()[0] & 0b11);
|
Assert.AreEqual(1, writer.ToArray()[0] & 0b111);
|
||||||
Assert.AreEqual(value, Get30BitEncodedValue(writer));
|
Assert.AreEqual(value, Get32BitEncodedValue(writer));
|
||||||
|
|
||||||
for (var i = 0; i < 30; ++i)
|
for (var i = 0; i < 32; ++i)
|
||||||
{
|
{
|
||||||
value = 1U << i;
|
value = 1U << i;
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount32Bits(value), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount30Bits(value) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount32Bits(value), writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get30BitEncodedValue(writer));
|
Assert.AreEqual(value, Get32BitEncodedValue(writer));
|
||||||
|
|
||||||
for (var j = 0; j < 8; ++j)
|
for (var j = 0; j < 8; ++j)
|
||||||
{
|
{
|
||||||
@@ -649,18 +392,16 @@ namespace Unity.Netcode.EditorTests
|
|||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount32Bits(value), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount30Bits(value) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount32Bits(value), writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get30BitEncodedValue(writer));
|
Assert.AreEqual(value, Get32BitEncodedValue(writer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Throws<ArgumentException>(() => { BytePacker.WriteValueBitPacked(writer, 1U << 30); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBitPacking29BitsSigned()
|
public void TestBitPacking32BitsSigned()
|
||||||
{
|
{
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
var writer = new FastBufferWriter(9, Allocator.Temp);
|
||||||
|
|
||||||
@@ -670,28 +411,28 @@ namespace Unity.Netcode.EditorTests
|
|||||||
int value = 0;
|
int value = 0;
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(1, writer.Position);
|
Assert.AreEqual(1, writer.Position);
|
||||||
Assert.AreEqual(0, writer.ToArray()[0] & 0b11);
|
Assert.AreEqual(1, writer.ToArray()[0] & 0b111);
|
||||||
Assert.AreEqual(value, Get30BitEncodedValue(writer));
|
Assert.AreEqual(value, Get32BitEncodedValue(writer));
|
||||||
|
|
||||||
for (var i = 0; i < 29; ++i)
|
for (var i = 0; i < 32; ++i)
|
||||||
{
|
{
|
||||||
value = 1 << i;
|
value = 1 << i;
|
||||||
uint zzvalue = (uint)Arithmetic.ZigZagEncode(value);
|
uint zzvalue = (uint)Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount32Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount32Bits(zzvalue), writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get29BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get32BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
value = -value;
|
value = -value;
|
||||||
zzvalue = (uint)Arithmetic.ZigZagEncode(value);
|
zzvalue = (uint)Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount32Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount32Bits(zzvalue), writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get29BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get32BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
for (var j = 0; j < 8; ++j)
|
for (var j = 0; j < 8; ++j)
|
||||||
{
|
{
|
||||||
@@ -700,25 +441,25 @@ namespace Unity.Netcode.EditorTests
|
|||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount32Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount32Bits(zzvalue), writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get29BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get32BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
value = -value;
|
value = -value;
|
||||||
zzvalue = (uint)Arithmetic.ZigZagEncode(value);
|
zzvalue = (uint)Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount32Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount32Bits(zzvalue), writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get29BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get32BitSignedEncodedValue(writer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBitPacking15BitsUnsigned()
|
public void TestBitPacking16BitsUnsigned()
|
||||||
{
|
{
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
var writer = new FastBufferWriter(9, Allocator.Temp);
|
||||||
|
|
||||||
@@ -728,18 +469,18 @@ namespace Unity.Netcode.EditorTests
|
|||||||
ushort value = 0;
|
ushort value = 0;
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(1, writer.Position);
|
Assert.AreEqual(1, writer.Position);
|
||||||
Assert.AreEqual(0, writer.ToArray()[0] & 0b1);
|
Assert.AreEqual(1, writer.ToArray()[0] & 0b11);
|
||||||
Assert.AreEqual(value, Get15BitEncodedValue(writer));
|
Assert.AreEqual(value, Get16BitEncodedValue(writer));
|
||||||
|
|
||||||
for (var i = 0; i < 15; ++i)
|
for (var i = 0; i < 16; ++i)
|
||||||
{
|
{
|
||||||
value = (ushort)(1U << i);
|
value = (ushort)(1U << i);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount16Bits(value), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount15Bits(value) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount16Bits(value), writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get15BitEncodedValue(writer));
|
Assert.AreEqual(value, Get16BitEncodedValue(writer));
|
||||||
|
|
||||||
for (var j = 0; j < 8; ++j)
|
for (var j = 0; j < 8; ++j)
|
||||||
{
|
{
|
||||||
@@ -747,17 +488,15 @@ namespace Unity.Netcode.EditorTests
|
|||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount16Bits(value), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount15Bits(value) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount16Bits(value), writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get15BitEncodedValue(writer));
|
Assert.AreEqual(value, Get16BitEncodedValue(writer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Throws<ArgumentException>(() => { BytePacker.WriteValueBitPacked(writer, (ushort)(1U << 15)); });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBitPacking14BitsSigned()
|
public void TestBitPacking16BitsSigned()
|
||||||
{
|
{
|
||||||
var writer = new FastBufferWriter(9, Allocator.Temp);
|
var writer = new FastBufferWriter(9, Allocator.Temp);
|
||||||
|
|
||||||
@@ -767,28 +506,28 @@ namespace Unity.Netcode.EditorTests
|
|||||||
short value = 0;
|
short value = 0;
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(1, writer.Position);
|
Assert.AreEqual(1, writer.Position);
|
||||||
Assert.AreEqual(0, writer.ToArray()[0] & 0b1);
|
Assert.AreEqual(1, writer.ToArray()[0] & 0b11);
|
||||||
Assert.AreEqual(value, Get15BitEncodedValue(writer));
|
Assert.AreEqual(value, Get16BitEncodedValue(writer));
|
||||||
|
|
||||||
for (var i = 0; i < 14; ++i)
|
for (var i = 0; i < 16; ++i)
|
||||||
{
|
{
|
||||||
value = (short)(1 << i);
|
value = (short)(1 << i);
|
||||||
ushort zzvalue = (ushort)Arithmetic.ZigZagEncode(value);
|
ushort zzvalue = (ushort)Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount16Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount16Bits(zzvalue), writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get14BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get16BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
value = (short)-value;
|
value = (short)-value;
|
||||||
zzvalue = (ushort)Arithmetic.ZigZagEncode(value);
|
zzvalue = (ushort)Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount16Bits(zzvalue), writer.Position, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})");
|
Assert.AreEqual(GetByteCount16Bits(zzvalue), writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})");
|
||||||
Assert.AreEqual(value, Get14BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get16BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
for (var j = 0; j < 8; ++j)
|
for (var j = 0; j < 8; ++j)
|
||||||
{
|
{
|
||||||
@@ -797,18 +536,18 @@ namespace Unity.Netcode.EditorTests
|
|||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount16Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount16Bits(zzvalue), writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get14BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get16BitSignedEncodedValue(writer));
|
||||||
|
|
||||||
value = (short)-value;
|
value = (short)-value;
|
||||||
zzvalue = (ushort)Arithmetic.ZigZagEncode(value);
|
zzvalue = (ushort)Arithmetic.ZigZagEncode(value);
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
writer.Truncate();
|
writer.Truncate();
|
||||||
BytePacker.WriteValueBitPacked(writer, value);
|
BytePacker.WriteValueBitPacked(writer, value);
|
||||||
Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount16Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})");
|
Assert.AreEqual(GetByteCount16Bits(zzvalue), writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})");
|
||||||
Assert.AreEqual(value, Get14BitSignedEncodedValue(writer));
|
Assert.AreEqual(value, Get16BitSignedEncodedValue(writer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,5 +126,40 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
transport.Shutdown();
|
transport.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UTP_TRANSPORT_2_0_ABOVE
|
||||||
|
[Test]
|
||||||
|
public void UnityTransport_EmptySecurityStringsShouldThrow([Values("", null)] string cert, [Values("", null)] string secret)
|
||||||
|
{
|
||||||
|
var supportingGO = new GameObject();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var networkManager = supportingGO.AddComponent<NetworkManager>(); // NM is required for UTP to work with certificates.
|
||||||
|
networkManager.NetworkConfig = new NetworkConfig();
|
||||||
|
UnityTransport transport = supportingGO.AddComponent<UnityTransport>();
|
||||||
|
networkManager.NetworkConfig.NetworkTransport = transport;
|
||||||
|
transport.Initialize();
|
||||||
|
transport.SetServerSecrets(serverCertificate: cert, serverPrivateKey: secret);
|
||||||
|
|
||||||
|
// Use encryption, but don't set certificate and check for exception
|
||||||
|
transport.UseEncryption = true;
|
||||||
|
Assert.Throws<System.Exception>(() =>
|
||||||
|
{
|
||||||
|
networkManager.StartServer();
|
||||||
|
});
|
||||||
|
// Make sure StartServer failed
|
||||||
|
Assert.False(transport.NetworkDriver.IsCreated);
|
||||||
|
Assert.False(networkManager.IsServer);
|
||||||
|
Assert.False(networkManager.IsListening);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (supportingGO != null)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(supportingGO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f672293e0efc41a6a7e930fd7ff14436
|
|
||||||
timeCreated: 1631650280
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using Unity.Netcode.Editor;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.EditorTests
|
|
||||||
{
|
|
||||||
internal static class UITestHelpers
|
|
||||||
{
|
|
||||||
[MenuItem("Netcode/UI/Reset Multiplayer Tools Tip Status")]
|
|
||||||
private static void ResetMultiplayerToolsTipStatus()
|
|
||||||
{
|
|
||||||
PlayerPrefs.DeleteKey(NetworkManagerEditor.InstallMultiplayerToolsTipDismissedPlayerPrefKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: bde5fc3349494f77bebd0be12a6957e1
|
|
||||||
timeCreated: 1631650292
|
|
||||||
31
Tests/Editor/XXHashTests.cs
Normal file
31
Tests/Editor/XXHashTests.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.EditorTests
|
||||||
|
{
|
||||||
|
public class XXHashTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestXXHash32Short()
|
||||||
|
{
|
||||||
|
Assert.That("TestStuff".Hash32(), Is.EqualTo(0x64e10c4c));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestXXHash32Long()
|
||||||
|
{
|
||||||
|
Assert.That("TestingHashingWithLongStringValues".Hash32(), Is.EqualTo(0xba3d1783));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestXXHas64Short()
|
||||||
|
{
|
||||||
|
Assert.That("TestStuff".Hash64(), Is.EqualTo(0x4c3be8d82d14a5a9));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestXXHash64Long()
|
||||||
|
{
|
||||||
|
Assert.That("TestingHashingWithLongStringValues".Hash64(), Is.EqualTo(0x5b374f98b10bf246));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Tests/Editor/XXHashTests.cs.meta
Normal file
11
Tests/Editor/XXHashTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ce9bdc7200b66410286810307554534b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -32,6 +32,11 @@
|
|||||||
"name": "Unity",
|
"name": "Unity",
|
||||||
"expression": "(0,2022.2.0a5)",
|
"expression": "(0,2022.2.0a5)",
|
||||||
"define": "UNITY_UNET_PRESENT"
|
"define": "UNITY_UNET_PRESENT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.unity.transport",
|
||||||
|
"expression": "2.0.0-exp",
|
||||||
|
"define": "UTP_TRANSPORT_2_0_ABOVE"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -45,14 +45,18 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
private EmptyComponent GetObjectForClient(ulong clientId)
|
private EmptyComponent GetObjectForClient(ulong clientId)
|
||||||
{
|
{
|
||||||
foreach (var component in Object.FindObjectsOfType<EmptyComponent>())
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var emptyComponents = Object.FindObjectsByType<EmptyComponent>(FindObjectsSortMode.InstanceID);
|
||||||
|
#else
|
||||||
|
var emptyComponents = Object.FindObjectsOfType<EmptyComponent>();
|
||||||
|
#endif
|
||||||
|
foreach (var component in emptyComponents)
|
||||||
{
|
{
|
||||||
if (component.IsSpawned && component.NetworkManager.LocalClientId == clientId)
|
if (component.IsSpawned && component.NetworkManager.LocalClientId == clientId)
|
||||||
{
|
{
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -271,14 +271,19 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
private T GetComponentForClient<T>(ulong clientId) where T : NetworkBehaviour
|
private T GetComponentForClient<T>(ulong clientId) where T : NetworkBehaviour
|
||||||
{
|
{
|
||||||
foreach (var component in Object.FindObjectsOfType<T>())
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var componentsToFind = Object.FindObjectsByType<T>(FindObjectsSortMode.InstanceID);
|
||||||
|
#else
|
||||||
|
var componentsToFind = Object.FindObjectsOfType<T>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
foreach (var component in componentsToFind)
|
||||||
{
|
{
|
||||||
if (component.IsSpawned && component.NetworkManager.LocalClientId == clientId)
|
if (component.IsSpawned && component.NetworkManager.LocalClientId == clientId)
|
||||||
{
|
{
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -639,6 +644,8 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool LogAllMessages => true;
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator WhenMultipleSpawnTriggeredMessagesAreDeferred_TheyAreAllProcessedOnSpawn()
|
public IEnumerator WhenMultipleSpawnTriggeredMessagesAreDeferred_TheyAreAllProcessedOnSpawn()
|
||||||
{
|
{
|
||||||
@@ -658,7 +665,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
||||||
|
|
||||||
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
|
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage, NetworkVariableDeltaMessage>();
|
||||||
|
|
||||||
foreach (var client in m_ClientNetworkManagers)
|
foreach (var client in m_ClientNetworkManagers)
|
||||||
{
|
{
|
||||||
@@ -666,9 +673,9 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
Assert.IsTrue(manager.DeferMessageCalled);
|
Assert.IsTrue(manager.DeferMessageCalled);
|
||||||
Assert.IsFalse(manager.ProcessTriggersCalled);
|
Assert.IsFalse(manager.ProcessTriggersCalled);
|
||||||
|
|
||||||
Assert.AreEqual(3, manager.DeferredMessageCountTotal());
|
Assert.AreEqual(4, manager.DeferredMessageCountTotal());
|
||||||
Assert.AreEqual(3, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
|
Assert.AreEqual(4, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
|
||||||
Assert.AreEqual(3, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
|
Assert.AreEqual(4, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
|
||||||
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnAddPrefab));
|
Assert.AreEqual(0, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnAddPrefab));
|
||||||
AddPrefabsToClient(client);
|
AddPrefabsToClient(client);
|
||||||
}
|
}
|
||||||
@@ -751,7 +758,13 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
{
|
{
|
||||||
var found1 = false;
|
var found1 = false;
|
||||||
var found2 = false;
|
var found2 = false;
|
||||||
foreach (var component in Object.FindObjectsOfType<DeferredMessageTestRpcComponent>())
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var deferredMessageTestRpcComponents = Object.FindObjectsByType<DeferredMessageTestRpcComponent>(FindObjectsSortMode.None);
|
||||||
|
#else
|
||||||
|
var deferredMessageTestRpcComponents = Object.FindObjectsOfType<DeferredMessageTestRpcComponent>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
foreach (var component in deferredMessageTestRpcComponents)
|
||||||
{
|
{
|
||||||
if (component.IsSpawned && component.NetworkManager.LocalClientId == client.LocalClientId)
|
if (component.IsSpawned && component.NetworkManager.LocalClientId == client.LocalClientId)
|
||||||
{
|
{
|
||||||
@@ -794,7 +807,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
serverObject.GetComponent<NetworkObject>().ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
||||||
|
|
||||||
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage>();
|
yield return WaitForAllClientsToReceive<ChangeOwnershipMessage, NetworkVariableDeltaMessage>();
|
||||||
|
|
||||||
// Validate messages are deferred and pending
|
// Validate messages are deferred and pending
|
||||||
foreach (var client in m_ClientNetworkManagers)
|
foreach (var client in m_ClientNetworkManagers)
|
||||||
@@ -802,9 +815,11 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
|
var manager = (TestDeferredMessageManager)client.DeferredMessageManager;
|
||||||
Assert.IsTrue(manager.DeferMessageCalled);
|
Assert.IsTrue(manager.DeferMessageCalled);
|
||||||
Assert.IsFalse(manager.ProcessTriggersCalled);
|
Assert.IsFalse(manager.ProcessTriggersCalled);
|
||||||
Assert.AreEqual(4, manager.DeferredMessageCountTotal());
|
|
||||||
Assert.AreEqual(3, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
|
Assert.AreEqual(5, manager.DeferredMessageCountTotal());
|
||||||
Assert.AreEqual(3, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
|
|
||||||
|
Assert.AreEqual(4, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnSpawn));
|
||||||
|
Assert.AreEqual(4, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnSpawn, serverObject.GetComponent<NetworkObject>().NetworkObjectId));
|
||||||
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnAddPrefab));
|
Assert.AreEqual(1, manager.DeferredMessageCountForType(IDeferredMessageManager.TriggerType.OnAddPrefab));
|
||||||
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnAddPrefab, serverObject.GetComponent<NetworkObject>().GlobalObjectIdHash));
|
Assert.AreEqual(1, manager.DeferredMessageCountForKey(IDeferredMessageManager.TriggerType.OnAddPrefab, serverObject.GetComponent<NetworkObject>().GlobalObjectIdHash));
|
||||||
AddPrefabsToClient(client);
|
AddPrefabsToClient(client);
|
||||||
|
|||||||
@@ -29,10 +29,15 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
{
|
{
|
||||||
// Check the condition for this test and automatically handle varying processing
|
// Check the condition for this test and automatically handle varying processing
|
||||||
// environments and conditions
|
// environments and conditions
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
yield return WaitForConditionOrTimeOut(() =>
|
||||||
|
Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where(
|
||||||
|
(c) => c.IsSpawned).Count() == 2);
|
||||||
|
#else
|
||||||
yield return WaitForConditionOrTimeOut(() =>
|
yield return WaitForConditionOrTimeOut(() =>
|
||||||
Object.FindObjectsOfType<NetworkVisibilityComponent>().Where(
|
Object.FindObjectsOfType<NetworkVisibilityComponent>().Where(
|
||||||
(c) => c.IsSpawned).Count() == 2);
|
(c) => c.IsSpawned).Count() == 2);
|
||||||
|
#endif
|
||||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for instances " +
|
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for instances " +
|
||||||
"to be detected!");
|
"to be detected!");
|
||||||
}
|
}
|
||||||
@@ -64,9 +69,15 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
{
|
{
|
||||||
// Check the condition for this test and automatically handle varying processing
|
// Check the condition for this test and automatically handle varying processing
|
||||||
// environments and conditions
|
// environments and conditions
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
yield return WaitForConditionOrTimeOut(() =>
|
||||||
|
Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where(
|
||||||
|
(c) => c.IsSpawned).Count() == 2);
|
||||||
|
#else
|
||||||
yield return WaitForConditionOrTimeOut(() =>
|
yield return WaitForConditionOrTimeOut(() =>
|
||||||
Object.FindObjectsOfType<NetworkVisibilityComponent>().Where(
|
Object.FindObjectsOfType<NetworkVisibilityComponent>().Where(
|
||||||
(c) => c.IsSpawned).Count() == 2);
|
(c) => c.IsSpawned).Count() == 2);
|
||||||
|
#endif
|
||||||
|
|
||||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for instances " +
|
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for instances " +
|
||||||
"to be detected!");
|
"to be detected!");
|
||||||
|
|||||||
93
Tests/Runtime/Messaging/DisconnectReasonTests.cs
Normal file
93
Tests/Runtime/Messaging/DisconnectReasonTests.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.RuntimeTests
|
||||||
|
{
|
||||||
|
public class DisconnectReasonObject : NetworkBehaviour
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DisconnectReasonTests : NetcodeIntegrationTest
|
||||||
|
{
|
||||||
|
protected override int NumberOfClients => 2;
|
||||||
|
|
||||||
|
private GameObject m_PrefabToSpawn;
|
||||||
|
|
||||||
|
protected override void OnServerAndClientsCreated()
|
||||||
|
{
|
||||||
|
m_PrefabToSpawn = CreateNetworkObjectPrefab("DisconnectReasonObject");
|
||||||
|
m_PrefabToSpawn.AddComponent<DisconnectReasonObject>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int m_DisconnectCount;
|
||||||
|
private bool m_ThrowOnDisconnect = false;
|
||||||
|
|
||||||
|
public void OnClientDisconnectCallback(ulong clientId)
|
||||||
|
{
|
||||||
|
m_DisconnectCount++;
|
||||||
|
if (m_ThrowOnDisconnect)
|
||||||
|
{
|
||||||
|
throw new SystemException("whatever");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator DisconnectReasonTest()
|
||||||
|
{
|
||||||
|
float startTime = Time.realtimeSinceStartup;
|
||||||
|
m_ThrowOnDisconnect = false;
|
||||||
|
m_DisconnectCount = 0;
|
||||||
|
|
||||||
|
// Add a callback for both clients, when they get disconnected
|
||||||
|
m_ClientNetworkManagers[0].OnClientDisconnectCallback += OnClientDisconnectCallback;
|
||||||
|
m_ClientNetworkManagers[1].OnClientDisconnectCallback += OnClientDisconnectCallback;
|
||||||
|
|
||||||
|
// Disconnect both clients, from the server
|
||||||
|
m_ServerNetworkManager.DisconnectClient(m_ClientNetworkManagers[0].LocalClientId, "Bogus reason 1");
|
||||||
|
m_ServerNetworkManager.DisconnectClient(m_ClientNetworkManagers[1].LocalClientId, "Bogus reason 2");
|
||||||
|
|
||||||
|
while (m_DisconnectCount < 2 && Time.realtimeSinceStartup < startTime + 10.0f)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(m_ClientNetworkManagers[0].DisconnectReason, "Bogus reason 1");
|
||||||
|
Assert.AreEqual(m_ClientNetworkManagers[1].DisconnectReason, "Bogus reason 2");
|
||||||
|
|
||||||
|
Debug.Assert(m_DisconnectCount == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator DisconnectExceptionTest()
|
||||||
|
{
|
||||||
|
m_ThrowOnDisconnect = true;
|
||||||
|
m_DisconnectCount = 0;
|
||||||
|
float startTime = Time.realtimeSinceStartup;
|
||||||
|
|
||||||
|
// Add a callback for first client, when they get disconnected
|
||||||
|
m_ClientNetworkManagers[0].OnClientDisconnectCallback += OnClientDisconnectCallback;
|
||||||
|
m_ClientNetworkManagers[1].OnClientDisconnectCallback += OnClientDisconnectCallback;
|
||||||
|
|
||||||
|
// Disconnect first client, from the server
|
||||||
|
LogAssert.Expect(LogType.Exception, new Regex(".*whatever.*"));
|
||||||
|
m_ServerNetworkManager.DisconnectClient(m_ClientNetworkManagers[0].LocalClientId);
|
||||||
|
|
||||||
|
// Disconnect second client, from the server
|
||||||
|
LogAssert.Expect(LogType.Exception, new Regex(".*whatever.*"));
|
||||||
|
m_ServerNetworkManager.DisconnectClient(m_ClientNetworkManagers[1].LocalClientId);
|
||||||
|
|
||||||
|
while (m_DisconnectCount < 2 && Time.realtimeSinceStartup < startTime + 10.0f)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(m_DisconnectCount == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Tests/Runtime/Messaging/DisconnectReasonTests.cs.meta
Normal file
11
Tests/Runtime/Messaging/DisconnectReasonTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 93141fa15824f406b89dbc6f32c8910d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -3,7 +3,6 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.TestTools;
|
using UnityEngine.TestTools;
|
||||||
using Unity.Netcode.TestHelpers.Runtime;
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
|
|
||||||
@@ -27,16 +26,6 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
public IEnumerator NamedMessageIsReceivedOnClientWithContent()
|
public IEnumerator NamedMessageIsReceivedOnClientWithContent()
|
||||||
{
|
{
|
||||||
var messageName = Guid.NewGuid().ToString();
|
var messageName = Guid.NewGuid().ToString();
|
||||||
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
|
||||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
|
||||||
using (writer)
|
|
||||||
{
|
|
||||||
writer.WriteValueSafe(messageContent);
|
|
||||||
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(
|
|
||||||
messageName,
|
|
||||||
FirstClient.LocalClientId,
|
|
||||||
writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong receivedMessageSender = 0;
|
ulong receivedMessageSender = 0;
|
||||||
var receivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
var receivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
@@ -49,7 +38,49 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
reader.ReadValueSafe(out receivedMessageContent);
|
reader.ReadValueSafe(out receivedMessageContent);
|
||||||
});
|
});
|
||||||
|
|
||||||
yield return new WaitForSeconds(0.2f);
|
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
||||||
|
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(messageContent);
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(
|
||||||
|
messageName,
|
||||||
|
FirstClient.LocalClientId,
|
||||||
|
writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return WaitForMessageReceived<NamedMessage>(new List<NetworkManager> { FirstClient });
|
||||||
|
|
||||||
|
Assert.AreEqual(messageContent.Value, receivedMessageContent.Value);
|
||||||
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void NamedMessageIsReceivedOnHostWithContent()
|
||||||
|
{
|
||||||
|
var messageName = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
ulong receivedMessageSender = 0;
|
||||||
|
var receivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(
|
||||||
|
messageName,
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
receivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out receivedMessageContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
||||||
|
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(messageContent);
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(
|
||||||
|
messageName,
|
||||||
|
m_ServerNetworkManager.LocalClientId,
|
||||||
|
writer);
|
||||||
|
}
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, receivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, receivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
|
||||||
@@ -59,16 +90,6 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent()
|
public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent()
|
||||||
{
|
{
|
||||||
var messageName = Guid.NewGuid().ToString();
|
var messageName = Guid.NewGuid().ToString();
|
||||||
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
|
||||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
|
||||||
using (writer)
|
|
||||||
{
|
|
||||||
writer.WriteValueSafe(messageContent);
|
|
||||||
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(
|
|
||||||
messageName,
|
|
||||||
new List<ulong> { FirstClient.LocalClientId, SecondClient.LocalClientId },
|
|
||||||
writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong firstReceivedMessageSender = 0;
|
ulong firstReceivedMessageSender = 0;
|
||||||
var firstReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
var firstReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
@@ -92,19 +113,78 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
reader.ReadValueSafe(out secondReceivedMessageContent);
|
reader.ReadValueSafe(out secondReceivedMessageContent);
|
||||||
});
|
});
|
||||||
|
|
||||||
yield return new WaitForSeconds(0.2f);
|
ulong thirdReceivedMessageSender = 0;
|
||||||
|
var thirdReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(
|
||||||
|
messageName,
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
thirdReceivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out thirdReceivedMessageContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
||||||
|
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(messageContent);
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(
|
||||||
|
messageName,
|
||||||
|
new List<ulong> { m_ServerNetworkManager.LocalClientId, FirstClient.LocalClientId, SecondClient.LocalClientId },
|
||||||
|
writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return WaitForMessageReceived<NamedMessage>(new List<NetworkManager> { FirstClient, SecondClient });
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, firstReceivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, firstReceivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, secondReceivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, secondReceivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
|
||||||
|
|
||||||
|
Assert.AreEqual(messageContent.Value, thirdReceivedMessageContent.Value);
|
||||||
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, thirdReceivedMessageSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator WhenSendingNamedMessageToAll_AllClientsReceiveIt()
|
public IEnumerator WhenSendingNamedMessageToAll_AllClientsReceiveIt()
|
||||||
{
|
{
|
||||||
var messageName = Guid.NewGuid().ToString();
|
var messageName = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
ulong firstReceivedMessageSender = 0;
|
||||||
|
var firstReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(
|
||||||
|
messageName,
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
firstReceivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out firstReceivedMessageContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
ulong secondReceivedMessageSender = 0;
|
||||||
|
var secondReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
SecondClient.CustomMessagingManager.RegisterNamedMessageHandler(
|
||||||
|
messageName,
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
secondReceivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out secondReceivedMessageContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
ulong thirdReceivedMessageSender = 0;
|
||||||
|
var thirdReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(
|
||||||
|
messageName,
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
thirdReceivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out thirdReceivedMessageContent);
|
||||||
|
});
|
||||||
|
|
||||||
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
||||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||||
using (writer)
|
using (writer)
|
||||||
@@ -113,35 +193,16 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessageToAll(messageName, writer);
|
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessageToAll(messageName, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong firstReceivedMessageSender = 0;
|
yield return WaitForMessageReceived<NamedMessage>(new List<NetworkManager> { FirstClient, SecondClient });
|
||||||
var firstReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
|
||||||
FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(
|
|
||||||
messageName,
|
|
||||||
(ulong sender, FastBufferReader reader) =>
|
|
||||||
{
|
|
||||||
firstReceivedMessageSender = sender;
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out firstReceivedMessageContent);
|
|
||||||
});
|
|
||||||
|
|
||||||
ulong secondReceivedMessageSender = 0;
|
|
||||||
var secondReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
|
||||||
SecondClient.CustomMessagingManager.RegisterNamedMessageHandler(
|
|
||||||
messageName,
|
|
||||||
(ulong sender, FastBufferReader reader) =>
|
|
||||||
{
|
|
||||||
secondReceivedMessageSender = sender;
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out secondReceivedMessageContent);
|
|
||||||
});
|
|
||||||
|
|
||||||
yield return new WaitForSeconds(0.2f);
|
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, firstReceivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, firstReceivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, secondReceivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, secondReceivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
|
||||||
|
|
||||||
|
Assert.AreEqual(messageContent.Value, thirdReceivedMessageContent.Value);
|
||||||
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, thirdReceivedMessageSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.TestTools;
|
using UnityEngine.TestTools;
|
||||||
using Unity.Netcode.TestHelpers.Runtime;
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
|
|
||||||
@@ -19,16 +18,6 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator UnnamedMessageIsReceivedOnClientWithContent()
|
public IEnumerator UnnamedMessageIsReceivedOnClientWithContent()
|
||||||
{
|
{
|
||||||
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
|
||||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
|
||||||
using (writer)
|
|
||||||
{
|
|
||||||
writer.WriteValueSafe(messageContent);
|
|
||||||
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(
|
|
||||||
FirstClient.LocalClientId,
|
|
||||||
writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong receivedMessageSender = 0;
|
ulong receivedMessageSender = 0;
|
||||||
var receivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
var receivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
|
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
|
||||||
@@ -39,7 +28,44 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
reader.ReadValueSafe(out receivedMessageContent);
|
reader.ReadValueSafe(out receivedMessageContent);
|
||||||
};
|
};
|
||||||
|
|
||||||
yield return new WaitForSeconds(0.2f);
|
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
||||||
|
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(messageContent);
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(
|
||||||
|
FirstClient.LocalClientId,
|
||||||
|
writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return WaitForMessageReceived<UnnamedMessage>(new List<NetworkManager> { FirstClient });
|
||||||
|
|
||||||
|
Assert.AreEqual(messageContent.Value, receivedMessageContent.Value);
|
||||||
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UnnamedMessageIsReceivedOnHostWithContent()
|
||||||
|
{
|
||||||
|
ulong receivedMessageSender = 0;
|
||||||
|
var receivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.OnUnnamedMessage +=
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
receivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out receivedMessageContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
||||||
|
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(messageContent);
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(
|
||||||
|
m_ServerNetworkManager.LocalClientId,
|
||||||
|
writer);
|
||||||
|
}
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, receivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, receivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
|
||||||
@@ -48,16 +74,6 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent()
|
public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent()
|
||||||
{
|
{
|
||||||
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
|
||||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
|
||||||
using (writer)
|
|
||||||
{
|
|
||||||
writer.WriteValueSafe(messageContent);
|
|
||||||
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(
|
|
||||||
new List<ulong> { FirstClient.LocalClientId, SecondClient.LocalClientId },
|
|
||||||
writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong firstReceivedMessageSender = 0;
|
ulong firstReceivedMessageSender = 0;
|
||||||
var firstReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
var firstReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
|
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
|
||||||
@@ -78,18 +94,71 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
reader.ReadValueSafe(out secondReceivedMessageContent);
|
reader.ReadValueSafe(out secondReceivedMessageContent);
|
||||||
};
|
};
|
||||||
|
|
||||||
yield return new WaitForSeconds(0.2f);
|
ulong thirdReceivedMessageSender = 0;
|
||||||
|
var thirdReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.OnUnnamedMessage +=
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
thirdReceivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out thirdReceivedMessageContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
||||||
|
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(messageContent);
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(
|
||||||
|
new List<ulong> { m_ServerNetworkManager.LocalClientId, FirstClient.LocalClientId, SecondClient.LocalClientId },
|
||||||
|
writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return WaitForMessageReceived<UnnamedMessage>(new List<NetworkManager> { FirstClient, SecondClient });
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, firstReceivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, firstReceivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, secondReceivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, secondReceivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
|
||||||
|
|
||||||
|
Assert.AreEqual(messageContent.Value, thirdReceivedMessageContent.Value);
|
||||||
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, thirdReceivedMessageSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator WhenSendingUnnamedMessageToAll_AllClientsReceiveIt()
|
public IEnumerator WhenSendingUnnamedMessageToAll_AllClientsReceiveIt()
|
||||||
{
|
{
|
||||||
|
ulong firstReceivedMessageSender = 0;
|
||||||
|
var firstReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
firstReceivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out firstReceivedMessageContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
ulong secondReceivedMessageSender = 0;
|
||||||
|
var secondReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
SecondClient.CustomMessagingManager.OnUnnamedMessage +=
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
secondReceivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out secondReceivedMessageContent);
|
||||||
|
};
|
||||||
|
|
||||||
|
ulong thirdReceivedMessageSender = 0;
|
||||||
|
var thirdReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
||||||
|
m_ServerNetworkManager.CustomMessagingManager.OnUnnamedMessage +=
|
||||||
|
(ulong sender, FastBufferReader reader) =>
|
||||||
|
{
|
||||||
|
thirdReceivedMessageSender = sender;
|
||||||
|
|
||||||
|
reader.ReadValueSafe(out thirdReceivedMessageContent);
|
||||||
|
};
|
||||||
|
|
||||||
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
var messageContent = new ForceNetworkSerializeByMemcpy<Guid>(Guid.NewGuid());
|
||||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||||
using (writer)
|
using (writer)
|
||||||
@@ -98,33 +167,16 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessageToAll(writer);
|
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessageToAll(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong firstReceivedMessageSender = 0;
|
yield return WaitForMessageReceived<UnnamedMessage>(new List<NetworkManager> { FirstClient, SecondClient });
|
||||||
var firstReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
|
||||||
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
|
|
||||||
(ulong sender, FastBufferReader reader) =>
|
|
||||||
{
|
|
||||||
firstReceivedMessageSender = sender;
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out firstReceivedMessageContent);
|
|
||||||
};
|
|
||||||
|
|
||||||
ulong secondReceivedMessageSender = 0;
|
|
||||||
var secondReceivedMessageContent = new ForceNetworkSerializeByMemcpy<Guid>(new Guid());
|
|
||||||
SecondClient.CustomMessagingManager.OnUnnamedMessage +=
|
|
||||||
(ulong sender, FastBufferReader reader) =>
|
|
||||||
{
|
|
||||||
secondReceivedMessageSender = sender;
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out secondReceivedMessageContent);
|
|
||||||
};
|
|
||||||
|
|
||||||
yield return new WaitForSeconds(0.2f);
|
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, firstReceivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, firstReceivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
|
||||||
|
|
||||||
Assert.AreEqual(messageContent.Value, secondReceivedMessageContent.Value);
|
Assert.AreEqual(messageContent.Value, secondReceivedMessageContent.Value);
|
||||||
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
|
||||||
|
|
||||||
|
Assert.AreEqual(messageContent.Value, thirdReceivedMessageContent.Value);
|
||||||
|
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, thirdReceivedMessageSender);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using Unity.Collections;
|
||||||
using Unity.Multiplayer.Tools.MetricTypes;
|
using Unity.Multiplayer.Tools.MetricTypes;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.TestTools;
|
using UnityEngine.TestTools;
|
||||||
@@ -43,6 +44,18 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
return networkObject;
|
return networkObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GetWriteSizeForOwnerChange(NetworkObject networkObject, ulong newOwner)
|
||||||
|
{
|
||||||
|
var message = new ChangeOwnershipMessage
|
||||||
|
{
|
||||||
|
NetworkObjectId = networkObject.NetworkObjectId,
|
||||||
|
OwnerClientId = newOwner
|
||||||
|
};
|
||||||
|
using var writer = new FastBufferWriter(1024, Allocator.Temp);
|
||||||
|
message.Serialize(writer, message.Version);
|
||||||
|
return writer.Length;
|
||||||
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator TrackOwnershipChangeSentMetric()
|
public IEnumerator TrackOwnershipChangeSentMetric()
|
||||||
{
|
{
|
||||||
@@ -68,7 +81,9 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
ownershipChangeSent = metricValues.Last();
|
ownershipChangeSent = metricValues.Last();
|
||||||
Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeSent.NetworkId.NetworkId);
|
Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeSent.NetworkId.NetworkId);
|
||||||
Assert.AreEqual(Client.LocalClientId, ownershipChangeSent.Connection.Id);
|
Assert.AreEqual(Client.LocalClientId, ownershipChangeSent.Connection.Id);
|
||||||
Assert.AreEqual(FastBufferWriter.GetWriteSize<ChangeOwnershipMessage>() + k_MessageHeaderSize, ownershipChangeSent.BytesCount);
|
|
||||||
|
var serializedLength = GetWriteSizeForOwnerChange(networkObject, 1);
|
||||||
|
Assert.AreEqual(serializedLength + k_MessageHeaderSize, ownershipChangeSent.BytesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
@@ -89,7 +104,9 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
|
|
||||||
var ownershipChangeReceived = metricValues.First();
|
var ownershipChangeReceived = metricValues.First();
|
||||||
Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeReceived.NetworkId.NetworkId);
|
Assert.AreEqual(networkObject.NetworkObjectId, ownershipChangeReceived.NetworkId.NetworkId);
|
||||||
Assert.AreEqual(FastBufferWriter.GetWriteSize<ChangeOwnershipMessage>(), ownershipChangeReceived.BytesCount);
|
|
||||||
|
var serializedLength = GetWriteSizeForOwnerChange(networkObject, 1);
|
||||||
|
Assert.AreEqual(serializedLength, ownershipChangeReceived.BytesCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using Unity.Collections;
|
||||||
using Unity.Multiplayer.Tools.MetricTypes;
|
using Unity.Multiplayer.Tools.MetricTypes;
|
||||||
using UnityEngine.TestTools;
|
using UnityEngine.TestTools;
|
||||||
using Unity.Netcode.TestHelpers.Runtime.Metrics;
|
using Unity.Netcode.TestHelpers.Runtime.Metrics;
|
||||||
@@ -11,10 +12,8 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
{
|
{
|
||||||
internal class ServerLogsMetricTests : SingleClientMetricTestBase
|
internal class ServerLogsMetricTests : SingleClientMetricTestBase
|
||||||
{
|
{
|
||||||
// Header is dynamically sized due to packing, will be 2 bytes for all test messages.
|
// Header is dynamically sized due to packing, will be 3 bytes for all test messages.
|
||||||
private const int k_MessageHeaderSize = 2;
|
private const int k_MessageHeaderSize = 3;
|
||||||
private static readonly int k_ServerLogSentMessageOverhead = 2 + k_MessageHeaderSize;
|
|
||||||
private static readonly int k_ServerLogReceivedMessageOverhead = 2;
|
|
||||||
|
|
||||||
protected override IEnumerator OnSetup()
|
protected override IEnumerator OnSetup()
|
||||||
{
|
{
|
||||||
@@ -22,6 +21,19 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
return base.OnSetup();
|
return base.OnSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int GetWriteSizeForLog(NetworkLog.LogType logType, string logMessage)
|
||||||
|
{
|
||||||
|
var message = new ServerLogMessage
|
||||||
|
{
|
||||||
|
LogType = logType,
|
||||||
|
Message = logMessage
|
||||||
|
};
|
||||||
|
using var writer = new FastBufferWriter(1024, Allocator.Temp);
|
||||||
|
message.Serialize(writer, message.Version);
|
||||||
|
return writer.Length;
|
||||||
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator TrackServerLogSentMetric()
|
public IEnumerator TrackServerLogSentMetric()
|
||||||
{
|
{
|
||||||
@@ -41,7 +53,9 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
var sentMetric = sentMetrics.First();
|
var sentMetric = sentMetrics.First();
|
||||||
Assert.AreEqual(Server.LocalClientId, sentMetric.Connection.Id);
|
Assert.AreEqual(Server.LocalClientId, sentMetric.Connection.Id);
|
||||||
Assert.AreEqual((uint)NetworkLog.LogType.Warning, (uint)sentMetric.LogLevel);
|
Assert.AreEqual((uint)NetworkLog.LogType.Warning, (uint)sentMetric.LogLevel);
|
||||||
Assert.AreEqual(message.Length + k_ServerLogSentMessageOverhead, sentMetric.BytesCount);
|
|
||||||
|
var serializedLength = GetWriteSizeForLog(NetworkLog.LogType.Warning, message);
|
||||||
|
Assert.AreEqual(serializedLength + k_MessageHeaderSize, sentMetric.BytesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnityTest]
|
[UnityTest]
|
||||||
@@ -64,7 +78,9 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
|||||||
var receivedMetric = receivedMetrics.First();
|
var receivedMetric = receivedMetrics.First();
|
||||||
Assert.AreEqual(Client.LocalClientId, receivedMetric.Connection.Id);
|
Assert.AreEqual(Client.LocalClientId, receivedMetric.Connection.Id);
|
||||||
Assert.AreEqual((uint)NetworkLog.LogType.Warning, (uint)receivedMetric.LogLevel);
|
Assert.AreEqual((uint)NetworkLog.LogType.Warning, (uint)receivedMetric.LogLevel);
|
||||||
Assert.AreEqual(message.Length + k_ServerLogReceivedMessageOverhead, receivedMetric.BytesCount);
|
|
||||||
|
var serializedLength = GetWriteSizeForLog(NetworkLog.LogType.Warning, message);
|
||||||
|
Assert.AreEqual(serializedLength, receivedMetric.BytesCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,12 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
Assert.IsFalse(serverBehaviour.IsOwnedByServer);
|
Assert.IsFalse(serverBehaviour.IsOwnedByServer);
|
||||||
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, serverBehaviour.OwnerClientId);
|
Assert.AreEqual(m_ClientNetworkManagers[0].LocalClientId, serverBehaviour.OwnerClientId);
|
||||||
|
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var clientObject = Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID).Where((obj) => obj.NetworkManagerOwner == m_ClientNetworkManagers[0]).FirstOrDefault();
|
||||||
|
#else
|
||||||
var clientObject = Object.FindObjectsOfType<NetworkObject>().Where((obj) => obj.NetworkManagerOwner == m_ClientNetworkManagers[0]).FirstOrDefault();
|
var clientObject = Object.FindObjectsOfType<NetworkObject>().Where((obj) => obj.NetworkManagerOwner == m_ClientNetworkManagers[0]).FirstOrDefault();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
Assert.IsNotNull(clientObject);
|
Assert.IsNotNull(clientObject);
|
||||||
Assert.IsTrue(clientObject.IsOwner);
|
Assert.IsTrue(clientObject.IsOwner);
|
||||||
|
|||||||
@@ -1,233 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using Unity.Collections;
|
|
||||||
using Unity.Netcode.TestHelpers.Runtime;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.RuntimeTests
|
|
||||||
{
|
|
||||||
public class NetworkObjectSceneSerializationTests
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The purpose behind this test is to assure that in-scene NetworkObjects
|
|
||||||
/// that are serialized into a single stream (approval or switch scene this happens)
|
|
||||||
/// will continue to be processed even if one of the NetworkObjects is invalid.
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void NetworkObjectSceneSerializationFailure()
|
|
||||||
{
|
|
||||||
var networkObjectsToTest = new List<GameObject>();
|
|
||||||
|
|
||||||
var writer = new FastBufferWriter(1300, Allocator.Temp, 4096000);
|
|
||||||
var invalidNetworkObjectOffsets = new List<long>();
|
|
||||||
var invalidNetworkObjectIdCount = new List<int>();
|
|
||||||
var invalidNetworkObjects = new List<GameObject>();
|
|
||||||
var invalidNetworkObjectFrequency = 3;
|
|
||||||
using (writer)
|
|
||||||
{
|
|
||||||
// Construct 50 NetworkObjects
|
|
||||||
for (int i = 0; i < 50; i++)
|
|
||||||
{
|
|
||||||
// Inject an invalid NetworkObject every [invalidNetworkObjectFrequency] entry
|
|
||||||
if ((i % invalidNetworkObjectFrequency) == 0)
|
|
||||||
{
|
|
||||||
// Create the invalid NetworkObject
|
|
||||||
var gameObject = new GameObject($"InvalidTestObject{i}");
|
|
||||||
|
|
||||||
Assert.IsNotNull(gameObject);
|
|
||||||
|
|
||||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
|
||||||
|
|
||||||
Assert.IsNotNull(networkObject);
|
|
||||||
|
|
||||||
var networkVariableComponent = gameObject.AddComponent<NetworkBehaviourWithNetworkVariables>();
|
|
||||||
Assert.IsNotNull(networkVariableComponent);
|
|
||||||
|
|
||||||
// Add invalid NetworkObject's starting position before serialization to handle trapping for the Debug.LogError message
|
|
||||||
// that we know will be thrown
|
|
||||||
invalidNetworkObjectOffsets.Add(writer.Position);
|
|
||||||
|
|
||||||
networkObject.GlobalObjectIdHash = (uint)(i);
|
|
||||||
invalidNetworkObjectIdCount.Add(i);
|
|
||||||
|
|
||||||
invalidNetworkObjects.Add(gameObject);
|
|
||||||
|
|
||||||
writer.WriteValueSafe((int)networkObject.GetSceneOriginHandle());
|
|
||||||
// Serialize the invalid NetworkObject
|
|
||||||
var sceneObject = networkObject.GetMessageSceneObject(0);
|
|
||||||
var prePosition = writer.Position;
|
|
||||||
sceneObject.Serialize(writer);
|
|
||||||
|
|
||||||
Debug.Log(
|
|
||||||
$"Invalid {nameof(NetworkObject)} Size {writer.Position - prePosition}");
|
|
||||||
|
|
||||||
// Now adjust how frequent we will inject invalid NetworkObjects
|
|
||||||
invalidNetworkObjectFrequency = Random.Range(2, 5);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create a valid NetworkObject
|
|
||||||
var gameObject = new GameObject($"TestObject{i}");
|
|
||||||
|
|
||||||
Assert.IsNotNull(gameObject);
|
|
||||||
|
|
||||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
|
||||||
|
|
||||||
var networkVariableComponent = gameObject.AddComponent<NetworkBehaviourWithNetworkVariables>();
|
|
||||||
Assert.IsNotNull(networkVariableComponent);
|
|
||||||
|
|
||||||
Assert.IsNotNull(networkObject);
|
|
||||||
|
|
||||||
networkObject.GlobalObjectIdHash = (uint)(i + 4096);
|
|
||||||
|
|
||||||
networkObjectsToTest.Add(gameObject);
|
|
||||||
|
|
||||||
writer.WriteValueSafe(networkObject.GetSceneOriginHandle());
|
|
||||||
|
|
||||||
// Handle populating the scenes loaded list
|
|
||||||
var scene = networkObject.gameObject.scene;
|
|
||||||
|
|
||||||
if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey(
|
|
||||||
scene.handle))
|
|
||||||
{
|
|
||||||
NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded
|
|
||||||
.Add(scene.handle, scene);
|
|
||||||
}
|
|
||||||
var handle = networkObject.GetSceneOriginHandle();
|
|
||||||
// Since this is a unit test, we will fake the server to client handle lookup by just adding the same handle key and value
|
|
||||||
if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle
|
|
||||||
.ContainsKey(handle))
|
|
||||||
{
|
|
||||||
NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle
|
|
||||||
.Add(handle, handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the valid NetworkObject
|
|
||||||
var sceneObject = networkObject.GetMessageSceneObject(0);
|
|
||||||
sceneObject.Serialize(writer);
|
|
||||||
|
|
||||||
if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.ContainsKey(
|
|
||||||
networkObject.GlobalObjectIdHash))
|
|
||||||
{
|
|
||||||
NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add(
|
|
||||||
networkObject.GlobalObjectIdHash, new Dictionary<int, NetworkObject>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add this valid NetworkObject into the ScenePlacedObjects list
|
|
||||||
NetworkManagerHelper.NetworkManagerObject.SceneManager
|
|
||||||
.ScenePlacedObjects[networkObject.GlobalObjectIdHash]
|
|
||||||
.Add(SceneManager.GetActiveScene().handle, networkObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalBufferSize = writer.Position;
|
|
||||||
|
|
||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
|
||||||
using (reader)
|
|
||||||
{
|
|
||||||
|
|
||||||
var networkObjectsDeSerialized = new List<NetworkObject>();
|
|
||||||
var currentLogLevel = NetworkManager.Singleton.LogLevel;
|
|
||||||
var invalidNetworkObjectCount = 0;
|
|
||||||
while (reader.Position != totalBufferSize)
|
|
||||||
{
|
|
||||||
// If we reach the point where we expect it to fail, then make sure we let TestRunner know it should expect this log error message
|
|
||||||
if (invalidNetworkObjectOffsets.Count > 0 &&
|
|
||||||
reader.Position == invalidNetworkObjectOffsets[0])
|
|
||||||
{
|
|
||||||
invalidNetworkObjectOffsets.RemoveAt(0);
|
|
||||||
|
|
||||||
// Turn off Network Logging to avoid other errors that we know will happen after the below LogAssert.Expect message occurs.
|
|
||||||
NetworkManager.Singleton.LogLevel = LogLevel.Nothing;
|
|
||||||
|
|
||||||
// Trap for this specific error message so we don't make Test Runner think we failed (it will fail on Debug.LogError)
|
|
||||||
UnityEngine.TestTools.LogAssert.Expect(LogType.Error,
|
|
||||||
$"Failed to spawn {nameof(NetworkObject)} for Hash {invalidNetworkObjectIdCount[invalidNetworkObjectCount]}.");
|
|
||||||
|
|
||||||
invalidNetworkObjectCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out int handle);
|
|
||||||
NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(handle);
|
|
||||||
var sceneObject = new NetworkObject.SceneObject();
|
|
||||||
sceneObject.Deserialize(reader);
|
|
||||||
|
|
||||||
var deserializedNetworkObject = NetworkObject.AddSceneObject(sceneObject, reader,
|
|
||||||
NetworkManagerHelper.NetworkManagerObject);
|
|
||||||
if (deserializedNetworkObject != null)
|
|
||||||
{
|
|
||||||
networkObjectsDeSerialized.Add(deserializedNetworkObject);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Under this condition, we are expecting null (i.e. no NetworkObject instantiated)
|
|
||||||
// and will set our log level back to the original value to assure the valid NetworkObjects
|
|
||||||
// aren't causing any log Errors to occur
|
|
||||||
NetworkManager.Singleton.LogLevel = currentLogLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now validate all NetworkObjects returned against the original NetworkObjects we created
|
|
||||||
// after they validate, destroy the objects
|
|
||||||
foreach (var entry in networkObjectsToTest)
|
|
||||||
{
|
|
||||||
var entryNetworkObject = entry.GetComponent<NetworkObject>();
|
|
||||||
Assert.IsTrue(networkObjectsDeSerialized.Contains(entryNetworkObject));
|
|
||||||
Object.Destroy(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy the invalid network objects
|
|
||||||
foreach (var entry in invalidNetworkObjects)
|
|
||||||
{
|
|
||||||
Object.Destroy(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
// Create, instantiate, and host
|
|
||||||
NetworkManagerHelper.StartNetworkManager(out NetworkManager networkManager, NetworkManagerHelper.NetworkManagerOperatingMode.None);
|
|
||||||
networkManager.NetworkConfig.EnableSceneManagement = true;
|
|
||||||
networkManager.StartHost();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[TearDown]
|
|
||||||
public void TearDown()
|
|
||||||
{
|
|
||||||
// Stop, shutdown, and destroy
|
|
||||||
NetworkManagerHelper.ShutdownNetworkManager();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A simple test class that will provide varying NetworkBuffer stream sizes
|
|
||||||
/// when the NetworkVariable is serialized
|
|
||||||
/// </summary>
|
|
||||||
public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour
|
|
||||||
{
|
|
||||||
private const uint k_MinDataBlocks = 1;
|
|
||||||
private const uint k_MaxDataBlocks = 64;
|
|
||||||
|
|
||||||
public NetworkList<ulong> NetworkVariableData;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
var dataBlocksAssigned = new List<ulong>();
|
|
||||||
var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks);
|
|
||||||
for (var i = 0; i < numberDataBlocks; i++)
|
|
||||||
{
|
|
||||||
dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkVariableData = new NetworkList<ulong>(dataBlocksAssigned);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
624
Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
Normal file
624
Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
Normal file
@@ -0,0 +1,624 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Netcode.TestHelpers.Runtime;
|
||||||
|
using Random = UnityEngine.Random;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.RuntimeTests
|
||||||
|
{
|
||||||
|
[TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Host)]
|
||||||
|
[TestFixture(VariableLengthSafety.EnabledNetVarSafety, HostOrServer.Host)]
|
||||||
|
[TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Server)]
|
||||||
|
[TestFixture(VariableLengthSafety.EnabledNetVarSafety, HostOrServer.Server)]
|
||||||
|
public class NetworkObjectSynchronizationTests : NetcodeIntegrationTest
|
||||||
|
{
|
||||||
|
private const int k_NumberToSpawn = 30;
|
||||||
|
protected override int NumberOfClients => 0;
|
||||||
|
|
||||||
|
private GameObject m_NetworkPrefab;
|
||||||
|
private GameObject m_InValidNetworkPrefab;
|
||||||
|
private GameObject m_SynchronizationPrefab;
|
||||||
|
private GameObject m_OnSynchronizePrefab;
|
||||||
|
private VariableLengthSafety m_VariableLengthSafety;
|
||||||
|
|
||||||
|
private LogLevel m_CurrentLogLevel;
|
||||||
|
|
||||||
|
public enum VariableLengthSafety
|
||||||
|
{
|
||||||
|
DisableNetVarSafety,
|
||||||
|
EnabledNetVarSafety,
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkObjectSynchronizationTests(VariableLengthSafety variableLengthSafety, HostOrServer hostOrServer)
|
||||||
|
{
|
||||||
|
m_VariableLengthSafety = variableLengthSafety;
|
||||||
|
m_UseHost = hostOrServer == HostOrServer.Host;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnCreatePlayerPrefab()
|
||||||
|
{
|
||||||
|
m_PlayerPrefab.AddComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
base.OnCreatePlayerPrefab();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnServerAndClientsCreated()
|
||||||
|
{
|
||||||
|
|
||||||
|
// Set the NetworkVariable Safety Check setting
|
||||||
|
m_ServerNetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
|
||||||
|
|
||||||
|
// Ignore the errors generated during this test (they are expected)
|
||||||
|
m_ServerNetworkManager.LogLevel = LogLevel.Nothing;
|
||||||
|
|
||||||
|
// Disable forcing the same prefabs to avoid failed connections
|
||||||
|
m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false;
|
||||||
|
|
||||||
|
// Create the valid network prefab
|
||||||
|
m_NetworkPrefab = CreateNetworkObjectPrefab("ValidObject");
|
||||||
|
m_NetworkPrefab.AddComponent<NetworkBehaviourWithNetworkVariables>();
|
||||||
|
|
||||||
|
// Create the invalid network prefab (that will fail on client side)
|
||||||
|
m_InValidNetworkPrefab = CreateNetworkObjectPrefab("InvalidObject");
|
||||||
|
m_InValidNetworkPrefab.AddComponent<NetworkBehaviourWithNetworkVariables>();
|
||||||
|
|
||||||
|
// Create the synchronization network prefab (some pass and some fail)
|
||||||
|
m_SynchronizationPrefab = CreateNetworkObjectPrefab("SyncObject");
|
||||||
|
m_SynchronizationPrefab.AddComponent<NetworkBehaviourSynchronizeFailureComponent>();
|
||||||
|
m_SynchronizationPrefab.AddComponent<NetworkBehaviourWithNetworkVariables>();
|
||||||
|
|
||||||
|
m_OnSynchronizePrefab = CreateNetworkObjectPrefab("OnSyncObject");
|
||||||
|
m_OnSynchronizePrefab.AddComponent<NetworkBehaviourOnSynchronizeComponent>();
|
||||||
|
|
||||||
|
base.OnServerAndClientsCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewClientCreated(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
|
||||||
|
networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
|
||||||
|
foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.NetworkPrefabs)
|
||||||
|
{
|
||||||
|
// To simulate a failure, we exclude the m_InValidNetworkPrefab from the connecting
|
||||||
|
// client's side.
|
||||||
|
if (networkPrefab.Prefab.name != m_InValidNetworkPrefab.name)
|
||||||
|
{
|
||||||
|
networkManager.NetworkConfig.NetworkPrefabs.Add(networkPrefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Disable forcing the same prefabs to avoid failed connections
|
||||||
|
networkManager.NetworkConfig.ForceSamePrefabs = false;
|
||||||
|
networkManager.LogLevel = m_CurrentLogLevel;
|
||||||
|
base.OnNewClientCreated(networkManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator NetworkObjectDeserializationFailure()
|
||||||
|
{
|
||||||
|
m_CurrentLogLevel = LogLevel.Nothing;
|
||||||
|
var validSpawnedNetworkObjects = new List<GameObject>();
|
||||||
|
NetworkBehaviourWithNetworkVariables.ResetSpawnCount();
|
||||||
|
|
||||||
|
// Spawn NetworkObjects on the server side with half of them being the
|
||||||
|
// invalid network prefabs to simulate NetworkObject synchronization failure
|
||||||
|
for (int i = 0; i < k_NumberToSpawn; i++)
|
||||||
|
{
|
||||||
|
if (i % 2 == 0)
|
||||||
|
{
|
||||||
|
SpawnObject(m_InValidNetworkPrefab, m_ServerNetworkManager);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Keep track of the prefabs that should successfully spawn on the client side
|
||||||
|
validSpawnedNetworkObjects.Add(SpawnObject(m_NetworkPrefab, m_ServerNetworkManager));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assure the server-side spawned all NetworkObjects
|
||||||
|
yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.ServerSpawnCount == k_NumberToSpawn);
|
||||||
|
|
||||||
|
// Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
|
||||||
|
yield return CreateAndStartNewClient();
|
||||||
|
|
||||||
|
if (m_UseHost)
|
||||||
|
{
|
||||||
|
var serverSideClientPlayerComponent = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
var serverSideHostPlayerComponent = m_ServerNetworkManager.LocalClient.PlayerObject.GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
var clientSidePlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
var clientSideHostPlayerComponent = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ServerNetworkManager.LocalClientId].GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
|
||||||
|
// Validate that the client side player values match the server side value of the client's player
|
||||||
|
Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData1.Value == clientSidePlayerComponent.NetworkVariableData1.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})" +
|
||||||
|
$" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})!");
|
||||||
|
Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData2.Value == clientSidePlayerComponent.NetworkVariableData2.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})" +
|
||||||
|
$" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})!");
|
||||||
|
Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData3.Value == clientSidePlayerComponent.NetworkVariableData3.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})" +
|
||||||
|
$" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})!");
|
||||||
|
Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData4.Value == clientSidePlayerComponent.NetworkVariableData4.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})" +
|
||||||
|
$" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})!");
|
||||||
|
|
||||||
|
|
||||||
|
// Validate that only the 2nd and 4th NetworkVariable on the client side instance of the host's player is the same and the other two do not match
|
||||||
|
// (i.e. NetworkVariables owned by the server should not get synchronized on client)
|
||||||
|
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData1.Value != clientSideHostPlayerComponent.NetworkVariableData1.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData1.Value})" +
|
||||||
|
$" should not be equal to the server side value ({clientSideHostPlayerComponent.NetworkVariableData1.Value})!");
|
||||||
|
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData2.Value == clientSideHostPlayerComponent.NetworkVariableData2.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData2.Value})" +
|
||||||
|
$" does not equal the server side value ({clientSideHostPlayerComponent.NetworkVariableData2.Value})!");
|
||||||
|
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData3.Value != clientSideHostPlayerComponent.NetworkVariableData3.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData3.Value})" +
|
||||||
|
$" should not be equal to the server side value ({clientSideHostPlayerComponent.NetworkVariableData3.Value})!");
|
||||||
|
Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData4.Value == clientSideHostPlayerComponent.NetworkVariableData4.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData4.Value})" +
|
||||||
|
$" does not equal the server side value ({clientSideHostPlayerComponent.NetworkVariableData4.Value})!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Spawn and connect another client when running as a server
|
||||||
|
yield return CreateAndStartNewClient();
|
||||||
|
yield return WaitForConditionOrTimeOut(() => m_PlayerNetworkObjects[2].Count > 1);
|
||||||
|
AssertOnTimeout($"Timed out waiting for second client to have access to the first client's cloned player object!");
|
||||||
|
|
||||||
|
var clientSide1PlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
var clientSide2Player1Clone = m_PlayerNetworkObjects[2][clientSide1PlayerComponent.OwnerClientId].GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
var clientOneId = clientSide1PlayerComponent.OwnerClientId;
|
||||||
|
|
||||||
|
var clientSide2PlayerComponent = m_ClientNetworkManagers[1].LocalClient.PlayerObject.GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
var clientSide1Player2Clone = m_PlayerNetworkObjects[1][clientSide2PlayerComponent.OwnerClientId].GetComponent<NetworkBehaviourWithOwnerNetworkVariables>();
|
||||||
|
var clientTwoId = clientSide2PlayerComponent.OwnerClientId;
|
||||||
|
|
||||||
|
// Validate that client one's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not
|
||||||
|
Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData1.Value != clientSide2Player1Clone.NetworkVariableData1.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData1.Value})" +
|
||||||
|
$" should not be equal to Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData1.Value})!");
|
||||||
|
|
||||||
|
Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData2.Value == clientSide2Player1Clone.NetworkVariableData2.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData2.Value})" +
|
||||||
|
$" does not equal Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData2.Value})!");
|
||||||
|
|
||||||
|
Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData3.Value != clientSide2Player1Clone.NetworkVariableData3.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData3.Value})" +
|
||||||
|
$" should not be equal to Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData3.Value})!");
|
||||||
|
|
||||||
|
Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData4.Value == clientSide2Player1Clone.NetworkVariableData4.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData4.Value})" +
|
||||||
|
$" does not equal Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData4.Value})!");
|
||||||
|
|
||||||
|
|
||||||
|
// Validate that client two's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not
|
||||||
|
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData1.Value != clientSide1Player2Clone.NetworkVariableData1.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData1.Value})" +
|
||||||
|
$" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData1.Value})!");
|
||||||
|
|
||||||
|
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData2.Value == clientSide1Player2Clone.NetworkVariableData2.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData2.Value})" +
|
||||||
|
$" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData2.Value})!");
|
||||||
|
|
||||||
|
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData3.Value != clientSide1Player2Clone.NetworkVariableData3.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData3.Value})" +
|
||||||
|
$" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData3.Value})!");
|
||||||
|
|
||||||
|
Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData4.Value == clientSide1Player2Clone.NetworkVariableData4.Value,
|
||||||
|
$"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData4.Value})" +
|
||||||
|
$" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData4.Value})!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now validate all of the NetworkVariable values match to assure everything synchronized properly
|
||||||
|
foreach (var spawnedObject in validSpawnedNetworkObjects)
|
||||||
|
{
|
||||||
|
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||||
|
{
|
||||||
|
//Validate that the connected client has spawned all of the instances that shouldn't have failed.
|
||||||
|
var clientSideNetworkObjects = s_GlobalNetworkObjects[clientNetworkManager.LocalClientId];
|
||||||
|
|
||||||
|
Assert.IsTrue(NetworkBehaviourWithNetworkVariables.ClientSpawnCount[clientNetworkManager.LocalClientId] == validSpawnedNetworkObjects.Count, $"Client-{clientNetworkManager.LocalClientId} spawned " +
|
||||||
|
$"({NetworkBehaviourWithNetworkVariables.ClientSpawnCount}) {nameof(NetworkObject)}s but the expected number of {nameof(NetworkObject)}s should have been ({validSpawnedNetworkObjects.Count})!");
|
||||||
|
|
||||||
|
var spawnedNetworkObject = spawnedObject.GetComponent<NetworkObject>();
|
||||||
|
Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedNetworkObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a " +
|
||||||
|
$"{nameof(NetworkObject.NetworkObjectId)} of {spawnedNetworkObject.NetworkObjectId}");
|
||||||
|
|
||||||
|
var clientSideObject = clientSideNetworkObjects[spawnedNetworkObject.NetworkObjectId];
|
||||||
|
Assert.IsTrue(clientSideObject.NetworkManager == clientNetworkManager, $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!");
|
||||||
|
|
||||||
|
ValidateNetworkBehaviourWithNetworkVariables(spawnedNetworkObject, clientSideObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateNetworkBehaviourWithNetworkVariables(NetworkObject serverSideNetworkObject, NetworkObject clientSideNetworkObject)
|
||||||
|
{
|
||||||
|
var serverSideComponent = serverSideNetworkObject.GetComponent<NetworkBehaviourWithNetworkVariables>();
|
||||||
|
var clientSideComponent = clientSideNetworkObject.GetComponent<NetworkBehaviourWithNetworkVariables>();
|
||||||
|
|
||||||
|
string netVarName1 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1);
|
||||||
|
string netVarName2 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1);
|
||||||
|
string netVarName3 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1);
|
||||||
|
string netVarName4 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1);
|
||||||
|
|
||||||
|
Assert.IsTrue(serverSideComponent.NetworkVariableData1.Count == clientSideComponent.NetworkVariableData1.Count, $"[{serverSideComponent.name}:{netVarName1}] Server side {nameof(NetworkList<byte>)} " +
|
||||||
|
$"count ({serverSideComponent.NetworkVariableData1.Count}) does not match the client side {nameof(NetworkList<byte>)} count ({clientSideComponent.NetworkVariableData1.Count})!");
|
||||||
|
|
||||||
|
for (int i = 0; i < serverSideComponent.NetworkVariableData1.Count; i++)
|
||||||
|
{
|
||||||
|
Assert.IsTrue(serverSideComponent.NetworkVariableData1[i] == clientSideComponent.NetworkVariableData1[i], $"[{serverSideComponent.name}:{netVarName1}][Index:{i}] Server side instance value " +
|
||||||
|
$"({serverSideComponent.NetworkVariableData1[i]}) does not match the client side instance value ({clientSideComponent.NetworkVariableData1[i]})!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.IsTrue(serverSideComponent.NetworkVariableData2.Value == clientSideComponent.NetworkVariableData2.Value, $"[{serverSideComponent.name}:{netVarName2}] Server side instance value ({serverSideComponent.NetworkVariableData2.Value}) " +
|
||||||
|
$"does not match the client side instance value ({clientSideComponent.NetworkVariableData2.Value})!");
|
||||||
|
Assert.IsTrue(serverSideComponent.NetworkVariableData3.Value == clientSideComponent.NetworkVariableData3.Value, $"[{serverSideComponent.name}:{netVarName3}] Server side instance value ({serverSideComponent.NetworkVariableData3.Value}) " +
|
||||||
|
$"does not match the client side instance value ({clientSideComponent.NetworkVariableData3.Value})!");
|
||||||
|
Assert.IsTrue(serverSideComponent.NetworkVariableData4.Value == clientSideComponent.NetworkVariableData4.Value, $"[{serverSideComponent.name}:{netVarName4}] Server side instance value ({serverSideComponent.NetworkVariableData4.Value}) " +
|
||||||
|
$"does not match the client side instance value ({clientSideComponent.NetworkVariableData4.Value})!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This validates that when a NetworkBehaviour fails serialization or deserialization during synchronizations that other NetworkBehaviours
|
||||||
|
/// will still be initialized properly
|
||||||
|
/// </summary>
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator NetworkBehaviourSynchronization()
|
||||||
|
{
|
||||||
|
m_ServerNetworkManager.LogLevel = LogLevel.Normal;
|
||||||
|
m_CurrentLogLevel = LogLevel.Normal;
|
||||||
|
NetworkBehaviourSynchronizeFailureComponent.ResetBehaviour();
|
||||||
|
|
||||||
|
var spawnedObjectList = new List<GameObject>();
|
||||||
|
var numberOfObjectsToSpawn = NetworkBehaviourSynchronizeFailureComponent.NumberOfFailureTypes * 4;
|
||||||
|
// Spawn 11 more NetworkObjects where there should be 4 of each failure type
|
||||||
|
for (int i = 0; i < numberOfObjectsToSpawn; i++)
|
||||||
|
{
|
||||||
|
var synchronizationObject = SpawnObject(m_SynchronizationPrefab, m_ServerNetworkManager);
|
||||||
|
var synchronizationBehaviour = synchronizationObject.GetComponent<NetworkBehaviourSynchronizeFailureComponent>();
|
||||||
|
synchronizationBehaviour.AssignNextFailureType();
|
||||||
|
spawnedObjectList.Add(synchronizationObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
|
||||||
|
yield return CreateAndStartNewClient();
|
||||||
|
|
||||||
|
// Validate that when a NetworkBehaviour fails to synchronize and is skipped over it does not
|
||||||
|
// impact the rest of the NetworkBehaviours.
|
||||||
|
var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
|
||||||
|
foreach (var spawnedObject in spawnedObjectList)
|
||||||
|
{
|
||||||
|
var serverSideSpawnedNetworkObject = spawnedObject.GetComponent<NetworkObject>();
|
||||||
|
var clientSideObject = clientSideNetworkObjects[serverSideSpawnedNetworkObject.NetworkObjectId];
|
||||||
|
var clientSideSpawnedNetworkObject = clientSideObject.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
|
ValidateNetworkBehaviourWithNetworkVariables(serverSideSpawnedNetworkObject, clientSideSpawnedNetworkObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A basic validation for the NetworkBehaviour.OnSynchronize method
|
||||||
|
/// </summary>
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator NetworkBehaviourOnSynchronize()
|
||||||
|
{
|
||||||
|
var serverSideInstance = SpawnObject(m_OnSynchronizePrefab, m_ServerNetworkManager).GetComponent<NetworkBehaviourOnSynchronizeComponent>();
|
||||||
|
|
||||||
|
// Now spawn and connect a client that will have custom serialized data applied during the client synchronization process.
|
||||||
|
yield return CreateAndStartNewClient();
|
||||||
|
|
||||||
|
var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
|
||||||
|
var clientSideInstance = clientSideNetworkObjects[serverSideInstance.NetworkObjectId].GetComponent<NetworkBehaviourOnSynchronizeComponent>();
|
||||||
|
|
||||||
|
// Validate the values match
|
||||||
|
Assert.IsTrue(serverSideInstance.CustomSerializationData.Value1 == clientSideInstance.CustomSerializationData.Value1, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value1}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value1})");
|
||||||
|
Assert.IsTrue(serverSideInstance.CustomSerializationData.Value2 == clientSideInstance.CustomSerializationData.Value2, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value2}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value2})");
|
||||||
|
Assert.IsTrue(serverSideInstance.CustomSerializationData.Value3 == clientSideInstance.CustomSerializationData.Value3, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value3}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value3})");
|
||||||
|
Assert.IsTrue(serverSideInstance.CustomSerializationData.Value4 == clientSideInstance.CustomSerializationData.Value4, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value4}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value4})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A test NetworkBeahviour that provides a varying NetworkList size as well as
|
||||||
|
/// additional NetworkVariables to assure if a NetworkObject fails to be created
|
||||||
|
/// the synchronization process will continue (i.e. it will skip over that block
|
||||||
|
/// of the reader buffer).
|
||||||
|
/// </summary>
|
||||||
|
public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public static int ServerSpawnCount { get; internal set; }
|
||||||
|
public static readonly Dictionary<ulong, int> ClientSpawnCount = new Dictionary<ulong, int>();
|
||||||
|
|
||||||
|
public static void ResetSpawnCount()
|
||||||
|
{
|
||||||
|
ServerSpawnCount = 0;
|
||||||
|
ClientSpawnCount.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private const uint k_MinDataBlocks = 1;
|
||||||
|
private const uint k_MaxDataBlocks = 64;
|
||||||
|
|
||||||
|
// Add various types of NetworkVariables
|
||||||
|
public NetworkList<ulong> NetworkVariableData1;
|
||||||
|
public NetworkVariable<int> NetworkVariableData2;
|
||||||
|
public NetworkVariable<long> NetworkVariableData3;
|
||||||
|
public NetworkVariable<byte> NetworkVariableData4;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
var dataBlocksAssigned = new List<ulong>();
|
||||||
|
var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks);
|
||||||
|
for (var i = 0; i < numberDataBlocks; i++)
|
||||||
|
{
|
||||||
|
dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkVariableData1 = new NetworkList<ulong>(dataBlocksAssigned);
|
||||||
|
NetworkVariableData2 = new NetworkVariable<int>(Random.Range(1, 1000));
|
||||||
|
NetworkVariableData3 = new NetworkVariable<long>(Random.Range(1, 1000));
|
||||||
|
NetworkVariableData4 = new NetworkVariable<byte>((byte)Random.Range(1, 255));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkSpawn()
|
||||||
|
{
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
ServerSpawnCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ClientSpawnCount.ContainsKey(NetworkManager.LocalClientId))
|
||||||
|
{
|
||||||
|
ClientSpawnCount.Add(NetworkManager.LocalClientId, 0);
|
||||||
|
}
|
||||||
|
ClientSpawnCount[NetworkManager.LocalClientId]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnNetworkSpawn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A test NetworkBeahviour that has varying permissions in order to validate that
|
||||||
|
/// when variable length safety checks are off NetworkVariables still are updated
|
||||||
|
/// properly.
|
||||||
|
/// </summary>
|
||||||
|
public class NetworkBehaviourWithOwnerNetworkVariables : NetworkBehaviour
|
||||||
|
{
|
||||||
|
|
||||||
|
// Should not synchronize on non-owners
|
||||||
|
public NetworkVariable<int> NetworkVariableData1 = new NetworkVariable<int>(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
|
||||||
|
// Should synchronize with everyone
|
||||||
|
public NetworkVariable<long> NetworkVariableData2 = new NetworkVariable<long>();
|
||||||
|
// Should not synchronize on non-owners
|
||||||
|
public NetworkVariable<byte> NetworkVariableData3 = new NetworkVariable<byte>(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
|
||||||
|
// Should synchronize with everyone
|
||||||
|
public NetworkVariable<ushort> NetworkVariableData4 = new NetworkVariable<ushort>();
|
||||||
|
|
||||||
|
public override void OnNetworkSpawn()
|
||||||
|
{
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
NetworkVariableData1.Value = Random.Range(1, 1000);
|
||||||
|
NetworkVariableData2.Value = Random.Range(1, 1000);
|
||||||
|
NetworkVariableData3.Value = (byte)Random.Range(1, 255);
|
||||||
|
NetworkVariableData4.Value = (ushort)Random.Range(1, ushort.MaxValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A test NetworkBeahviour that simulates various types of synchronization failures
|
||||||
|
/// and provides a synchronization success version to validate that synchronization
|
||||||
|
/// will continue if user synchronization code fails.
|
||||||
|
/// </summary>
|
||||||
|
public class NetworkBehaviourSynchronizeFailureComponent : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public static int NumberOfFailureTypes { get; internal set; }
|
||||||
|
public static int ServerSpawnCount { get; internal set; }
|
||||||
|
public static int ClientSpawnCount { get; internal set; }
|
||||||
|
|
||||||
|
private static FailureTypes s_FailureType = FailureTypes.None;
|
||||||
|
|
||||||
|
public enum FailureTypes
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
DuringWriting,
|
||||||
|
DuringReading,
|
||||||
|
DontReadAnything,
|
||||||
|
ThrowWriteSideException,
|
||||||
|
ThrowReadSideException
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ResetBehaviour()
|
||||||
|
{
|
||||||
|
ServerSpawnCount = 0;
|
||||||
|
ClientSpawnCount = 0;
|
||||||
|
s_FailureType = FailureTypes.None;
|
||||||
|
NumberOfFailureTypes = System.Enum.GetValues(typeof(FailureTypes)).Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MyCustomData m_MyCustomData;
|
||||||
|
|
||||||
|
private struct MyCustomData : INetworkSerializable
|
||||||
|
{
|
||||||
|
public FailureTypes FailureType;
|
||||||
|
private ushort m_DataSize;
|
||||||
|
private byte[] m_DataBlock;
|
||||||
|
|
||||||
|
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||||
|
{
|
||||||
|
if (serializer.IsWriter)
|
||||||
|
{
|
||||||
|
var writer = serializer.GetFastBufferWriter();
|
||||||
|
switch (FailureType)
|
||||||
|
{
|
||||||
|
case FailureTypes.None:
|
||||||
|
// We want to write something for these two cases
|
||||||
|
case FailureTypes.DuringReading:
|
||||||
|
case FailureTypes.DontReadAnything:
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(m_DataSize);
|
||||||
|
for (int i = 0; i < m_DataSize; i++)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(m_DataBlock[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FailureTypes.DuringWriting:
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(m_DataSize);
|
||||||
|
// Try to write past the allocated size to generate an exception
|
||||||
|
// while also filling the buffer to verify that the buffer will be
|
||||||
|
// reset back to the original position.
|
||||||
|
for (int i = 0; i <= m_DataSize; i++)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(m_DataBlock[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FailureTypes.ThrowWriteSideException:
|
||||||
|
{
|
||||||
|
throw new System.Exception("Write side exception!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var reader = serializer.GetFastBufferReader();
|
||||||
|
switch (FailureType)
|
||||||
|
{
|
||||||
|
case FailureTypes.None:
|
||||||
|
{
|
||||||
|
reader.ReadValueSafe(out m_DataSize);
|
||||||
|
m_DataBlock = new byte[m_DataSize];
|
||||||
|
for (int i = 0; i < m_DataSize; i++)
|
||||||
|
{
|
||||||
|
reader.ReadValueSafe(out m_DataBlock[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FailureTypes.DuringReading:
|
||||||
|
{
|
||||||
|
reader.ReadValueSafe(out m_DataSize);
|
||||||
|
// Allocate more space than needed
|
||||||
|
m_DataBlock = new byte[(int)(m_DataSize * 1.5f)];
|
||||||
|
// Now read past the size of this message to verify
|
||||||
|
// that the reader will get rest back to the appropriate
|
||||||
|
// position and an error will be generated for this
|
||||||
|
for (int i = 0; i < m_DataBlock.Length; i++)
|
||||||
|
{
|
||||||
|
reader.ReadValueSafe(out m_DataBlock[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FailureTypes.DontReadAnything:
|
||||||
|
{
|
||||||
|
// Don't read anything
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FailureTypes.ThrowReadSideException:
|
||||||
|
{
|
||||||
|
throw new System.Exception("Read side exception!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GenerateData(ushort size)
|
||||||
|
{
|
||||||
|
m_DataSize = size;
|
||||||
|
m_DataBlock = new byte[size];
|
||||||
|
for (int i = 0; i < m_DataSize; i++)
|
||||||
|
{
|
||||||
|
m_DataBlock[i] = (byte)Random.Range(0, 512);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This NetworkVariable is synchronized before OnSynchronize is invoked
|
||||||
|
// which enables us to perform the tests.
|
||||||
|
// Users could follow the same pattern for game assets and synchronize
|
||||||
|
// clients based on NetworkVariable settings. (i.e. a specific NPC type or the like)
|
||||||
|
private NetworkVariable<FailureTypes> m_FailureType;
|
||||||
|
|
||||||
|
public void AssignNextFailureType()
|
||||||
|
{
|
||||||
|
var currentPosition = (int)s_FailureType;
|
||||||
|
currentPosition = (++currentPosition) % NumberOfFailureTypes;
|
||||||
|
s_FailureType = (FailureTypes)currentPosition;
|
||||||
|
m_FailureType.Value = s_FailureType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
m_FailureType = new NetworkVariable<FailureTypes>();
|
||||||
|
m_MyCustomData = new MyCustomData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkSpawn()
|
||||||
|
{
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
ServerSpawnCount++;
|
||||||
|
m_MyCustomData.GenerateData((ushort)Random.Range(1, 512));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ClientSpawnCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnNetworkSpawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
|
||||||
|
{
|
||||||
|
// Assign the failure type first
|
||||||
|
m_MyCustomData.FailureType = m_FailureType.Value;
|
||||||
|
// Now handle the serialization for this failure type
|
||||||
|
m_MyCustomData.NetworkSerialize(serializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NetworkBehaviourOnSynchronizeComponent : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public SomeCustomSerializationData CustomSerializationData = new SomeCustomSerializationData();
|
||||||
|
|
||||||
|
public struct SomeCustomSerializationData : INetworkSerializable
|
||||||
|
{
|
||||||
|
public uint Value1;
|
||||||
|
public bool Value2;
|
||||||
|
public long Value3;
|
||||||
|
public float Value4;
|
||||||
|
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||||
|
{
|
||||||
|
serializer.SerializeValue(ref Value1);
|
||||||
|
serializer.SerializeValue(ref Value2);
|
||||||
|
serializer.SerializeValue(ref Value3);
|
||||||
|
serializer.SerializeValue(ref Value4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkSpawn()
|
||||||
|
{
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
CustomSerializationData.Value1 = (uint)Random.Range(0, 10000);
|
||||||
|
CustomSerializationData.Value2 = true;
|
||||||
|
CustomSerializationData.Value3 = Random.Range(0, 10000);
|
||||||
|
CustomSerializationData.Value4 = Random.Range(-1000.0f, 1000.0f);
|
||||||
|
}
|
||||||
|
base.OnNetworkSpawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
|
||||||
|
{
|
||||||
|
serializer.SerializeNetworkSerializable(ref CustomSerializationData);
|
||||||
|
base.OnSynchronize(ref serializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -186,7 +186,12 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
//Stop, shutdown, and destroy
|
//Stop, shutdown, and destroy
|
||||||
NetworkManagerHelper.ShutdownNetworkManager();
|
NetworkManagerHelper.ShutdownNetworkManager();
|
||||||
|
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
var networkObjects = UnityEngine.Object.FindObjectsByType<NetworkObject>(FindObjectsSortMode.InstanceID).ToList();
|
||||||
|
#else
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>().ToList();
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>().ToList();
|
||||||
|
#endif
|
||||||
|
|
||||||
var networkObjectsList = networkObjects.Where(c => c.name.Contains(k_PrefabObjectName));
|
var networkObjectsList = networkObjects.Where(c => c.name.Contains(k_PrefabObjectName));
|
||||||
foreach (var networkObject in networkObjectsList)
|
foreach (var networkObject in networkObjectsList)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,16 +8,12 @@ using Unity.Netcode.TestHelpers.Runtime;
|
|||||||
|
|
||||||
namespace Unity.Netcode.RuntimeTests
|
namespace Unity.Netcode.RuntimeTests
|
||||||
{
|
{
|
||||||
public class NetworkShowHideTestComponent : NetworkBehaviour
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ShowHideObject : NetworkBehaviour
|
public class ShowHideObject : NetworkBehaviour
|
||||||
{
|
{
|
||||||
public static List<ShowHideObject> ClientTargetedNetworkObjects = new List<ShowHideObject>();
|
public static List<ShowHideObject> ClientTargetedNetworkObjects = new List<ShowHideObject>();
|
||||||
public static ulong ClientIdToTarget;
|
public static ulong ClientIdToTarget;
|
||||||
public static bool Silent;
|
public static bool Silent;
|
||||||
|
public static int ValueAfterOwnershipChange = 0;
|
||||||
|
|
||||||
public static NetworkObject GetNetworkObjectById(ulong networkObjectId)
|
public static NetworkObject GetNetworkObjectById(ulong networkObjectId)
|
||||||
{
|
{
|
||||||
@@ -62,13 +58,35 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
public NetworkVariable<int> MyNetworkVariable;
|
public NetworkVariable<int> MyNetworkVariable;
|
||||||
public NetworkList<int> MyListSetOnSpawn;
|
public NetworkList<int> MyListSetOnSpawn;
|
||||||
|
public NetworkVariable<int> MyOwnerReadNetworkVariable;
|
||||||
|
static public NetworkManager NetworkManagerOfInterest;
|
||||||
|
|
||||||
|
internal static int GainOwnershipCount = 0;
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
|
// Debug.Log($"Awake {NetworkManager.LocalClientId}");
|
||||||
MyNetworkVariable = new NetworkVariable<int>();
|
MyNetworkVariable = new NetworkVariable<int>();
|
||||||
MyNetworkVariable.OnValueChanged += Changed;
|
MyNetworkVariable.OnValueChanged += Changed;
|
||||||
|
|
||||||
MyListSetOnSpawn = new NetworkList<int>();
|
MyListSetOnSpawn = new NetworkList<int>();
|
||||||
|
|
||||||
|
MyOwnerReadNetworkVariable = new NetworkVariable<int>(readPerm: NetworkVariableReadPermission.Owner);
|
||||||
|
MyOwnerReadNetworkVariable.OnValueChanged += OwnerReadChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGainedOwnership()
|
||||||
|
{
|
||||||
|
GainOwnershipCount++;
|
||||||
|
base.OnGainedOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OwnerReadChanged(int before, int after)
|
||||||
|
{
|
||||||
|
if (NetworkManager == NetworkManagerOfInterest)
|
||||||
|
{
|
||||||
|
ValueAfterOwnershipChange = after;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Changed(int before, int after)
|
public void Changed(int before, int after)
|
||||||
@@ -94,11 +112,6 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
private NetworkObject m_Object2OnClient0;
|
private NetworkObject m_Object2OnClient0;
|
||||||
private NetworkObject m_Object3OnClient0;
|
private NetworkObject m_Object3OnClient0;
|
||||||
|
|
||||||
protected override void OnCreatePlayerPrefab()
|
|
||||||
{
|
|
||||||
var networkTransform = m_PlayerPrefab.AddComponent<NetworkShowHideTestComponent>();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnServerAndClientsCreated()
|
protected override void OnServerAndClientsCreated()
|
||||||
{
|
{
|
||||||
m_PrefabToSpawn = CreateNetworkObjectPrefab("ShowHideObject");
|
m_PrefabToSpawn = CreateNetworkObjectPrefab("ShowHideObject");
|
||||||
@@ -338,5 +351,48 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
Assert.True(ShowHideObject.ClientTargetedNetworkObjects[0].OwnerClientId == m_ClientNetworkManagers[0].LocalClientId);
|
Assert.True(ShowHideObject.ClientTargetedNetworkObjects[0].OwnerClientId == m_ClientNetworkManagers[0].LocalClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator NetworkHideChangeOwnershipNotHidden()
|
||||||
|
{
|
||||||
|
ShowHideObject.ClientTargetedNetworkObjects.Clear();
|
||||||
|
ShowHideObject.ClientIdToTarget = m_ClientNetworkManagers[1].LocalClientId;
|
||||||
|
ShowHideObject.Silent = true;
|
||||||
|
|
||||||
|
var spawnedObject1 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
|
||||||
|
m_NetSpawnedObject1 = spawnedObject1.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
|
// wait for host to have spawned and gained ownership
|
||||||
|
while (ShowHideObject.GainOwnershipCount == 0)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the value
|
||||||
|
m_NetSpawnedObject1.GetComponent<ShowHideObject>().MyOwnerReadNetworkVariable.Value++;
|
||||||
|
|
||||||
|
// wait for three ticks
|
||||||
|
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 3);
|
||||||
|
|
||||||
|
// check we'll actually be changing owners
|
||||||
|
Assert.False(ShowHideObject.ClientTargetedNetworkObjects[0].OwnerClientId == m_ClientNetworkManagers[0].LocalClientId);
|
||||||
|
|
||||||
|
// only check for value change on one specific client
|
||||||
|
ShowHideObject.NetworkManagerOfInterest = m_ClientNetworkManagers[0];
|
||||||
|
|
||||||
|
// change ownership
|
||||||
|
m_NetSpawnedObject1.ChangeOwnership(m_ClientNetworkManagers[0].LocalClientId);
|
||||||
|
|
||||||
|
// wait three ticks
|
||||||
|
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 3);
|
||||||
|
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ClientNetworkManagers[0], 3);
|
||||||
|
|
||||||
|
// verify ownership changed
|
||||||
|
Assert.True(ShowHideObject.ClientTargetedNetworkObjects[0].OwnerClientId == m_ClientNetworkManagers[0].LocalClientId);
|
||||||
|
|
||||||
|
// verify the expected client got the OnValueChanged. (Only client 1 sets this value)
|
||||||
|
Assert.True(ShowHideObject.ValueAfterOwnershipChange == 1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -734,7 +734,6 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
Assert.IsTrue(m_DetectedPotentialInterpolatedTeleport == 0.0f, $"Detected possible interpolation on non-authority side! NonAuthority distance: {m_DetectedPotentialInterpolatedTeleport} | Target distance: {targetDistance}");
|
Assert.IsTrue(m_DetectedPotentialInterpolatedTeleport == 0.0f, $"Detected possible interpolation on non-authority side! NonAuthority distance: {m_DetectedPotentialInterpolatedTeleport} | Target distance: {targetDistance}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This test validates the <see cref="NetworkTransform.SetState(Vector3?, Quaternion?, Vector3?, bool)"/> method
|
/// This test validates the <see cref="NetworkTransform.SetState(Vector3?, Quaternion?, Vector3?, bool)"/> method
|
||||||
/// usage for the non-authoritative side. It will either be the owner or the server making/requesting state changes.
|
/// usage for the non-authoritative side. It will either be the owner or the server making/requesting state changes.
|
||||||
@@ -842,7 +841,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
if (!nonauthorityIsEqual)
|
if (!nonauthorityIsEqual)
|
||||||
{
|
{
|
||||||
VerboseDebug($"NonAuthority position {nonAuthorityRotationEuler} != rotation to match: {rotationEulerToMatch}!");
|
VerboseDebug($"NonAuthority rotation {nonAuthorityRotationEuler} != rotation to match: {rotationEulerToMatch}!");
|
||||||
}
|
}
|
||||||
return auhtorityIsEqual && nonauthorityIsEqual;
|
return auhtorityIsEqual && nonauthorityIsEqual;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,12 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
[UnityTest]
|
[UnityTest]
|
||||||
public IEnumerator HiddenObjectsTest()
|
public IEnumerator HiddenObjectsTest()
|
||||||
{
|
{
|
||||||
|
#if UNITY_2023_1_OR_NEWER
|
||||||
|
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsByType<NetworkVisibilityComponent>(FindObjectsSortMode.None).Where((c) => c.IsSpawned).Count() == 2);
|
||||||
|
#else
|
||||||
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == 2);
|
yield return WaitForConditionOrTimeOut(() => Object.FindObjectsOfType<NetworkVisibilityComponent>().Where((c) => c.IsSpawned).Count() == 2);
|
||||||
|
#endif
|
||||||
|
|
||||||
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for the visible object count to equal 2!");
|
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for the visible object count to equal 2!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
This package contains third-party software components governed by the license(s) indicated below:
|
|
||||||
---------
|
|
||||||
|
|
||||||
## Package: Editor/CodeGen/XXHash
|
|
||||||
|
|
||||||
---------
|
|
||||||
|
|
||||||
Component Name: xxHash
|
|
||||||
|
|
||||||
License Type: MIT
|
|
||||||
|
|
||||||
Copyright (c) 2015, 2016 Sedat Kapanoglu
|
|
||||||
|
|
||||||
http://www.xxhash.com/
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 43e3f545298d44899b42e94aa7d17f07
|
|
||||||
TextScriptImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -2,19 +2,20 @@
|
|||||||
"name": "com.unity.netcode.gameobjects",
|
"name": "com.unity.netcode.gameobjects",
|
||||||
"displayName": "Netcode for GameObjects",
|
"displayName": "Netcode for GameObjects",
|
||||||
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
|
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"unity": "2020.3",
|
"unity": "2020.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"com.unity.nuget.mono-cecil": "1.10.1",
|
"com.unity.nuget.mono-cecil": "1.10.1",
|
||||||
"com.unity.transport": "1.3.0"
|
"com.unity.transport": "1.3.0"
|
||||||
},
|
},
|
||||||
"upmCi": {
|
"upmCi": {
|
||||||
"footprint": "4d959c429b2aabd0ba04a6a1a4e1c5e352e6366f"
|
"footprint": "4160b9a4b03a930793225fd4767f2b00fee62c79"
|
||||||
},
|
},
|
||||||
|
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@1.2/manual/index.html",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git",
|
"url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"revision": "2c69184e5f85a025455c415145be3eeecbf98446"
|
"revision": "789f0930ead8e5ba27e123323f407d6f3393aa7b"
|
||||||
},
|
},
|
||||||
"samples": [
|
"samples": [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user