Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
514166e159 | ||
|
|
ffef45b50f | ||
|
|
b3bd4727ab | ||
|
|
0581a42b70 | ||
|
|
4d70c198bd |
116
CHANGELOG.md
116
CHANGELOG.md
@@ -6,6 +6,122 @@ 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.7.1] - 2023-11-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed a bug where having a class with Rpcs that inherits from a class without Rpcs that inherits from NetworkVariable would cause a compile error. (#2751)
|
||||||
|
- Fixed issue where `NetworkBehaviour.Synchronize` was not truncating the write buffer if nothing was serialized during `NetworkBehaviour.OnSynchronize` causing an additional 6 bytes to be written per `NetworkBehaviour` component instance. (#2749)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
## [1.7.0] - 2023-10-11
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- exposed NetworkObject.GetNetworkBehaviourAtOrderIndex as a public API (#2724)
|
||||||
|
- Added context menu tool that provides users with the ability to quickly update the GlobalObjectIdHash value for all in-scene placed prefab instances that were created prior to adding a NetworkObject component to it. (#2707)
|
||||||
|
- Added methods NetworkManager.SetPeerMTU and NetworkManager.GetPeerMTU to be able to set MTU sizes per-peer (#2676)
|
||||||
|
- Added `GenerateSerializationForGenericParameterAttribute`, which can be applied to user-created Network Variable types to ensure the codegen generates serialization for the generic types they wrap. (#2694)
|
||||||
|
- Added `GenerateSerializationForTypeAttribute`, which can be applied to any class or method to ensure the codegen generates serialization for the specific provided type. (#2694)
|
||||||
|
- Exposed `NetworkVariableSerialization<T>.Read`, `NetworkVariableSerialization<T>.Write`, `NetworkVariableSerialization<T>.AreEqual`, and `NetworkVariableSerialization<T>.Duplicate` to further support the creation of user-created network variables by allowing users to access the generated serialization methods and serialize generic types efficiently without boxing. (#2694)
|
||||||
|
- Added `NetworkVariableBase.MarkNetworkBehaviourDirty` so that user-created network variable types can mark their containing `NetworkBehaviour` to be processed by the update loop. (#2694)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed issue where the server side `NetworkSceneManager` instance was not adding the currently active scene to its list of scenes loaded. (#2723)
|
||||||
|
- Generic NetworkBehaviour types no longer result in compile errors or runtime errors (#2720)
|
||||||
|
- Rpcs within Generic NetworkBehaviour types can now serialize parameters of the class's generic types (but may not have generic types of their own) (#2720)
|
||||||
|
- Errors are no longer thrown when entering play mode with domain reload disabled (#2720)
|
||||||
|
- NetworkSpawn is now correctly called each time when entering play mode with scene reload disabled (#2720)
|
||||||
|
- NetworkVariables of non-integer types will no longer break the inspector (#2714)
|
||||||
|
- NetworkVariables with NonSerializedAttribute will not appear in the inspector (#2714)
|
||||||
|
- Fixed issue where `UnityTransport` would attempt to establish WebSocket connections even if using UDP/DTLS Relay allocations when the build target was WebGL. This only applied to working in the editor since UDP/DTLS can't work in the browser. (#2695)
|
||||||
|
- Fixed issue where a `NetworkBehaviour` component's `OnNetworkDespawn` was not being invoked on the host-server side for an in-scene placed `NetworkObject` when a scene was unloaded (during a scene transition) and the `NetworkBehaviour` component was positioned/ordered before the `NetworkObject` component. (#2685)
|
||||||
|
- Fixed issue where `SpawnWithObservers` was not being honored when `NetworkConfig.EnableSceneManagement` was disabled. (#2682)
|
||||||
|
- Fixed issue where `NetworkAnimator` was not internally tracking changes to layer weights which prevented proper layer weight synchronization back to the original layer weight value. (#2674)
|
||||||
|
- Fixed "writing past the end of the buffer" error when calling ResetDirty() on managed network variables that are larger than 256 bytes when serialized. (#2670)
|
||||||
|
- Fixed issue where generation of the `DefaultNetworkPrefabs` asset was not enabled by default. (#2662)
|
||||||
|
- Fixed issue where the `GlobalObjectIdHash` value could be updated but the asset not marked as dirty. (#2662)
|
||||||
|
- Fixed issue where the `GlobalObjectIdHash` value of a (network) prefab asset could be assigned an incorrect value when editing the prefab in a temporary scene. (#2662)
|
||||||
|
- Fixed issue where the `GlobalObjectIdHash` value generated after creating a (network) prefab from an object constructed within the scene would not be the correct final value in a stand alone build. (#2662)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated dependency on `com.unity.transport` to version 1.4.0. (#2716)
|
||||||
|
|
||||||
|
## [1.6.0] - 2023-08-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added a protected virtual method `NetworkTransform.OnInitialize(ref NetworkTransformState replicatedState)` that just returns the replicated state reference.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed issue where invoking `NetworkManager.Shutdown` within `NetworkManager.OnClientStopped` or `NetworkManager.OnServerStopped` would force `NetworkManager.ShutdownInProgress` to remain true after completing the shutdown process. (#2661)
|
||||||
|
- Fixed issue where ARMv7 Android builds would crash when trying to validate the batch header. (#2654)
|
||||||
|
- Fixed issue with client synchronization of position when using half precision and the delta position reaches the maximum value and is collapsed on the host prior to being forwarded to the non-owner clients. (#2636)
|
||||||
|
- Fixed issue with scale not synchronizing properly depending upon the spawn order of NetworkObjects. (#2636)
|
||||||
|
- Fixed issue position was not properly transitioning between ownership changes with an owner authoritative NetworkTransform. (#2636)
|
||||||
|
- Fixed issue where a late joining non-owner client could update an owner authoritative NetworkTransform if ownership changed without any updates to position prior to the non-owner client joining. (#2636)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
## [1.5.2] - 2023-07-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed issue where `NetworkClient.OwnedObjects` was not returning any owned objects due to the `NetworkClient.IsConnected` not being properly set. (#2631)
|
||||||
|
- Fixed a crash when calling TrySetParent with a null Transform (#2625)
|
||||||
|
- Fixed issue where a `NetworkTransform` using full precision state updates was losing transform state updates when interpolation was enabled. (#2624)
|
||||||
|
- Fixed issue where `NetworkObject.SpawnWithObservers` was not being honored for late joining clients. (#2623)
|
||||||
|
- Fixed issue where invoking `NetworkManager.Shutdown` multiple times, depending upon the timing, could cause an exception. (#2622)
|
||||||
|
- Fixed issue where removing ownership would not notify the server that it gained ownership. This also resolves the issue where an owner authoritative NetworkTransform would not properly initialize upon removing ownership from a remote client. (#2618)
|
||||||
|
- Fixed ILPP issues when using CoreCLR and for certain dedicated server builds. (#2614)
|
||||||
|
- Fixed an ILPP compile error when creating a generic NetworkBehaviour singleton with a static T instance. (#2603)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
## [1.5.1] - 2023-06-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added support for serializing `NativeArray<>` and `NativeList<>` in `FastBufferReader`/`FastBufferWriter`, `BufferSerializer`, `NetworkVariable`, and RPCs. (To use `NativeList<>`, add `UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT` to your Scripting Define Symbols in `Project Settings > Player`) (#2375)
|
||||||
|
- The location of the automatically-created default network prefab list can now be configured (#2544)
|
||||||
|
- Added: Message size limits (max single message and max fragmented message) can now be set using NetworkManager.MaximumTransmissionUnitSize and NetworkManager.MaximumFragmentedMessageSize for transports that don't work with the default values (#2530)
|
||||||
|
- Added `NetworkObject.SpawnWithObservers` property (default is true) that when set to false will spawn a `NetworkObject` with no observers and will not be spawned on any client until `NetworkObject.NetworkShow` is invoked. (#2568)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed: Fixed a null reference in codegen in some projects (#2581)
|
||||||
|
- Fixed issue where the `OnClientDisconnected` client identifier was incorrect after a pending client connection was denied. (#2569)
|
||||||
|
- Fixed warning "Runtime Network Prefabs was not empty at initialization time." being erroneously logged when no runtime network prefabs had been added (#2565)
|
||||||
|
- Fixed issue where some temporary debug console logging was left in a merged PR. (#2562)
|
||||||
|
- Fixed the "Generate Default Network Prefabs List" setting not loading correctly and always reverting to being checked. (#2545)
|
||||||
|
- Fixed issue where users could not use NetworkSceneManager.VerifySceneBeforeLoading to exclude runtime generated scenes from client synchronization. (#2550)
|
||||||
|
- Fixed missing value on `NetworkListEvent` for `EventType.RemoveAt` events. (#2542,#2543)
|
||||||
|
- Fixed issue where parenting a NetworkTransform under a transform with a scale other than Vector3.one would result in incorrect values on non-authoritative instances. (#2538)
|
||||||
|
- Fixed issue where a server would include scene migrated and then despawned NetworkObjects to a client that was being synchronized. (#2532)
|
||||||
|
- Fixed the inspector throwing exceptions when attempting to render `NetworkVariable`s of enum types. (#2529)
|
||||||
|
- Making a `NetworkVariable` with an `INetworkSerializable` type that doesn't meet the `new()` constraint will now create a compile-time error instead of an editor crash (#2528)
|
||||||
|
- Fixed Multiplayer Tools package installation docs page link on the NetworkManager popup. (#2526)
|
||||||
|
- Fixed an exception and error logging when two different objects are shown and hidden on the same frame (#2524)
|
||||||
|
- Fixed a memory leak in `UnityTransport` that occurred if `StartClient` failed. (#2518)
|
||||||
|
- Fixed issue where a client could throw an exception if abruptly disconnected from a network session with one or more spawned `NetworkObject`(s). (#2510)
|
||||||
|
- Fixed issue where invalid endpoint addresses were not being detected and returning false from NGO UnityTransport. (#2496)
|
||||||
|
- Fixed some errors that could occur if a connection is lost and the loss is detected when attempting to write to the socket. (#2495)
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Adding network prefabs before NetworkManager initialization is now supported. (#2565)
|
||||||
|
- Connecting clients being synchronized now switch to the server's active scene before spawning and synchronizing NetworkObjects. (#2532)
|
||||||
|
- Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.4. (#2533)
|
||||||
|
- Improved performance of NetworkBehaviour initialization by replacing reflection when initializing NetworkVariables with compile-time code generation, which should help reduce hitching during additive scene loads. (#2522)
|
||||||
|
|
||||||
## [1.4.0] - 2023-04-10
|
## [1.4.0] - 2023-04-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Unity.Netcode.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The half float precision value of the z-axis as a <see cref="half"/>.
|
/// The half float precision value of the z-axis as a <see cref="half"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public half Z => Axis.x;
|
public half Z => Axis.z;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to store the half float precision values as a <see cref="half3"/>
|
/// Used to store the half float precision values as a <see cref="half3"/>
|
||||||
@@ -39,6 +39,17 @@ namespace Unity.Netcode.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool3 AxisToSynchronize;
|
public bool3 AxisToSynchronize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Directly sets each axial value to the passed in full precision values
|
||||||
|
/// that are converted to half precision
|
||||||
|
/// </summary>
|
||||||
|
internal void Set(float x, float y, float z)
|
||||||
|
{
|
||||||
|
Axis.x = math.half(x);
|
||||||
|
Axis.y = math.half(y);
|
||||||
|
Axis.z = math.half(z);
|
||||||
|
}
|
||||||
|
|
||||||
private void SerializeWrite(FastBufferWriter writer)
|
private void SerializeWrite(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Length; i++)
|
for (int i = 0; i < Length; i++)
|
||||||
|
|||||||
@@ -1137,6 +1137,7 @@ namespace Unity.Netcode.Components
|
|||||||
if (m_LayerWeights[animationState.Layer] != animationState.Weight)
|
if (m_LayerWeights[animationState.Layer] != animationState.Weight)
|
||||||
{
|
{
|
||||||
m_Animator.SetLayerWeight(animationState.Layer, animationState.Weight);
|
m_Animator.SetLayerWeight(animationState.Layer, animationState.Weight);
|
||||||
|
m_LayerWeights[animationState.Layer] = animationState.Weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Unity.Collections;
|
||||||
using Unity.Mathematics;
|
using Unity.Mathematics;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -49,6 +50,11 @@ namespace Unity.Netcode.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public OnClientRequestChangeDelegate OnClientRequestChange;
|
public OnClientRequestChangeDelegate OnClientRequestChange;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When set each state update will contain a state identifier
|
||||||
|
/// </summary>
|
||||||
|
internal static bool TrackByStateId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data structure used to synchronize the <see cref="NetworkTransform"/>
|
/// Data structure used to synchronize the <see cref="NetworkTransform"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -71,9 +77,16 @@ namespace Unity.Netcode.Components
|
|||||||
private const int k_UseHalfFloats = 0x00004000; // Persists between state updates (authority dictates if this is set)
|
private const int k_UseHalfFloats = 0x00004000; // Persists between state updates (authority dictates if this is set)
|
||||||
private const int k_Synchronization = 0x00008000;
|
private const int k_Synchronization = 0x00008000;
|
||||||
private const int k_PositionSlerp = 0x00010000; // Persists between state updates (authority dictates if this is set)
|
private const int k_PositionSlerp = 0x00010000; // Persists between state updates (authority dictates if this is set)
|
||||||
|
private const int k_IsParented = 0x00020000; // When parented and synchronizing, we need to have both lossy and local scale due to varying spawn order
|
||||||
|
private const int k_TrackStateId = 0x10000000; // (Internal Debugging) When set each state update will contain a state identifier
|
||||||
|
|
||||||
// Stores persistent and state relative flags
|
// Stores persistent and state relative flags
|
||||||
private uint m_Bitset;
|
private uint m_Bitset;
|
||||||
|
internal uint BitSet
|
||||||
|
{
|
||||||
|
get { return m_Bitset; }
|
||||||
|
set { m_Bitset = value; }
|
||||||
|
}
|
||||||
|
|
||||||
// Used to store the tick calculated sent time
|
// Used to store the tick calculated sent time
|
||||||
internal double SentTime;
|
internal double SentTime;
|
||||||
@@ -98,6 +111,7 @@ namespace Unity.Netcode.Components
|
|||||||
// Used for half precision scale
|
// Used for half precision scale
|
||||||
internal HalfVector3 HalfVectorScale;
|
internal HalfVector3 HalfVectorScale;
|
||||||
internal Vector3 Scale;
|
internal Vector3 Scale;
|
||||||
|
internal Vector3 LossyScale;
|
||||||
|
|
||||||
// Used for half precision quaternion
|
// Used for half precision quaternion
|
||||||
internal HalfVector4 HalfVectorRotation;
|
internal HalfVector4 HalfVectorRotation;
|
||||||
@@ -118,7 +132,6 @@ namespace Unity.Netcode.Components
|
|||||||
internal int NetworkTick;
|
internal int NetworkTick;
|
||||||
|
|
||||||
// Used when tracking by state ID is enabled
|
// Used when tracking by state ID is enabled
|
||||||
internal bool TrackByStateId;
|
|
||||||
internal int StateId;
|
internal int StateId;
|
||||||
|
|
||||||
// Used during serialization
|
// Used during serialization
|
||||||
@@ -416,6 +429,24 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal bool IsParented
|
||||||
|
{
|
||||||
|
get => GetFlag(k_IsParented);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetFlag(value, k_IsParented);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool TrackByStateId
|
||||||
|
{
|
||||||
|
get => GetFlag(k_TrackStateId);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetFlag(value, k_TrackStateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private bool GetFlag(int flag)
|
private bool GetFlag(int flag)
|
||||||
{
|
{
|
||||||
@@ -468,6 +499,10 @@ namespace Unity.Netcode.Components
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// When there is no change in an updated state's position then there are no values to return.
|
/// When there is no change in an updated state's position then there are no values to return.
|
||||||
/// Checking for <see cref="HasPositionChange"/> is one way to detect this.
|
/// Checking for <see cref="HasPositionChange"/> is one way to detect this.
|
||||||
|
/// When used with half precision it returns the half precision delta position state update
|
||||||
|
/// which will not be the full position.
|
||||||
|
/// To get a NettworkTransform's full position, use <see cref="GetSpaceRelativePosition(bool)"/> and
|
||||||
|
/// pass true as the parameter.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns><see cref="Vector3"/></returns>
|
/// <returns><see cref="Vector3"/></returns>
|
||||||
public Vector3 GetPosition()
|
public Vector3 GetPosition()
|
||||||
@@ -534,6 +569,8 @@ namespace Unity.Netcode.Components
|
|||||||
return NetworkTick;
|
return NetworkTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal HalfVector3 HalfEulerRotation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializes this <see cref="NetworkTransformState"/>
|
/// Serializes this <see cref="NetworkTransformState"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -553,23 +590,6 @@ namespace Unity.Netcode.Components
|
|||||||
positionStart = m_Reader.Position;
|
positionStart = m_Reader.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TrackByStateId)
|
|
||||||
{
|
|
||||||
var stateId = StateId;
|
|
||||||
if (IsSynchronizing)
|
|
||||||
{
|
|
||||||
StateId = -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (serializer.IsWriter)
|
|
||||||
{
|
|
||||||
StateId++;
|
|
||||||
}
|
|
||||||
serializer.SerializeValue(ref StateId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synchronize State Flags and Network Tick
|
// Synchronize State Flags and Network Tick
|
||||||
{
|
{
|
||||||
if (isWriting)
|
if (isWriting)
|
||||||
@@ -589,11 +609,22 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If debugging states and track by state identifier is enabled, serialize the current state identifier
|
||||||
|
if (TrackByStateId)
|
||||||
|
{
|
||||||
|
serializer.SerializeValue(ref StateId);
|
||||||
|
}
|
||||||
|
|
||||||
// Synchronize Position
|
// Synchronize Position
|
||||||
if (HasPositionChange)
|
if (HasPositionChange)
|
||||||
{
|
{
|
||||||
if (UseHalfFloatPrecision)
|
if (UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
|
// Apply which axis should be updated for both write/read (teleporting, synchronizing, or just updating)
|
||||||
|
NetworkDeltaPosition.HalfVector3.AxisToSynchronize[0] = HasPositionX;
|
||||||
|
NetworkDeltaPosition.HalfVector3.AxisToSynchronize[1] = HasPositionY;
|
||||||
|
NetworkDeltaPosition.HalfVector3.AxisToSynchronize[2] = HasPositionZ;
|
||||||
|
|
||||||
if (IsTeleportingNextFrame)
|
if (IsTeleportingNextFrame)
|
||||||
{
|
{
|
||||||
// **Always use full precision when teleporting and UseHalfFloatPrecision is enabled**
|
// **Always use full precision when teleporting and UseHalfFloatPrecision is enabled**
|
||||||
@@ -604,7 +635,7 @@ namespace Unity.Netcode.Components
|
|||||||
serializer.SerializeValue(ref DeltaPosition);
|
serializer.SerializeValue(ref DeltaPosition);
|
||||||
if (!isWriting)
|
if (!isWriting)
|
||||||
{
|
{
|
||||||
NetworkDeltaPosition = new NetworkDeltaPosition(Vector3.zero, 0, math.bool3(HasPositionX, HasPositionY, HasPositionZ));
|
NetworkDeltaPosition.NetworkTick = NetworkTick;
|
||||||
NetworkDeltaPosition.NetworkSerialize(serializer);
|
NetworkDeltaPosition.NetworkSerialize(serializer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -617,7 +648,7 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
if (!isWriting)
|
if (!isWriting)
|
||||||
{
|
{
|
||||||
NetworkDeltaPosition = new NetworkDeltaPosition(Vector3.zero, 0, math.bool3(HasPositionX, HasPositionY, HasPositionZ));
|
NetworkDeltaPosition.NetworkTick = NetworkTick;
|
||||||
NetworkDeltaPosition.NetworkSerialize(serializer);
|
NetworkDeltaPosition.NetworkSerialize(serializer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -626,9 +657,8 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Legacy Position Synchronization
|
else // Full precision axis specific position synchronization
|
||||||
{
|
{
|
||||||
// Position Values
|
|
||||||
if (HasPositionX)
|
if (HasPositionX)
|
||||||
{
|
{
|
||||||
serializer.SerializeValue(ref PositionX);
|
serializer.SerializeValue(ref PositionX);
|
||||||
@@ -703,11 +733,21 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
if (HasRotAngleChange)
|
if (HasRotAngleChange)
|
||||||
{
|
{
|
||||||
var halfPrecisionRotation = new HalfVector3(RotAngleX, RotAngleY, RotAngleZ, math.bool3(HasRotAngleX, HasRotAngleY, HasRotAngleZ));
|
// Apply which axis should be updated for both write/read
|
||||||
serializer.SerializeValue(ref halfPrecisionRotation);
|
HalfEulerRotation.AxisToSynchronize[0] = HasRotAngleX;
|
||||||
|
HalfEulerRotation.AxisToSynchronize[1] = HasRotAngleY;
|
||||||
|
HalfEulerRotation.AxisToSynchronize[2] = HasRotAngleZ;
|
||||||
|
|
||||||
|
if (isWriting)
|
||||||
|
{
|
||||||
|
HalfEulerRotation.Set(RotAngleX, RotAngleY, RotAngleZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializer.SerializeValue(ref HalfEulerRotation);
|
||||||
|
|
||||||
if (!isWriting)
|
if (!isWriting)
|
||||||
{
|
{
|
||||||
var eulerRotation = halfPrecisionRotation.ToVector3();
|
var eulerRotation = HalfEulerRotation.ToVector3();
|
||||||
if (HasRotAngleX)
|
if (HasRotAngleX)
|
||||||
{
|
{
|
||||||
RotAngleX = eulerRotation.x;
|
RotAngleX = eulerRotation.x;
|
||||||
@@ -749,6 +789,12 @@ namespace Unity.Netcode.Components
|
|||||||
// Synchronize Scale
|
// Synchronize Scale
|
||||||
if (HasScaleChange)
|
if (HasScaleChange)
|
||||||
{
|
{
|
||||||
|
// If we are teleporting (which includes synchronizing) and the associated NetworkObject has a parent
|
||||||
|
// then we want to serialize the LossyScale since NetworkObject spawn order is not guaranteed
|
||||||
|
if (IsTeleportingNextFrame && IsParented)
|
||||||
|
{
|
||||||
|
serializer.SerializeValue(ref LossyScale);
|
||||||
|
}
|
||||||
// Half precision scale synchronization
|
// Half precision scale synchronization
|
||||||
if (UseHalfFloatPrecision)
|
if (UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
@@ -758,9 +804,19 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Apply which axis should be updated for both write/read
|
||||||
|
HalfVectorScale.AxisToSynchronize[0] = HasScaleX;
|
||||||
|
HalfVectorScale.AxisToSynchronize[1] = HasScaleY;
|
||||||
|
HalfVectorScale.AxisToSynchronize[2] = HasScaleZ;
|
||||||
|
|
||||||
// For scale, when half precision is enabled we can still only send the axis with deltas
|
// For scale, when half precision is enabled we can still only send the axis with deltas
|
||||||
HalfVectorScale = new HalfVector3(Scale, math.bool3(HasScaleX, HasScaleY, HasScaleZ));
|
if (isWriting)
|
||||||
|
{
|
||||||
|
HalfVectorScale.Set(Scale[0], Scale[1], Scale[2]);
|
||||||
|
}
|
||||||
|
|
||||||
serializer.SerializeValue(ref HalfVectorScale);
|
serializer.SerializeValue(ref HalfVectorScale);
|
||||||
|
|
||||||
if (!isWriting)
|
if (!isWriting)
|
||||||
{
|
{
|
||||||
Scale = HalfVectorScale.ToVector3();
|
Scale = HalfVectorScale.ToVector3();
|
||||||
@@ -1029,26 +1085,6 @@ namespace Unity.Netcode.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected NetworkManager m_CachedNetworkManager; // Note: we no longer use this and are only keeping it until we decide to deprecate it
|
protected NetworkManager m_CachedNetworkManager; // Note: we no longer use this and are only keeping it until we decide to deprecate it
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// We have two internal NetworkVariables.
|
|
||||||
/// One for server authoritative and one for "client/owner" authoritative.
|
|
||||||
/// </summary>
|
|
||||||
private readonly NetworkVariable<NetworkTransformState> m_ReplicatedNetworkStateServer = new NetworkVariable<NetworkTransformState>(new NetworkTransformState(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
|
|
||||||
private readonly NetworkVariable<NetworkTransformState> m_ReplicatedNetworkStateOwner = new NetworkVariable<NetworkTransformState>(new NetworkTransformState(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
|
||||||
|
|
||||||
internal NetworkVariable<NetworkTransformState> ReplicatedNetworkState
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!IsServerAuthoritative())
|
|
||||||
{
|
|
||||||
return m_ReplicatedNetworkStateOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_ReplicatedNetworkStateServer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper method that returns the space relative position of the transform.
|
/// Helper method that returns the space relative position of the transform.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1078,9 +1114,18 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// When half float precision is enabled, get the NetworkDeltaPosition's full position
|
||||||
|
if (UseHalfFloatPrecision)
|
||||||
|
{
|
||||||
|
return m_HalfPositionState.GetFullPosition();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, just get the current position
|
||||||
return m_CurrentPosition;
|
return m_CurrentPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper method that returns the space relative rotation of the transform.
|
/// Helper method that returns the space relative rotation of the transform.
|
||||||
@@ -1149,6 +1194,8 @@ namespace Unity.Netcode.Components
|
|||||||
// This represents the most recent local authoritative state.
|
// This represents the most recent local authoritative state.
|
||||||
private NetworkTransformState m_LocalAuthoritativeNetworkState;
|
private NetworkTransformState m_LocalAuthoritativeNetworkState;
|
||||||
|
|
||||||
|
internal NetworkTransformState LocalAuthoritativeNetworkState => m_LocalAuthoritativeNetworkState;
|
||||||
|
|
||||||
private ClientRpcParams m_ClientRpcParams = new ClientRpcParams() { Send = new ClientRpcSendParams() };
|
private ClientRpcParams m_ClientRpcParams = new ClientRpcParams() { Send = new ClientRpcSendParams() };
|
||||||
private List<ulong> m_ClientIds = new List<ulong>() { 0 };
|
private List<ulong> m_ClientIds = new List<ulong>() { 0 };
|
||||||
|
|
||||||
@@ -1159,8 +1206,14 @@ namespace Unity.Netcode.Components
|
|||||||
// Non-Authoritative's current position, scale, and rotation that is used to assure the non-authoritative side cannot make adjustments to
|
// Non-Authoritative's current position, scale, and rotation that is used to assure the non-authoritative side cannot make adjustments to
|
||||||
// the portions of the transform being synchronized.
|
// the portions of the transform being synchronized.
|
||||||
private Vector3 m_CurrentPosition;
|
private Vector3 m_CurrentPosition;
|
||||||
|
private Vector3 m_TargetPosition;
|
||||||
private Vector3 m_CurrentScale;
|
private Vector3 m_CurrentScale;
|
||||||
|
private Vector3 m_TargetScale;
|
||||||
private Quaternion m_CurrentRotation;
|
private Quaternion m_CurrentRotation;
|
||||||
|
private Vector3 m_TargetRotation;
|
||||||
|
|
||||||
|
// Used to for each instance to uniquely identify the named message
|
||||||
|
private string m_MessageName;
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -1255,7 +1308,13 @@ namespace Unity.Netcode.Components
|
|||||||
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
|
protected override void OnSynchronize<T>(ref BufferSerializer<T> serializer)
|
||||||
{
|
{
|
||||||
var targetClientId = m_TargetIdBeingSynchronized;
|
var targetClientId = m_TargetIdBeingSynchronized;
|
||||||
var synchronizationState = new NetworkTransformState();
|
var synchronizationState = new NetworkTransformState()
|
||||||
|
{
|
||||||
|
HalfEulerRotation = new HalfVector3(),
|
||||||
|
HalfVectorRotation = new HalfVector4(),
|
||||||
|
HalfVectorScale = new HalfVector3(),
|
||||||
|
NetworkDeltaPosition = new NetworkDeltaPosition(),
|
||||||
|
};
|
||||||
|
|
||||||
if (serializer.IsWriter)
|
if (serializer.IsWriter)
|
||||||
{
|
{
|
||||||
@@ -1283,6 +1342,7 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
m_LocalAuthoritativeNetworkState = synchronizationState;
|
m_LocalAuthoritativeNetworkState = synchronizationState;
|
||||||
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
|
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
|
||||||
|
m_LocalAuthoritativeNetworkState.IsSynchronizing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1346,6 +1406,8 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tracks the last tick a state update was sent (see further below)
|
||||||
|
private int m_LastTick;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Authoritative side only
|
/// Authoritative side only
|
||||||
/// If there are any transform delta states, this method will synchronize the
|
/// If there are any transform delta states, this method will synchronize the
|
||||||
@@ -1363,12 +1425,28 @@ namespace Unity.Netcode.Components
|
|||||||
// If the transform has deltas (returns dirty) then...
|
// If the transform has deltas (returns dirty) then...
|
||||||
if (ApplyTransformToNetworkStateWithInfo(ref m_LocalAuthoritativeNetworkState, ref transformToCommit, synchronize))
|
if (ApplyTransformToNetworkStateWithInfo(ref m_LocalAuthoritativeNetworkState, ref transformToCommit, synchronize))
|
||||||
{
|
{
|
||||||
m_LocalAuthoritativeNetworkState.LastSerializedSize = ReplicatedNetworkState.Value.LastSerializedSize;
|
m_LocalAuthoritativeNetworkState.LastSerializedSize = m_OldState.LastSerializedSize;
|
||||||
|
|
||||||
|
// Make sure our network tick is incremented
|
||||||
|
if (m_LastTick == m_LocalAuthoritativeNetworkState.NetworkTick && !m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame)
|
||||||
|
{
|
||||||
|
// When running in authority and a remote client is the owner, the client can hit a perfect window of time where
|
||||||
|
// it is still on the previous network tick (as a count) but still have had the tick event triggered.
|
||||||
|
// (This is cheaper than calculating the exact tick each time and only can occur on clients)
|
||||||
|
if (!IsServer)
|
||||||
|
{
|
||||||
|
m_LocalAuthoritativeNetworkState.NetworkTick = m_LocalAuthoritativeNetworkState.NetworkTick + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"[NT TICK DUPLICATE] Server already sent an update on tick {m_LastTick} and is attempting to send again on the same network tick!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_LastTick = m_LocalAuthoritativeNetworkState.NetworkTick;
|
||||||
|
// Update the state
|
||||||
|
UpdateTransformState();
|
||||||
|
|
||||||
OnAuthorityPushTransformState(ref m_LocalAuthoritativeNetworkState);
|
OnAuthorityPushTransformState(ref m_LocalAuthoritativeNetworkState);
|
||||||
|
|
||||||
// "push"/commit the state
|
|
||||||
ReplicatedNetworkState.Value = m_LocalAuthoritativeNetworkState;
|
|
||||||
|
|
||||||
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
|
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1648,7 +1726,43 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only if we are not synchronizing...
|
// For scale, we need to check for parenting when synchronizing and/or teleporting
|
||||||
|
if (isSynchronization || networkState.IsTeleportingNextFrame)
|
||||||
|
{
|
||||||
|
// This all has to do with complex nested hierarchies and how it impacts scale
|
||||||
|
// when set for the first time and depending upon whether the NetworkObject is parented
|
||||||
|
// (or not parented) at the time the scale values are applied.
|
||||||
|
var hasParentNetworkObject = false;
|
||||||
|
|
||||||
|
// If the NetworkObject belonging to this NetworkTransform instance has a parent
|
||||||
|
// (i.e. this handles nested NetworkTransforms under a parent at some layer above)
|
||||||
|
if (NetworkObject.transform.parent != null)
|
||||||
|
{
|
||||||
|
var parentNetworkObject = NetworkObject.transform.parent.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
|
// In-scene placed NetworkObjects parented under a GameObject with no
|
||||||
|
// NetworkObject preserve their lossyScale when synchronizing.
|
||||||
|
if (parentNetworkObject == null && NetworkObject.IsSceneObject != false)
|
||||||
|
{
|
||||||
|
hasParentNetworkObject = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Or if the relative NetworkObject has a parent NetworkObject
|
||||||
|
hasParentNetworkObject = parentNetworkObject != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
networkState.IsParented = hasParentNetworkObject;
|
||||||
|
// If we are synchronizing and the associated NetworkObject has a parent then we want to send the
|
||||||
|
// LossyScale if the NetworkObject has a parent since NetworkObject spawn order is not guaranteed
|
||||||
|
if (hasParentNetworkObject)
|
||||||
|
{
|
||||||
|
networkState.LossyScale = transform.lossyScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking scale deltas when not synchronizing
|
||||||
if (!isSynchronization)
|
if (!isSynchronization)
|
||||||
{
|
{
|
||||||
if (!UseHalfFloatPrecision)
|
if (!UseHalfFloatPrecision)
|
||||||
@@ -1679,7 +1793,7 @@ namespace Unity.Netcode.Components
|
|||||||
var previousScale = networkState.Scale;
|
var previousScale = networkState.Scale;
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
if (Mathf.Abs(Mathf.DeltaAngle(previousScale[i], scale[i])) >= ScaleThreshold || networkState.IsTeleportingNextFrame)
|
if (Mathf.Abs(scale[i] - previousScale[i]) >= ScaleThreshold || networkState.IsTeleportingNextFrame)
|
||||||
{
|
{
|
||||||
isScaleDirty = true;
|
isScaleDirty = true;
|
||||||
networkState.Scale[i] = scale[i];
|
networkState.Scale[i] = scale[i];
|
||||||
@@ -1688,46 +1802,18 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // If we are synchronizing then we need to determine which scale to use
|
else // Just apply the full local scale when synchronizing
|
||||||
if (SynchronizeScale)
|
if (SynchronizeScale)
|
||||||
{
|
{
|
||||||
// This all has to do with complex nested hierarchies and how it impacts scale
|
|
||||||
// when set for the first time.
|
|
||||||
var hasParentNetworkObject = false;
|
|
||||||
|
|
||||||
// If the NetworkObject belonging to this NetworkTransform instance has a parent
|
|
||||||
// (i.e. this handles nested NetworkTransforms under a parent at some layer above)
|
|
||||||
if (NetworkObject.transform.parent != null)
|
|
||||||
{
|
|
||||||
var parentNetworkObject = NetworkObject.transform.parent.GetComponent<NetworkObject>();
|
|
||||||
|
|
||||||
// In-scene placed NetworkObjects parented under a GameObject with no
|
|
||||||
// NetworkObject preserve their lossyScale when synchronizing.
|
|
||||||
if (parentNetworkObject == null && NetworkObject.IsSceneObject != false)
|
|
||||||
{
|
|
||||||
hasParentNetworkObject = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Or if the relative NetworkObject has a parent NetworkObject
|
|
||||||
hasParentNetworkObject = parentNetworkObject != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If world position stays is set and the relative NetworkObject is parented under a NetworkObject
|
|
||||||
// then we want to use the lossy scale for the initial synchronization.
|
|
||||||
var useLossy = NetworkObject.WorldPositionStays() && hasParentNetworkObject;
|
|
||||||
var scaleToUse = useLossy ? transform.lossyScale : transform.localScale;
|
|
||||||
|
|
||||||
if (!UseHalfFloatPrecision)
|
if (!UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
networkState.ScaleX = scaleToUse.x;
|
networkState.ScaleX = transform.localScale.x;
|
||||||
networkState.ScaleY = scaleToUse.y;
|
networkState.ScaleY = transform.localScale.y;
|
||||||
networkState.ScaleZ = scaleToUse.z;
|
networkState.ScaleZ = transform.localScale.z;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
networkState.Scale = scaleToUse;
|
networkState.Scale = transform.localScale;
|
||||||
}
|
}
|
||||||
networkState.HasScaleX = true;
|
networkState.HasScaleX = true;
|
||||||
networkState.HasScaleY = true;
|
networkState.HasScaleY = true;
|
||||||
@@ -2009,6 +2095,7 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_CurrentPosition = currentPosition;
|
m_CurrentPosition = currentPosition;
|
||||||
|
m_TargetPosition = currentPosition;
|
||||||
|
|
||||||
// Apply the position
|
// Apply the position
|
||||||
if (newState.InLocalSpace)
|
if (newState.InLocalSpace)
|
||||||
@@ -2023,32 +2110,45 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
if (newState.HasScaleChange)
|
if (newState.HasScaleChange)
|
||||||
{
|
{
|
||||||
|
bool shouldUseLossy = false;
|
||||||
|
if (newState.IsParented)
|
||||||
|
{
|
||||||
|
if (transform.parent == null)
|
||||||
|
{
|
||||||
|
shouldUseLossy = NetworkObject.WorldPositionStays();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shouldUseLossy = !NetworkObject.WorldPositionStays();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (UseHalfFloatPrecision)
|
if (UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
currentScale = newState.Scale;
|
currentScale = shouldUseLossy ? newState.LossyScale : newState.Scale;
|
||||||
m_CurrentScale = currentScale;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Adjust based on which axis changed
|
// Adjust based on which axis changed
|
||||||
if (newState.HasScaleX)
|
if (newState.HasScaleX)
|
||||||
{
|
{
|
||||||
currentScale.x = newState.ScaleX;
|
currentScale.x = shouldUseLossy ? newState.LossyScale.x : newState.ScaleX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newState.HasScaleY)
|
if (newState.HasScaleY)
|
||||||
{
|
{
|
||||||
currentScale.y = newState.ScaleY;
|
currentScale.y = shouldUseLossy ? newState.LossyScale.y : newState.ScaleY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newState.HasScaleZ)
|
if (newState.HasScaleZ)
|
||||||
{
|
{
|
||||||
currentScale.z = newState.ScaleZ;
|
currentScale.z = shouldUseLossy ? newState.LossyScale.z : newState.ScaleZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_CurrentScale = currentScale;
|
m_CurrentScale = currentScale;
|
||||||
|
m_TargetScale = currentScale;
|
||||||
m_ScaleInterpolator.ResetTo(currentScale, sentTime);
|
m_ScaleInterpolator.ResetTo(currentScale, sentTime);
|
||||||
|
|
||||||
// Apply the adjusted scale
|
// Apply the adjusted scale
|
||||||
@@ -2082,6 +2182,7 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_CurrentRotation = currentRotation;
|
m_CurrentRotation = currentRotation;
|
||||||
|
m_TargetRotation = currentRotation.eulerAngles;
|
||||||
m_RotationInterpolator.ResetTo(currentRotation, sentTime);
|
m_RotationInterpolator.ResetTo(currentRotation, sentTime);
|
||||||
|
|
||||||
if (InLocalSpace)
|
if (InLocalSpace)
|
||||||
@@ -2107,7 +2208,7 @@ namespace Unity.Netcode.Components
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Only non-authoritative instances should invoke this
|
/// Only non-authoritative instances should invoke this
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private void UpdateState(NetworkTransformState oldState, NetworkTransformState newState)
|
private void ApplyUpdatedState(NetworkTransformState newState)
|
||||||
{
|
{
|
||||||
// Set the transforms's synchronization modes
|
// Set the transforms's synchronization modes
|
||||||
InLocalSpace = newState.InLocalSpace;
|
InLocalSpace = newState.InLocalSpace;
|
||||||
@@ -2137,8 +2238,10 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
// assure our local NetworkDeltaPosition state is updated
|
// assure our local NetworkDeltaPosition state is updated
|
||||||
m_HalfPositionState.HalfVector3.Axis = m_LocalAuthoritativeNetworkState.NetworkDeltaPosition.HalfVector3.Axis;
|
m_HalfPositionState.HalfVector3.Axis = m_LocalAuthoritativeNetworkState.NetworkDeltaPosition.HalfVector3.Axis;
|
||||||
// and update our current position
|
// and update our target position
|
||||||
m_LocalAuthoritativeNetworkState.CurrentPosition = m_HalfPositionState.ToVector3(newState.NetworkTick);
|
m_TargetPosition = m_HalfPositionState.ToVector3(newState.NetworkTick);
|
||||||
|
m_LocalAuthoritativeNetworkState.NetworkDeltaPosition.CurrentBasePosition = m_HalfPositionState.CurrentBasePosition;
|
||||||
|
m_LocalAuthoritativeNetworkState.CurrentPosition = m_TargetPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Interpolate)
|
if (!Interpolate)
|
||||||
@@ -2149,37 +2252,33 @@ namespace Unity.Netcode.Components
|
|||||||
// Apply axial changes from the new state
|
// Apply axial changes from the new state
|
||||||
// Either apply the delta position target position or the current state's delta position
|
// Either apply the delta position target position or the current state's delta position
|
||||||
// depending upon whether UsePositionDeltaCompression is enabled
|
// depending upon whether UsePositionDeltaCompression is enabled
|
||||||
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasPositionChange)
|
if (m_LocalAuthoritativeNetworkState.HasPositionChange)
|
||||||
{
|
{
|
||||||
if (m_LocalAuthoritativeNetworkState.UseHalfFloatPrecision)
|
if (!m_LocalAuthoritativeNetworkState.UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
UpdatePositionInterpolator(m_LocalAuthoritativeNetworkState.CurrentPosition, sentTime);
|
var newTargetPosition = m_TargetPosition;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var currentPosition = GetSpaceRelativePosition();
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasPositionX)
|
if (m_LocalAuthoritativeNetworkState.HasPositionX)
|
||||||
{
|
{
|
||||||
currentPosition.x = m_LocalAuthoritativeNetworkState.PositionX;
|
newTargetPosition.x = m_LocalAuthoritativeNetworkState.PositionX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasPositionY)
|
if (m_LocalAuthoritativeNetworkState.HasPositionY)
|
||||||
{
|
{
|
||||||
currentPosition.y = m_LocalAuthoritativeNetworkState.PositionY;
|
newTargetPosition.y = m_LocalAuthoritativeNetworkState.PositionY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasPositionZ)
|
if (m_LocalAuthoritativeNetworkState.HasPositionZ)
|
||||||
{
|
{
|
||||||
currentPosition.z = m_LocalAuthoritativeNetworkState.PositionZ;
|
newTargetPosition.z = m_LocalAuthoritativeNetworkState.PositionZ;
|
||||||
}
|
}
|
||||||
UpdatePositionInterpolator(currentPosition, sentTime);
|
m_TargetPosition = newTargetPosition;
|
||||||
}
|
}
|
||||||
|
UpdatePositionInterpolator(m_TargetPosition, sentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LocalAuthoritativeNetworkState.HasScaleChange)
|
if (m_LocalAuthoritativeNetworkState.HasScaleChange)
|
||||||
{
|
{
|
||||||
var currentScale = transform.localScale;
|
var currentScale = m_TargetScale;
|
||||||
if (UseHalfFloatPrecision)
|
if (UseHalfFloatPrecision)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
@@ -2207,6 +2306,7 @@ namespace Unity.Netcode.Components
|
|||||||
currentScale.z = m_LocalAuthoritativeNetworkState.ScaleZ;
|
currentScale.z = m_LocalAuthoritativeNetworkState.ScaleZ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_TargetScale = currentScale;
|
||||||
m_ScaleInterpolator.AddMeasurement(currentScale, sentTime);
|
m_ScaleInterpolator.AddMeasurement(currentScale, sentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2221,7 +2321,9 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
currentEulerAngles = m_TargetRotation;
|
||||||
// Adjust based on which axis changed
|
// Adjust based on which axis changed
|
||||||
|
// (both half precision and full precision apply Eulers to the RotAngle properties when reading the update)
|
||||||
if (m_LocalAuthoritativeNetworkState.HasRotAngleX)
|
if (m_LocalAuthoritativeNetworkState.HasRotAngleX)
|
||||||
{
|
{
|
||||||
currentEulerAngles.x = m_LocalAuthoritativeNetworkState.RotAngleX;
|
currentEulerAngles.x = m_LocalAuthoritativeNetworkState.RotAngleX;
|
||||||
@@ -2236,6 +2338,7 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
currentEulerAngles.z = m_LocalAuthoritativeNetworkState.RotAngleZ;
|
currentEulerAngles.z = m_LocalAuthoritativeNetworkState.RotAngleZ;
|
||||||
}
|
}
|
||||||
|
m_TargetRotation = currentEulerAngles;
|
||||||
currentRotation.eulerAngles = currentEulerAngles;
|
currentRotation.eulerAngles = currentEulerAngles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2253,6 +2356,8 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NetworkTransformState m_OldState = new NetworkTransformState();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Only non-authoritative instances should invoke this method
|
/// Only non-authoritative instances should invoke this method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2266,8 +2371,8 @@ namespace Unity.Netcode.Components
|
|||||||
// Get the time when this new state was sent
|
// Get the time when this new state was sent
|
||||||
newState.SentTime = new NetworkTime(NetworkManager.NetworkConfig.TickRate, newState.NetworkTick).Time;
|
newState.SentTime = new NetworkTime(NetworkManager.NetworkConfig.TickRate, newState.NetworkTick).Time;
|
||||||
|
|
||||||
// Update the state
|
// Apply the new state
|
||||||
UpdateState(oldState, newState);
|
ApplyUpdatedState(newState);
|
||||||
|
|
||||||
// Provide notifications when the state has been updated
|
// Provide notifications when the state has been updated
|
||||||
OnNetworkTransformStateUpdated(ref oldState, ref newState);
|
OnNetworkTransformStateUpdated(ref oldState, ref newState);
|
||||||
@@ -2350,10 +2455,19 @@ namespace Unity.Netcode.Components
|
|||||||
internal void OnUpdateAuthoritativeState(ref Transform transformSource)
|
internal void OnUpdateAuthoritativeState(ref Transform transformSource)
|
||||||
{
|
{
|
||||||
// If our replicated state is not dirty and our local authority state is dirty, clear it.
|
// If our replicated state is not dirty and our local authority state is dirty, clear it.
|
||||||
if (!ReplicatedNetworkState.IsDirty() && m_LocalAuthoritativeNetworkState.IsDirty && !m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame)
|
if (m_LocalAuthoritativeNetworkState.IsDirty && !m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame)
|
||||||
{
|
{
|
||||||
// Now clear our bitset and prepare for next network tick state update
|
// Now clear our bitset and prepare for next network tick state update
|
||||||
m_LocalAuthoritativeNetworkState.ClearBitSetForNextTick();
|
m_LocalAuthoritativeNetworkState.ClearBitSetForNextTick();
|
||||||
|
if (TrackByStateId)
|
||||||
|
{
|
||||||
|
m_LocalAuthoritativeNetworkState.TrackByStateId = true;
|
||||||
|
m_LocalAuthoritativeNetworkState.StateId++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_LocalAuthoritativeNetworkState.TrackByStateId = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AxisChangedDeltaPositionCheck();
|
AxisChangedDeltaPositionCheck();
|
||||||
@@ -2373,6 +2487,9 @@ namespace Unity.Netcode.Components
|
|||||||
// Update any changes to the transform
|
// Update any changes to the transform
|
||||||
var transformSource = transform;
|
var transformSource = transform;
|
||||||
OnUpdateAuthoritativeState(ref transformSource);
|
OnUpdateAuthoritativeState(ref transformSource);
|
||||||
|
|
||||||
|
m_CurrentPosition = GetSpaceRelativePosition();
|
||||||
|
m_TargetPosition = GetSpaceRelativePosition();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2387,27 +2504,25 @@ namespace Unity.Netcode.Components
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void OnNetworkSpawn()
|
public override void OnNetworkSpawn()
|
||||||
{
|
{
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
// NOTE: Legacy and no longer used (candidates for deprecation)
|
// NOTE: Legacy and no longer used (candidates for deprecation)
|
||||||
m_CachedIsServer = IsServer;
|
m_CachedIsServer = IsServer;
|
||||||
m_CachedNetworkManager = NetworkManager;
|
m_CachedNetworkManager = NetworkManager;
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Register a custom named message specifically for this instance
|
||||||
|
m_MessageName = $"NTU_{NetworkObjectId}_{NetworkBehaviourId}";
|
||||||
|
NetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(m_MessageName, TransformStateUpdate);
|
||||||
Initialize();
|
Initialize();
|
||||||
// This assures the initial spawning of the object synchronizes all connected clients
|
|
||||||
// with the current transform values. This should not be placed within Initialize since
|
|
||||||
// that can be invoked when ownership changes.
|
|
||||||
if (CanCommitToTransform)
|
|
||||||
{
|
|
||||||
var currentPosition = GetSpaceRelativePosition();
|
|
||||||
var currentRotation = GetSpaceRelativeRotation();
|
|
||||||
// Teleport to current position
|
|
||||||
SetStateInternal(currentPosition, currentRotation, transform.localScale, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void OnNetworkDespawn()
|
public override void OnNetworkDespawn()
|
||||||
{
|
{
|
||||||
ReplicatedNetworkState.OnValueChanged -= OnNetworkStateChanged;
|
if (!NetworkManager.ShutdownInProgress && NetworkManager.CustomMessagingManager != null)
|
||||||
|
{
|
||||||
|
NetworkManager.CustomMessagingManager.UnregisterNamedMessageHandler(m_MessageName);
|
||||||
|
}
|
||||||
CanCommitToTransform = false;
|
CanCommitToTransform = false;
|
||||||
if (NetworkManager != null && NetworkManager.NetworkTickSystem != null)
|
if (NetworkManager != null && NetworkManager.NetworkTickSystem != null)
|
||||||
{
|
{
|
||||||
@@ -2424,41 +2539,53 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
CanCommitToTransform = false;
|
CanCommitToTransform = false;
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
m_ReplicatedNetworkStateServer.Dispose();
|
|
||||||
m_ReplicatedNetworkStateOwner.Dispose();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override void OnGainedOwnership()
|
|
||||||
{
|
|
||||||
// Only initialize if we gained ownership
|
|
||||||
if (OwnerClientId == NetworkManager.LocalClientId)
|
|
||||||
{
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void OnLostOwnership()
|
public override void OnLostOwnership()
|
||||||
{
|
{
|
||||||
// Only initialize if we are not authority and lost
|
base.OnLostOwnership();
|
||||||
// ownership
|
}
|
||||||
if (OwnerClientId != NetworkManager.LocalClientId)
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void OnGainedOwnership()
|
||||||
|
{
|
||||||
|
base.OnGainedOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnOwnershipChanged(ulong previous, ulong current)
|
||||||
|
{
|
||||||
|
// If we were the previous owner or the newly assigned owner then reinitialize
|
||||||
|
if (current == NetworkManager.LocalClientId || previous == NetworkManager.LocalClientId)
|
||||||
{
|
{
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
base.OnOwnershipChanged(previous, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when first spawned and when ownership changes.
|
/// Invoked when first spawned and when ownership changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="replicatedState">the <see cref="NetworkVariable{T}"/> replicated <see cref="NetworkTransformState"/></param>
|
/// <param name="replicatedState">the current <see cref="NetworkTransformState"/> after initializing</param>
|
||||||
|
protected virtual void OnInitialize(ref NetworkTransformState replicatedState)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An owner read and owner write NetworkVariable so it doesn't generate any messages
|
||||||
|
/// </summary>
|
||||||
|
private NetworkVariable<NetworkTransformState> m_InternalStatNetVar = new NetworkVariable<NetworkTransformState>(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Owner);
|
||||||
|
/// <summary>
|
||||||
|
/// This method is only invoked by the owner
|
||||||
|
/// Use: OnInitialize(ref NetworkTransformState replicatedState) to be notified on all instances
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="replicatedState"></param>
|
||||||
protected virtual void OnInitialize(ref NetworkVariable<NetworkTransformState> replicatedState)
|
protected virtual void OnInitialize(ref NetworkVariable<NetworkTransformState> replicatedState)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes NetworkTransform when spawned and ownership changes.
|
/// Initializes NetworkTransform when spawned and ownership changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2470,8 +2597,8 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
CanCommitToTransform = IsServerAuthoritative() ? IsServer : IsOwner;
|
CanCommitToTransform = IsServerAuthoritative() ? IsServer : IsOwner;
|
||||||
var replicatedState = ReplicatedNetworkState;
|
|
||||||
var currentPosition = GetSpaceRelativePosition();
|
var currentPosition = GetSpaceRelativePosition();
|
||||||
|
var currentRotation = GetSpaceRelativeRotation();
|
||||||
|
|
||||||
if (CanCommitToTransform)
|
if (CanCommitToTransform)
|
||||||
{
|
{
|
||||||
@@ -2479,28 +2606,65 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
m_HalfPositionState = new NetworkDeltaPosition(currentPosition, NetworkManager.NetworkTickSystem.ServerTime.Tick, math.bool3(SyncPositionX, SyncPositionY, SyncPositionZ));
|
m_HalfPositionState = new NetworkDeltaPosition(currentPosition, NetworkManager.NetworkTickSystem.ServerTime.Tick, math.bool3(SyncPositionX, SyncPositionY, SyncPositionZ));
|
||||||
}
|
}
|
||||||
|
m_CurrentPosition = currentPosition;
|
||||||
|
m_TargetPosition = currentPosition;
|
||||||
// Authority only updates once per network tick
|
// Authority only updates once per network tick
|
||||||
NetworkManager.NetworkTickSystem.Tick -= NetworkTickSystem_Tick;
|
NetworkManager.NetworkTickSystem.Tick -= NetworkTickSystem_Tick;
|
||||||
NetworkManager.NetworkTickSystem.Tick += NetworkTickSystem_Tick;
|
NetworkManager.NetworkTickSystem.Tick += NetworkTickSystem_Tick;
|
||||||
|
|
||||||
|
// Teleport to current position
|
||||||
|
SetStateInternal(currentPosition, currentRotation, transform.localScale, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Sanity check to assure we only subscribe to OnValueChanged once
|
|
||||||
replicatedState.OnValueChanged -= OnNetworkStateChanged;
|
|
||||||
replicatedState.OnValueChanged += OnNetworkStateChanged;
|
|
||||||
|
|
||||||
// Assure we no longer subscribe to the tick event
|
// Assure we no longer subscribe to the tick event
|
||||||
NetworkManager.NetworkTickSystem.Tick -= NetworkTickSystem_Tick;
|
NetworkManager.NetworkTickSystem.Tick -= NetworkTickSystem_Tick;
|
||||||
|
|
||||||
ResetInterpolatedStateToCurrentAuthoritativeState();
|
ResetInterpolatedStateToCurrentAuthoritativeState();
|
||||||
m_CurrentPosition = GetSpaceRelativePosition();
|
|
||||||
|
m_CurrentPosition = currentPosition;
|
||||||
|
m_TargetPosition = currentPosition;
|
||||||
m_CurrentScale = transform.localScale;
|
m_CurrentScale = transform.localScale;
|
||||||
m_CurrentRotation = GetSpaceRelativeRotation();
|
m_TargetScale = transform.localScale;
|
||||||
|
m_CurrentRotation = currentRotation;
|
||||||
|
m_TargetRotation = currentRotation.eulerAngles;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
OnInitialize(ref m_LocalAuthoritativeNetworkState);
|
||||||
|
|
||||||
OnInitialize(ref replicatedState);
|
if (IsOwner)
|
||||||
|
{
|
||||||
|
m_InternalStatNetVar.Value = m_LocalAuthoritativeNetworkState;
|
||||||
|
OnInitialize(ref m_InternalStatNetVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// <remarks>
|
||||||
|
/// When a parent changes, non-authoritative instances should:
|
||||||
|
/// - Apply the resultant position, rotation, and scale from the parenting action.
|
||||||
|
/// - Clear interpolators (even if not enabled on this frame)
|
||||||
|
/// - Reset the interpolators to the position, rotation, and scale resultant values.
|
||||||
|
/// This prevents interpolation visual anomalies and issues during initial synchronization
|
||||||
|
/// </remarks>
|
||||||
|
public override void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject)
|
||||||
|
{
|
||||||
|
// Only if we are not authority
|
||||||
|
if (!CanCommitToTransform)
|
||||||
|
{
|
||||||
|
m_CurrentPosition = GetSpaceRelativePosition();
|
||||||
|
m_CurrentRotation = GetSpaceRelativeRotation();
|
||||||
|
m_CurrentScale = GetScale();
|
||||||
|
m_ScaleInterpolator.Clear();
|
||||||
|
m_PositionInterpolator.Clear();
|
||||||
|
m_RotationInterpolator.Clear();
|
||||||
|
var tempTime = new NetworkTime(NetworkManager.NetworkConfig.TickRate, NetworkManager.ServerTime.Tick).Time;
|
||||||
|
UpdatePositionInterpolator(m_CurrentPosition, tempTime, true);
|
||||||
|
m_ScaleInterpolator.ResetTo(m_CurrentScale, tempTime);
|
||||||
|
m_RotationInterpolator.ResetTo(m_CurrentRotation, tempTime);
|
||||||
|
}
|
||||||
|
base.OnNetworkObjectParentChanged(parentNetworkObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -2628,7 +2792,7 @@ namespace Unity.Netcode.Components
|
|||||||
var serverTime = NetworkManager.ServerTime;
|
var serverTime = NetworkManager.ServerTime;
|
||||||
var cachedDeltaTime = NetworkManager.RealTimeProvider.DeltaTime;
|
var cachedDeltaTime = NetworkManager.RealTimeProvider.DeltaTime;
|
||||||
var cachedServerTime = serverTime.Time;
|
var cachedServerTime = serverTime.Time;
|
||||||
// TODO: Investigate Further
|
|
||||||
// With owner authoritative mode, non-authority clients can lag behind
|
// With owner authoritative mode, non-authority clients can lag behind
|
||||||
// by more than 1 tick period of time. The current "solution" for now
|
// by more than 1 tick period of time. The current "solution" for now
|
||||||
// is to make their cachedRenderTime run 2 ticks behind.
|
// is to make their cachedRenderTime run 2 ticks behind.
|
||||||
@@ -2698,6 +2862,111 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
return OnIsServerAuthoritative();
|
return OnIsServerAuthoritative();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Receives the <see cref="NetworkTransformState"/> named message updates
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="senderId">authority of the transform</param>
|
||||||
|
/// <param name="messagePayload">serialzied <see cref="NetworkTransformState"/></param>
|
||||||
|
private void TransformStateUpdate(ulong senderId, FastBufferReader messagePayload)
|
||||||
|
{
|
||||||
|
if (!OnIsServerAuthoritative() && IsServer && OwnerClientId == NetworkManager.ServerClientId)
|
||||||
|
{
|
||||||
|
// Ownership must have changed, ignore any additional pending messages that might have
|
||||||
|
// come from a previous owner client.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward owner authoritative messages before doing anything else
|
||||||
|
if (IsServer && !OnIsServerAuthoritative())
|
||||||
|
{
|
||||||
|
ForwardStateUpdateMessage(messagePayload);
|
||||||
|
}
|
||||||
|
// Store the previous/old state
|
||||||
|
m_OldState = m_LocalAuthoritativeNetworkState;
|
||||||
|
|
||||||
|
// Deserialize the message
|
||||||
|
messagePayload.ReadNetworkSerializableInPlace(ref m_LocalAuthoritativeNetworkState);
|
||||||
|
|
||||||
|
// Apply the message
|
||||||
|
OnNetworkStateChanged(m_OldState, m_LocalAuthoritativeNetworkState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forwards owner authoritative state updates when received by the server
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messagePayload">the owner state message payload</param>
|
||||||
|
private unsafe void ForwardStateUpdateMessage(FastBufferReader messagePayload)
|
||||||
|
{
|
||||||
|
var serverAuthoritative = OnIsServerAuthoritative();
|
||||||
|
var currentPosition = messagePayload.Position;
|
||||||
|
var messageSize = messagePayload.Length - currentPosition;
|
||||||
|
var writer = new FastBufferWriter(messageSize, Allocator.Temp);
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteBytesSafe(messagePayload.GetUnsafePtr(), messageSize, currentPosition);
|
||||||
|
|
||||||
|
var clientCount = NetworkManager.ConnectionManager.ConnectedClientsList.Count;
|
||||||
|
for (int i = 0; i < clientCount; i++)
|
||||||
|
{
|
||||||
|
var clientId = NetworkManager.ConnectionManager.ConnectedClientsList[i].ClientId;
|
||||||
|
if (NetworkManager.ServerClientId == clientId || (!serverAuthoritative && clientId == OwnerClientId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
NetworkManager.CustomMessagingManager.SendNamedMessage(m_MessageName, clientId, writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messagePayload.Seek(currentPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends <see cref="NetworkTransformState"/> named message updates by the authority of the transform
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateTransformState()
|
||||||
|
{
|
||||||
|
if (NetworkManager.ShutdownInProgress)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isServerAuthoritative = OnIsServerAuthoritative();
|
||||||
|
if (isServerAuthoritative && !IsServer)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Server authoritative {nameof(NetworkTransform)} can only be updated by the server!");
|
||||||
|
}
|
||||||
|
else if (!isServerAuthoritative && !IsServer && !IsOwner)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Owner authoritative {nameof(NetworkTransform)} can only be updated by the owner!");
|
||||||
|
}
|
||||||
|
var customMessageManager = NetworkManager.CustomMessagingManager;
|
||||||
|
|
||||||
|
var writer = new FastBufferWriter(128, Allocator.Temp);
|
||||||
|
|
||||||
|
using (writer)
|
||||||
|
{
|
||||||
|
writer.WriteNetworkSerializable(m_LocalAuthoritativeNetworkState);
|
||||||
|
// Server-host always sends updates to all clients (but itself)
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
var clientCount = NetworkManager.ConnectionManager.ConnectedClientsList.Count;
|
||||||
|
for (int i = 0; i < clientCount; i++)
|
||||||
|
{
|
||||||
|
var clientId = NetworkManager.ConnectionManager.ConnectedClientsList[i].ClientId;
|
||||||
|
if (NetworkManager.ServerClientId == clientId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
customMessageManager.SendNamedMessage(m_MessageName, clientId, writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Clients (owner authoritative) send messages to the server-host
|
||||||
|
customMessageManager.SendNamedMessage(m_MessageName, NetworkManager.ServerClientId, writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface INetworkTransformLogStateEntry
|
internal interface INetworkTransformLogStateEntry
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ Netcode for GameObjects is a Unity package that provides networking capabilities
|
|||||||
See guides below to install Unity Netcode for GameObjects, set up your project, and get started with your first networked game:
|
See guides below to install Unity Netcode for GameObjects, set up your project, and get started with your first networked game:
|
||||||
|
|
||||||
- [Documentation](https://docs-multiplayer.unity3d.com/netcode/current/about)
|
- [Documentation](https://docs-multiplayer.unity3d.com/netcode/current/about)
|
||||||
- [Installation](https://docs-multiplayer.unity3d.com/netcode/current/migration/install)
|
- [Installation](https://docs-multiplayer.unity3d.com/netcode/current/installation)
|
||||||
- [First Steps](https://docs-multiplayer.unity3d.com/netcode/current/tutorials/helloworld/helloworldintro)
|
- [First Steps](https://docs-multiplayer.unity3d.com/netcode/current/tutorials/get-started-ngo)
|
||||||
- [API Reference](https://docs-multiplayer.unity3d.com/netcode/current/api/introduction)
|
- [API Reference](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@1.6/api/index.html)
|
||||||
|
|
||||||
# Technical details
|
# Technical details
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Unity.Collections;
|
|||||||
using Unity.CompilationPipeline.Common.Diagnostics;
|
using Unity.CompilationPipeline.Common.Diagnostics;
|
||||||
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Object = System.Object;
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor.CodeGen
|
namespace Unity.Netcode.Editor.CodeGen
|
||||||
{
|
{
|
||||||
@@ -58,7 +59,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
public static bool IsSubclassOf(this TypeDefinition typeDefinition, string classTypeFullName)
|
public static bool IsSubclassOf(this TypeDefinition typeDefinition, string classTypeFullName)
|
||||||
{
|
{
|
||||||
if (!typeDefinition.IsClass)
|
if (typeDefinition == null || !typeDefinition.IsClass)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -112,6 +113,64 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
public static TypeReference MakeGenericType(this TypeReference self, params TypeReference[] arguments)
|
||||||
|
{
|
||||||
|
if (self.GenericParameters.Count != arguments.Length)
|
||||||
|
{
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance = new GenericInstanceType(self);
|
||||||
|
foreach (var argument in arguments)
|
||||||
|
{
|
||||||
|
instance.GenericArguments.Add(argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MethodReference MakeGeneric(this MethodReference self, params TypeReference[] arguments)
|
||||||
|
{
|
||||||
|
var reference = new MethodReference(self.Name, self.ReturnType)
|
||||||
|
{
|
||||||
|
DeclaringType = self.DeclaringType.MakeGenericType(arguments),
|
||||||
|
HasThis = self.HasThis,
|
||||||
|
ExplicitThis = self.ExplicitThis,
|
||||||
|
CallingConvention = self.CallingConvention,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var parameter in self.Parameters)
|
||||||
|
{
|
||||||
|
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var generic_parameter in self.GenericParameters)
|
||||||
|
{
|
||||||
|
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
|
||||||
|
}
|
||||||
|
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSubclassOf(this TypeReference typeReference, TypeReference baseClass)
|
||||||
|
{
|
||||||
|
if (typeReference == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var type = typeReference.Resolve();
|
||||||
|
if (type?.BaseType == null || type.BaseType.Name == nameof(Object))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.BaseType.Resolve() == baseClass.Resolve())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return type.BaseType.IsSubclassOf(baseClass);
|
||||||
|
}
|
||||||
|
|
||||||
public static bool HasInterface(this TypeReference typeReference, string interfaceTypeFullName)
|
public static bool HasInterface(this TypeReference typeReference, string interfaceTypeFullName)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Mono.Cecil.Cil;
|
|||||||
using Mono.Cecil.Rocks;
|
using Mono.Cecil.Rocks;
|
||||||
using Unity.CompilationPipeline.Common.Diagnostics;
|
using Unity.CompilationPipeline.Common.Diagnostics;
|
||||||
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
||||||
|
using UnityEngine;
|
||||||
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
|
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
|
||||||
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
||||||
|
|
||||||
@@ -101,54 +102,54 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
private ModuleDefinition m_NetcodeModule;
|
private ModuleDefinition m_NetcodeModule;
|
||||||
private PostProcessorAssemblyResolver m_AssemblyResolver;
|
private PostProcessorAssemblyResolver m_AssemblyResolver;
|
||||||
|
|
||||||
private MethodReference m_MessagingSystem_ReceiveMessage_MethodRef;
|
private MethodReference m_RuntimeInitializeOnLoadAttribute_Ctor;
|
||||||
private MethodReference m_MessagingSystem_CreateMessageAndGetVersion_MethodRef;
|
|
||||||
private TypeReference m_MessagingSystem_MessageWithHandler_TypeRef;
|
private MethodReference m_MessageManager_ReceiveMessage_MethodRef;
|
||||||
private MethodReference m_MessagingSystem_MessageHandler_Constructor_TypeRef;
|
private MethodReference m_MessageManager_CreateMessageAndGetVersion_MethodRef;
|
||||||
private MethodReference m_MessagingSystem_VersionGetter_Constructor_TypeRef;
|
private TypeReference m_MessageManager_MessageWithHandler_TypeRef;
|
||||||
|
private MethodReference m_MessageManager_MessageHandler_Constructor_TypeRef;
|
||||||
|
private MethodReference m_MessageManager_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_MessageManager_MessageWithHandler_MessageType_FieldRef;
|
||||||
private FieldReference m_MessagingSystem_MessageWithHandler_Handler_FieldRef;
|
private FieldReference m_MessageManager_MessageWithHandler_Handler_FieldRef;
|
||||||
private FieldReference m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef;
|
private FieldReference m_MessageManager_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(NetworkMessageManager.ReceiveMessage);
|
||||||
private const string k_CreateMessageAndGetVersionName = nameof(MessagingSystem.CreateMessageAndGetVersion);
|
private const string k_CreateMessageAndGetVersionName = nameof(NetworkMessageManager.CreateMessageAndGetVersion);
|
||||||
|
|
||||||
private bool ImportReferences(ModuleDefinition moduleDefinition)
|
private bool ImportReferences(ModuleDefinition moduleDefinition)
|
||||||
{
|
{
|
||||||
// Different environments seem to have different situations...
|
// Different environments seem to have different situations...
|
||||||
// Some have these definitions in netstandard.dll...
|
// Some have these definitions in netstandard.dll, some seem to have them elsewhere...
|
||||||
// some seem to have them elsewhere...
|
// Since they're standard .net classes they're not going to cause the same issues as referencing other assemblies,
|
||||||
// Since they're standard .net classes they're not going to cause
|
// in theory, since the definitions should be standard and consistent across platforms
|
||||||
// the same issues as referencing other assemblies, in theory, since
|
// (i.e., there's no #if UNITY_EDITOR in them that could create invalid IL code)
|
||||||
// the definitions should be standard and consistent across platforms
|
|
||||||
// (i.e., there's no #if UNITY_EDITOR in them that could create
|
|
||||||
// invalid IL code)
|
|
||||||
TypeDefinition typeTypeDef = moduleDefinition.ImportReference(typeof(Type)).Resolve();
|
TypeDefinition typeTypeDef = moduleDefinition.ImportReference(typeof(Type)).Resolve();
|
||||||
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();
|
TypeDefinition listTypeDef = moduleDefinition.ImportReference(typeof(List<>)).Resolve();
|
||||||
|
m_RuntimeInitializeOnLoadAttribute_Ctor = moduleDefinition.ImportReference(typeof(RuntimeInitializeOnLoadMethodAttribute).GetConstructor(new Type[] { }));
|
||||||
|
|
||||||
TypeDefinition messageHandlerTypeDef = null;
|
TypeDefinition messageHandlerTypeDef = null;
|
||||||
TypeDefinition versionGetterTypeDef = null;
|
TypeDefinition versionGetterTypeDef = null;
|
||||||
TypeDefinition messageWithHandlerTypeDef = null;
|
TypeDefinition messageWithHandlerTypeDef = null;
|
||||||
TypeDefinition ilppMessageProviderTypeDef = null;
|
TypeDefinition ilppMessageProviderTypeDef = null;
|
||||||
TypeDefinition messagingSystemTypeDef = null;
|
TypeDefinition messageManagerSystemTypeDef = null;
|
||||||
foreach (var netcodeTypeDef in m_NetcodeModule.GetAllTypes())
|
foreach (var netcodeTypeDef in m_NetcodeModule.GetAllTypes())
|
||||||
{
|
{
|
||||||
if (messageHandlerTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.MessageHandler))
|
if (messageHandlerTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.MessageHandler))
|
||||||
{
|
{
|
||||||
messageHandlerTypeDef = netcodeTypeDef;
|
messageHandlerTypeDef = netcodeTypeDef;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (versionGetterTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.VersionGetter))
|
if (versionGetterTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.VersionGetter))
|
||||||
{
|
{
|
||||||
versionGetterTypeDef = netcodeTypeDef;
|
versionGetterTypeDef = netcodeTypeDef;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messageWithHandlerTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem.MessageWithHandler))
|
if (messageWithHandlerTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager.MessageWithHandler))
|
||||||
{
|
{
|
||||||
messageWithHandlerTypeDef = netcodeTypeDef;
|
messageWithHandlerTypeDef = netcodeTypeDef;
|
||||||
continue;
|
continue;
|
||||||
@@ -160,29 +161,29 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messagingSystemTypeDef == null && netcodeTypeDef.Name == nameof(MessagingSystem))
|
if (messageManagerSystemTypeDef == null && netcodeTypeDef.Name == nameof(NetworkMessageManager))
|
||||||
{
|
{
|
||||||
messagingSystemTypeDef = netcodeTypeDef;
|
messageManagerSystemTypeDef = netcodeTypeDef;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_MessagingSystem_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(messageHandlerTypeDef.GetConstructors().First());
|
m_MessageManager_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(messageHandlerTypeDef.GetConstructors().First());
|
||||||
m_MessagingSystem_VersionGetter_Constructor_TypeRef = moduleDefinition.ImportReference(versionGetterTypeDef.GetConstructors().First());
|
m_MessageManager_VersionGetter_Constructor_TypeRef = moduleDefinition.ImportReference(versionGetterTypeDef.GetConstructors().First());
|
||||||
|
|
||||||
m_MessagingSystem_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerTypeDef);
|
m_MessageManager_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerTypeDef);
|
||||||
foreach (var fieldDef in messageWithHandlerTypeDef.Fields)
|
foreach (var fieldDef in messageWithHandlerTypeDef.Fields)
|
||||||
{
|
{
|
||||||
switch (fieldDef.Name)
|
switch (fieldDef.Name)
|
||||||
{
|
{
|
||||||
case nameof(MessagingSystem.MessageWithHandler.MessageType):
|
case nameof(NetworkMessageManager.MessageWithHandler.MessageType):
|
||||||
m_MessagingSystem_MessageWithHandler_MessageType_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
m_MessageManager_MessageWithHandler_MessageType_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
||||||
break;
|
break;
|
||||||
case nameof(MessagingSystem.MessageWithHandler.Handler):
|
case nameof(NetworkMessageManager.MessageWithHandler.Handler):
|
||||||
m_MessagingSystem_MessageWithHandler_Handler_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
m_MessageManager_MessageWithHandler_Handler_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
||||||
break;
|
break;
|
||||||
case nameof(MessagingSystem.MessageWithHandler.GetVersion):
|
case nameof(NetworkMessageManager.MessageWithHandler.GetVersion):
|
||||||
m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
m_MessageManager_MessageWithHandler_GetVersion_FieldRef = moduleDefinition.ImportReference(fieldDef);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,15 +220,15 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var methodDef in messagingSystemTypeDef.Methods)
|
foreach (var methodDef in messageManagerSystemTypeDef.Methods)
|
||||||
{
|
{
|
||||||
switch (methodDef.Name)
|
switch (methodDef.Name)
|
||||||
{
|
{
|
||||||
case k_ReceiveMessageName:
|
case k_ReceiveMessageName:
|
||||||
m_MessagingSystem_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodDef);
|
m_MessageManager_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodDef);
|
||||||
break;
|
break;
|
||||||
case k_CreateMessageAndGetVersionName:
|
case k_CreateMessageAndGetVersionName:
|
||||||
m_MessagingSystem_CreateMessageAndGetVersion_MethodRef = moduleDefinition.ImportReference(methodDef);
|
m_MessageManager_CreateMessageAndGetVersion_MethodRef = moduleDefinition.ImportReference(methodDef);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,48 +236,29 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodDefinition GetOrCreateStaticConstructor(TypeDefinition typeDefinition)
|
|
||||||
{
|
|
||||||
var staticCtorMethodDef = typeDefinition.GetStaticConstructor();
|
|
||||||
if (staticCtorMethodDef == null)
|
|
||||||
{
|
|
||||||
staticCtorMethodDef = new MethodDefinition(
|
|
||||||
".cctor", // Static Constructor (constant-constructor)
|
|
||||||
MethodAttributes.HideBySig |
|
|
||||||
MethodAttributes.SpecialName |
|
|
||||||
MethodAttributes.RTSpecialName |
|
|
||||||
MethodAttributes.Static,
|
|
||||||
typeDefinition.Module.TypeSystem.Void);
|
|
||||||
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
|
||||||
typeDefinition.Methods.Add(staticCtorMethodDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
return staticCtorMethodDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateInstructionsToRegisterType(ILProcessor processor, List<Instruction> instructions, TypeReference type, MethodReference receiveMethod, MethodReference versionMethod)
|
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});
|
// NetworkMessageManager.__network_message_types.Add(new NetworkMessageManager.MessageWithHandler{MessageType=typeof(type), Handler=type.Receive});
|
||||||
processor.Body.Variables.Add(new VariableDefinition(m_MessagingSystem_MessageWithHandler_TypeRef));
|
processor.Body.Variables.Add(new VariableDefinition(m_MessageManager_MessageWithHandler_TypeRef));
|
||||||
int messageWithHandlerLocIdx = processor.Body.Variables.Count - 1;
|
int messageWithHandlerLocIdx = processor.Body.Variables.Count - 1;
|
||||||
|
|
||||||
instructions.Add(processor.Create(OpCodes.Ldsfld, m_ILPPMessageProvider___network_message_types_FieldRef));
|
instructions.Add(processor.Create(OpCodes.Ldsfld, m_ILPPMessageProvider___network_message_types_FieldRef));
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
|
||||||
instructions.Add(processor.Create(OpCodes.Initobj, m_MessagingSystem_MessageWithHandler_TypeRef));
|
instructions.Add(processor.Create(OpCodes.Initobj, m_MessageManager_MessageWithHandler_TypeRef));
|
||||||
|
|
||||||
// tmp.MessageType = typeof(type);
|
// tmp.MessageType = typeof(type);
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloca, messageWithHandlerLocIdx));
|
||||||
instructions.Add(processor.Create(OpCodes.Ldtoken, type));
|
instructions.Add(processor.Create(OpCodes.Ldtoken, type));
|
||||||
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_MessageManager_MessageWithHandler_MessageType_FieldRef));
|
||||||
|
|
||||||
// tmp.Handler = MessageHandler.ReceveMessage<type>
|
// 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));
|
||||||
|
|
||||||
instructions.Add(processor.Create(OpCodes.Ldftn, receiveMethod));
|
instructions.Add(processor.Create(OpCodes.Ldftn, receiveMethod));
|
||||||
instructions.Add(processor.Create(OpCodes.Newobj, m_MessagingSystem_MessageHandler_Constructor_TypeRef));
|
instructions.Add(processor.Create(OpCodes.Newobj, m_MessageManager_MessageHandler_Constructor_TypeRef));
|
||||||
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_Handler_FieldRef));
|
instructions.Add(processor.Create(OpCodes.Stfld, m_MessageManager_MessageWithHandler_Handler_FieldRef));
|
||||||
|
|
||||||
|
|
||||||
// tmp.GetVersion = MessageHandler.CreateMessageAndGetVersion<type>
|
// tmp.GetVersion = MessageHandler.CreateMessageAndGetVersion<type>
|
||||||
@@ -284,43 +266,46 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.Add(processor.Create(OpCodes.Ldnull));
|
instructions.Add(processor.Create(OpCodes.Ldnull));
|
||||||
|
|
||||||
instructions.Add(processor.Create(OpCodes.Ldftn, versionMethod));
|
instructions.Add(processor.Create(OpCodes.Ldftn, versionMethod));
|
||||||
instructions.Add(processor.Create(OpCodes.Newobj, m_MessagingSystem_VersionGetter_Constructor_TypeRef));
|
instructions.Add(processor.Create(OpCodes.Newobj, m_MessageManager_VersionGetter_Constructor_TypeRef));
|
||||||
instructions.Add(processor.Create(OpCodes.Stfld, m_MessagingSystem_MessageWithHandler_GetVersion_FieldRef));
|
instructions.Add(processor.Create(OpCodes.Stfld, m_MessageManager_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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a static module constructor (which is executed when the module is loaded) that registers all the message types in the assembly with MessagingSystem.
|
// Creates a static module constructor (which is executed when the module is loaded) that registers all the message types in the assembly with NetworkMessageManager.
|
||||||
// This is the same behavior as annotating a static method with [ModuleInitializer] in standardized C# (that attribute doesn't exist in Unity, but the static module constructor still works).
|
// This is the same behavior as annotating a static method with [ModuleInitializer] in standardized C# (that attribute doesn't exist in Unity, but the static module constructor still works).
|
||||||
// https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.moduleinitializerattribute?view=net-5.0
|
// https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.moduleinitializerattribute?view=net-5.0
|
||||||
// https://web.archive.org/web/20100212140402/http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
|
// https://web.archive.org/web/20100212140402/http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
|
||||||
private void CreateModuleInitializer(AssemblyDefinition assembly, List<TypeDefinition> networkMessageTypes)
|
private void CreateModuleInitializer(AssemblyDefinition assembly, List<TypeDefinition> networkMessageTypes)
|
||||||
{
|
{
|
||||||
foreach (var typeDefinition in assembly.MainModule.Types)
|
var typeDefinition = new TypeDefinition("__GEN", "INetworkMessageHelper", TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit, assembly.MainModule.TypeSystem.Object);
|
||||||
{
|
|
||||||
if (typeDefinition.FullName == "<Module>")
|
|
||||||
{
|
|
||||||
var staticCtorMethodDef = GetOrCreateStaticConstructor(typeDefinition);
|
|
||||||
|
|
||||||
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
var staticCtorMethodDef = new MethodDefinition(
|
||||||
|
$"InitializeMessages",
|
||||||
|
MethodAttributes.Assembly |
|
||||||
|
MethodAttributes.Static,
|
||||||
|
assembly.MainModule.TypeSystem.Void);
|
||||||
|
staticCtorMethodDef.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||||
|
staticCtorMethodDef.CustomAttributes.Add(new CustomAttribute(m_RuntimeInitializeOnLoadAttribute_Ctor));
|
||||||
|
typeDefinition.Methods.Add(staticCtorMethodDef);
|
||||||
|
|
||||||
var instructions = new List<Instruction>();
|
var instructions = new List<Instruction>();
|
||||||
|
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
||||||
|
|
||||||
foreach (var type in networkMessageTypes)
|
foreach (var type in networkMessageTypes)
|
||||||
{
|
{
|
||||||
var receiveMethod = new GenericInstanceMethod(m_MessagingSystem_ReceiveMessage_MethodRef);
|
var receiveMethod = new GenericInstanceMethod(m_MessageManager_ReceiveMessage_MethodRef);
|
||||||
receiveMethod.GenericArguments.Add(type);
|
receiveMethod.GenericArguments.Add(type);
|
||||||
var versionMethod = new GenericInstanceMethod(m_MessagingSystem_CreateMessageAndGetVersion_MethodRef);
|
var versionMethod = new GenericInstanceMethod(m_MessageManager_CreateMessageAndGetVersion_MethodRef);
|
||||||
versionMethod.GenericArguments.Add(type);
|
versionMethod.GenericArguments.Add(type);
|
||||||
CreateInstructionsToRegisterType(processor, instructions, type, receiveMethod, versionMethod);
|
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));
|
||||||
break;
|
|
||||||
}
|
assembly.MainModule.Types.Add(typeDefinition);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -53,6 +53,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
ProcessNetworkBehaviour(typeDefinition);
|
ProcessNetworkBehaviour(typeDefinition);
|
||||||
break;
|
break;
|
||||||
case nameof(__RpcParams):
|
case nameof(__RpcParams):
|
||||||
|
case nameof(RpcFallbackSerialization):
|
||||||
typeDefinition.IsPublic = true;
|
typeDefinition.IsPublic = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -79,6 +80,9 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
|
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Deprecate...
|
||||||
|
// This is changing accessibility for values that are no longer used, but since our validator runs
|
||||||
|
// after ILPP and sees those values as public, they cannot be removed until a major version change.
|
||||||
private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assemblyDefines)
|
private void ProcessNetworkManager(TypeDefinition typeDefinition, string[] assemblyDefines)
|
||||||
{
|
{
|
||||||
foreach (var fieldDefinition in typeDefinition.Fields)
|
foreach (var fieldDefinition in typeDefinition.Fields)
|
||||||
@@ -98,6 +102,14 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
fieldDefinition.IsPublic = true;
|
fieldDefinition.IsPublic = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var nestedTypeDefinition in typeDefinition.NestedTypes)
|
||||||
|
{
|
||||||
|
if (nestedTypeDefinition.Name == nameof(NetworkManager.RpcReceiveHandler))
|
||||||
|
{
|
||||||
|
nestedTypeDefinition.IsNestedPublic = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
|
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
|
||||||
@@ -108,13 +120,31 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
{
|
{
|
||||||
nestedType.IsNestedFamily = true;
|
nestedType.IsNestedFamily = true;
|
||||||
}
|
}
|
||||||
|
if (nestedType.Name == nameof(NetworkBehaviour.RpcReceiveHandler))
|
||||||
|
{
|
||||||
|
nestedType.IsNestedPublic = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var fieldDefinition in typeDefinition.Fields)
|
foreach (var fieldDefinition in typeDefinition.Fields)
|
||||||
{
|
{
|
||||||
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_exec_stage))
|
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_exec_stage) || fieldDefinition.Name == nameof(NetworkBehaviour.NetworkVariableFields))
|
||||||
{
|
{
|
||||||
fieldDefinition.IsFamily = true;
|
fieldDefinition.IsFamilyOrAssembly = true;
|
||||||
|
}
|
||||||
|
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_func_table))
|
||||||
|
{
|
||||||
|
fieldDefinition.IsFamilyOrAssembly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldDefinition.Name == nameof(NetworkBehaviour.RpcReceiveHandler))
|
||||||
|
{
|
||||||
|
fieldDefinition.IsFamilyOrAssembly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldDefinition.Name == nameof(NetworkBehaviour.__rpc_name_table))
|
||||||
|
{
|
||||||
|
fieldDefinition.IsFamilyOrAssembly = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,10 +153,20 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
if (methodDefinition.Name == nameof(NetworkBehaviour.__beginSendServerRpc) ||
|
if (methodDefinition.Name == nameof(NetworkBehaviour.__beginSendServerRpc) ||
|
||||||
methodDefinition.Name == nameof(NetworkBehaviour.__endSendServerRpc) ||
|
methodDefinition.Name == nameof(NetworkBehaviour.__endSendServerRpc) ||
|
||||||
methodDefinition.Name == nameof(NetworkBehaviour.__beginSendClientRpc) ||
|
methodDefinition.Name == nameof(NetworkBehaviour.__beginSendClientRpc) ||
|
||||||
methodDefinition.Name == nameof(NetworkBehaviour.__endSendClientRpc))
|
methodDefinition.Name == nameof(NetworkBehaviour.__endSendClientRpc) ||
|
||||||
|
methodDefinition.Name == nameof(NetworkBehaviour.__initializeVariables) ||
|
||||||
|
methodDefinition.Name == nameof(NetworkBehaviour.__initializeRpcs) ||
|
||||||
|
methodDefinition.Name == nameof(NetworkBehaviour.__registerRpc) ||
|
||||||
|
methodDefinition.Name == nameof(NetworkBehaviour.__nameNetworkVariable) ||
|
||||||
|
methodDefinition.Name == nameof(NetworkBehaviour.__createNativeList))
|
||||||
{
|
{
|
||||||
methodDefinition.IsFamily = true;
|
methodDefinition.IsFamily = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (methodDefinition.Name == nameof(NetworkBehaviour.__getTypeName))
|
||||||
|
{
|
||||||
|
methodDefinition.IsFamilyOrAssembly = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs
Normal file
30
Editor/Configuration/NetcodeForGameObjectsProjectSettings.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Editor.Configuration
|
||||||
|
{
|
||||||
|
[FilePath("ProjectSettings/NetcodeForGameObjects.asset", FilePathAttribute.Location.ProjectFolder)]
|
||||||
|
public class NetcodeForGameObjectsProjectSettings : ScriptableSingleton<NetcodeForGameObjectsProjectSettings>
|
||||||
|
{
|
||||||
|
internal static readonly string DefaultNetworkPrefabsPath = "Assets/DefaultNetworkPrefabs.asset";
|
||||||
|
[SerializeField] public string NetworkPrefabsPath = DefaultNetworkPrefabsPath;
|
||||||
|
public string TempNetworkPrefabsPath;
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
if (NetworkPrefabsPath == "")
|
||||||
|
{
|
||||||
|
NetworkPrefabsPath = DefaultNetworkPrefabsPath;
|
||||||
|
}
|
||||||
|
TempNetworkPrefabsPath = NetworkPrefabsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
public bool GenerateDefaultNetworkPrefabs = true;
|
||||||
|
|
||||||
|
internal void SaveSettings()
|
||||||
|
{
|
||||||
|
Save(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2727d53a542a4c1aa312905c3a02d807
|
||||||
|
timeCreated: 1685564945
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor.Configuration
|
namespace Unity.Netcode.Editor.Configuration
|
||||||
{
|
{
|
||||||
@@ -39,15 +37,4 @@ namespace Unity.Netcode.Editor.Configuration
|
|||||||
EditorPrefs.SetBool(AutoAddNetworkObjectIfNoneExists, autoAddSetting);
|
EditorPrefs.SetBool(AutoAddNetworkObjectIfNoneExists, autoAddSetting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[FilePath("ProjectSettings/NetcodeForGameObjects.settings", FilePathAttribute.Location.ProjectFolder)]
|
|
||||||
internal class NetcodeForGameObjectsProjectSettings : ScriptableSingleton<NetcodeForGameObjectsProjectSettings>
|
|
||||||
{
|
|
||||||
[SerializeField] public bool GenerateDefaultNetworkPrefabs = true;
|
|
||||||
|
|
||||||
internal void SaveSettings()
|
|
||||||
{
|
|
||||||
Save(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Directory = UnityEngine.Windows.Directory;
|
||||||
|
using File = UnityEngine.Windows.File;
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor.Configuration
|
namespace Unity.Netcode.Editor.Configuration
|
||||||
{
|
{
|
||||||
@@ -20,11 +24,60 @@ namespace Unity.Netcode.Editor.Configuration
|
|||||||
label = "Netcode for GameObjects",
|
label = "Netcode for GameObjects",
|
||||||
keywords = new[] { "netcode", "editor" },
|
keywords = new[] { "netcode", "editor" },
|
||||||
guiHandler = OnGuiHandler,
|
guiHandler = OnGuiHandler,
|
||||||
|
deactivateHandler = OnDeactivate
|
||||||
};
|
};
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void OnDeactivate()
|
||||||
|
{
|
||||||
|
var settings = NetcodeForGameObjectsProjectSettings.instance;
|
||||||
|
if (settings.TempNetworkPrefabsPath != settings.NetworkPrefabsPath)
|
||||||
|
{
|
||||||
|
var newPath = settings.TempNetworkPrefabsPath;
|
||||||
|
if (newPath == "")
|
||||||
|
{
|
||||||
|
newPath = NetcodeForGameObjectsProjectSettings.DefaultNetworkPrefabsPath;
|
||||||
|
settings.TempNetworkPrefabsPath = newPath;
|
||||||
|
}
|
||||||
|
var oldPath = settings.NetworkPrefabsPath;
|
||||||
|
settings.NetworkPrefabsPath = settings.TempNetworkPrefabsPath;
|
||||||
|
var dirName = Path.GetDirectoryName(newPath);
|
||||||
|
if (!Directory.Exists(dirName))
|
||||||
|
{
|
||||||
|
var dirs = dirName.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar });
|
||||||
|
var dirsQueue = new Queue<string>(dirs);
|
||||||
|
var parent = dirsQueue.Dequeue();
|
||||||
|
while (dirsQueue.Count != 0)
|
||||||
|
{
|
||||||
|
var child = dirsQueue.Dequeue();
|
||||||
|
var together = Path.Combine(parent, child);
|
||||||
|
if (!Directory.Exists(together))
|
||||||
|
{
|
||||||
|
AssetDatabase.CreateFolder(parent, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = together;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Directory.Exists(dirName))
|
||||||
|
{
|
||||||
|
if (File.Exists(oldPath))
|
||||||
|
{
|
||||||
|
AssetDatabase.MoveAsset(oldPath, newPath);
|
||||||
|
if (File.Exists(oldPath))
|
||||||
|
{
|
||||||
|
File.Delete(oldPath);
|
||||||
|
}
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.SaveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal static NetcodeSettingsLabel NetworkObjectsSectionLabel;
|
internal static NetcodeSettingsLabel NetworkObjectsSectionLabel;
|
||||||
internal static NetcodeSettingsToggle AutoAddNetworkObjectToggle;
|
internal static NetcodeSettingsToggle AutoAddNetworkObjectToggle;
|
||||||
@@ -70,6 +123,7 @@ namespace Unity.Netcode.Editor.Configuration
|
|||||||
var multiplayerToolsTipStatus = NetcodeForGameObjectsEditorSettings.GetNetcodeInstallMultiplayerToolTips() == 0;
|
var multiplayerToolsTipStatus = NetcodeForGameObjectsEditorSettings.GetNetcodeInstallMultiplayerToolTips() == 0;
|
||||||
var settings = NetcodeForGameObjectsProjectSettings.instance;
|
var settings = NetcodeForGameObjectsProjectSettings.instance;
|
||||||
var generateDefaultPrefabs = settings.GenerateDefaultNetworkPrefabs;
|
var generateDefaultPrefabs = settings.GenerateDefaultNetworkPrefabs;
|
||||||
|
var networkPrefabsPath = settings.TempNetworkPrefabsPath;
|
||||||
|
|
||||||
EditorGUI.BeginChangeCheck();
|
EditorGUI.BeginChangeCheck();
|
||||||
|
|
||||||
@@ -97,6 +151,7 @@ namespace Unity.Netcode.Editor.Configuration
|
|||||||
{
|
{
|
||||||
GUILayout.BeginVertical("Box");
|
GUILayout.BeginVertical("Box");
|
||||||
const string generateNetworkPrefabsString = "Generate Default Network Prefabs List";
|
const string generateNetworkPrefabsString = "Generate Default Network Prefabs List";
|
||||||
|
const string networkPrefabsLocationString = "Default Network Prefabs List path";
|
||||||
|
|
||||||
if (s_MaxLabelWidth == 0)
|
if (s_MaxLabelWidth == 0)
|
||||||
{
|
{
|
||||||
@@ -114,6 +169,14 @@ namespace Unity.Netcode.Editor.Configuration
|
|||||||
"to date with all NetworkObject prefabs."),
|
"to date with all NetworkObject prefabs."),
|
||||||
generateDefaultPrefabs,
|
generateDefaultPrefabs,
|
||||||
GUILayout.Width(s_MaxLabelWidth + 20));
|
GUILayout.Width(s_MaxLabelWidth + 20));
|
||||||
|
|
||||||
|
GUI.SetNextControlName("Location");
|
||||||
|
networkPrefabsPath = EditorGUILayout.TextField(
|
||||||
|
new GUIContent(
|
||||||
|
networkPrefabsLocationString,
|
||||||
|
"The path to the asset the default NetworkPrefabList object should be stored in."),
|
||||||
|
networkPrefabsPath,
|
||||||
|
GUILayout.Width(s_MaxLabelWidth + 270));
|
||||||
GUILayout.EndVertical();
|
GUILayout.EndVertical();
|
||||||
}
|
}
|
||||||
EditorGUILayout.EndFoldoutHeaderGroup();
|
EditorGUILayout.EndFoldoutHeaderGroup();
|
||||||
@@ -123,6 +186,7 @@ namespace Unity.Netcode.Editor.Configuration
|
|||||||
NetcodeForGameObjectsEditorSettings.SetAutoAddNetworkObjectSetting(autoAddNetworkObjectSetting);
|
NetcodeForGameObjectsEditorSettings.SetAutoAddNetworkObjectSetting(autoAddNetworkObjectSetting);
|
||||||
NetcodeForGameObjectsEditorSettings.SetNetcodeInstallMultiplayerToolTips(multiplayerToolsTipStatus ? 0 : 1);
|
NetcodeForGameObjectsEditorSettings.SetNetcodeInstallMultiplayerToolTips(multiplayerToolsTipStatus ? 0 : 1);
|
||||||
settings.GenerateDefaultNetworkPrefabs = generateDefaultPrefabs;
|
settings.GenerateDefaultNetworkPrefabs = generateDefaultPrefabs;
|
||||||
|
settings.TempNetworkPrefabsPath = networkPrefabsPath;
|
||||||
settings.SaveSettings();
|
settings.SaveSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,15 @@ namespace Unity.Netcode.Editor.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NetworkPrefabProcessor : AssetPostprocessor
|
public class NetworkPrefabProcessor : AssetPostprocessor
|
||||||
{
|
{
|
||||||
private static string s_DefaultNetworkPrefabsPath = "Assets/DefaultNetworkPrefabs.asset";
|
|
||||||
public static string DefaultNetworkPrefabsPath
|
public static string DefaultNetworkPrefabsPath
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return s_DefaultNetworkPrefabsPath;
|
return NetcodeForGameObjectsProjectSettings.instance.NetworkPrefabsPath;
|
||||||
}
|
}
|
||||||
internal set
|
internal set
|
||||||
{
|
{
|
||||||
s_DefaultNetworkPrefabsPath = value;
|
NetcodeForGameObjectsProjectSettings.instance.NetworkPrefabsPath = value;
|
||||||
// Force a recache of the prefab list
|
// Force a recache of the prefab list
|
||||||
s_PrefabsList = null;
|
s_PrefabsList = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,17 +37,15 @@ namespace Unity.Netcode.Editor
|
|||||||
for (int i = 0; i < fields.Length; i++)
|
for (int i = 0; i < fields.Length; i++)
|
||||||
{
|
{
|
||||||
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) && !fields[i].IsDefined(typeof(NonSerializedAttribute), true))
|
||||||
{
|
{
|
||||||
m_NetworkVariableNames.Add(ObjectNames.NicifyVariableName(fields[i].Name));
|
m_NetworkVariableNames.Add(ObjectNames.NicifyVariableName(fields[i].Name));
|
||||||
m_NetworkVariableFields.Add(ObjectNames.NicifyVariableName(fields[i].Name), fields[i]);
|
m_NetworkVariableFields.Add(ObjectNames.NicifyVariableName(fields[i].Name), fields[i]);
|
||||||
Debug.Log($"Adding NetworkVariable {fields[i].Name}");
|
|
||||||
}
|
}
|
||||||
if (ft.IsGenericType && ft.GetGenericTypeDefinition() == typeof(NetworkList<>) && !fields[i].IsDefined(typeof(HideInInspector), true))
|
if (ft.IsGenericType && ft.GetGenericTypeDefinition() == typeof(NetworkList<>) && !fields[i].IsDefined(typeof(HideInInspector), true) && !fields[i].IsDefined(typeof(NonSerializedAttribute), true))
|
||||||
{
|
{
|
||||||
m_NetworkVariableNames.Add(ObjectNames.NicifyVariableName(fields[i].Name));
|
m_NetworkVariableNames.Add(ObjectNames.NicifyVariableName(fields[i].Name));
|
||||||
m_NetworkVariableFields.Add(ObjectNames.NicifyVariableName(fields[i].Name), fields[i]);
|
m_NetworkVariableFields.Add(ObjectNames.NicifyVariableName(fields[i].Name), fields[i]);
|
||||||
Debug.Log($"Adding NetworkList {fields[i].Name}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +79,25 @@ namespace Unity.Netcode.Editor
|
|||||||
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.BeginHorizontal();
|
||||||
if (genericType.IsValueType)
|
if (genericType.IsValueType)
|
||||||
{
|
{
|
||||||
var method = typeof(NetworkBehaviourEditor).GetMethod("RenderNetworkContainerValueType", BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic);
|
var isEquatable = false;
|
||||||
|
foreach (var iface in genericType.GetInterfaces())
|
||||||
|
{
|
||||||
|
if (iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IEquatable<>))
|
||||||
|
{
|
||||||
|
isEquatable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodInfo method;
|
||||||
|
if (isEquatable)
|
||||||
|
{
|
||||||
|
method = typeof(NetworkBehaviourEditor).GetMethod(nameof(RenderNetworkContainerValueTypeIEquatable), BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
method = typeof(NetworkBehaviourEditor).GetMethod(nameof(RenderNetworkContainerValueType), BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic);
|
||||||
|
}
|
||||||
|
|
||||||
var genericMethod = method.MakeGenericMethod(genericType);
|
var genericMethod = method.MakeGenericMethod(genericType);
|
||||||
genericMethod.Invoke(this, new[] { (object)index });
|
genericMethod.Invoke(this, new[] { (object)index });
|
||||||
}
|
}
|
||||||
@@ -94,7 +110,23 @@ namespace Unity.Netcode.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderNetworkContainerValueType<T>(int index) where T : unmanaged, IEquatable<T>
|
private void RenderNetworkContainerValueType<T>(int index) where T : unmanaged
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var networkVariable = (NetworkVariable<T>)m_NetworkVariableFields[m_NetworkVariableNames[index]].GetValue(target);
|
||||||
|
RenderNetworkVariableValueType(index, networkVariable);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Log(e);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderNetworkContainerValueTypeIEquatable<T>(int index) where T : unmanaged, IEquatable<T>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -240,7 +272,7 @@ namespace Unity.Netcode.Editor
|
|||||||
bool expanded = true;
|
bool expanded = true;
|
||||||
while (property.NextVisible(expanded))
|
while (property.NextVisible(expanded))
|
||||||
{
|
{
|
||||||
if (m_NetworkVariableNames.Contains(property.name))
|
if (m_NetworkVariableNames.Contains(ObjectNames.NicifyVariableName(property.name)))
|
||||||
{
|
{
|
||||||
// Skip rendering of NetworkVars, they have special rendering
|
// Skip rendering of NetworkVars, they have special rendering
|
||||||
continue;
|
continue;
|
||||||
@@ -313,8 +345,9 @@ namespace Unity.Netcode.Editor
|
|||||||
/// <param name="networkObjectRemoved">used internally</param>
|
/// <param name="networkObjectRemoved">used internally</param>
|
||||||
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
|
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
|
||||||
{
|
{
|
||||||
// If there are no NetworkBehaviours or no gameObject, then exit early
|
// If there are no NetworkBehaviours or gameObjects then exit early
|
||||||
if (gameObject == null || (gameObject.GetComponent<NetworkBehaviour>() == null && gameObject.GetComponentInChildren<NetworkBehaviour>() == null))
|
// If we are in play mode and a user is inspecting something then exit early (we don't add NetworkObjects to something when in play mode)
|
||||||
|
if (EditorApplication.isPlaying || gameObject == null || (gameObject.GetComponent<NetworkBehaviour>() == null && gameObject.GetComponentInChildren<NetworkBehaviour>() == null))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -384,6 +417,48 @@ namespace Unity.Netcode.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (networkObject != null)
|
||||||
|
{
|
||||||
|
OrderNetworkObject(networkObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assures the NetworkObject precedes any NetworkBehaviour on the same GameObject as the NetworkObject
|
||||||
|
private static void OrderNetworkObject(NetworkObject networkObject)
|
||||||
|
{
|
||||||
|
var monoBehaviours = networkObject.gameObject.GetComponents<MonoBehaviour>();
|
||||||
|
var networkObjectIndex = 0;
|
||||||
|
var firstNetworkBehaviourIndex = -1;
|
||||||
|
for (int i = 0; i < monoBehaviours.Length; i++)
|
||||||
|
{
|
||||||
|
if (monoBehaviours[i] == networkObject)
|
||||||
|
{
|
||||||
|
networkObjectIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var networkBehaviour = monoBehaviours[i] as NetworkBehaviour;
|
||||||
|
if (networkBehaviour != null)
|
||||||
|
{
|
||||||
|
// Get the index of the first NetworkBehaviour Component
|
||||||
|
if (firstNetworkBehaviourIndex == -1)
|
||||||
|
{
|
||||||
|
firstNetworkBehaviourIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstNetworkBehaviourIndex != -1 && networkObjectIndex > firstNetworkBehaviourIndex)
|
||||||
|
{
|
||||||
|
var positionsToMove = networkObjectIndex - firstNetworkBehaviourIndex;
|
||||||
|
for (int i = 0; i < positionsToMove; i++)
|
||||||
|
{
|
||||||
|
UnityEditorInternal.ComponentUtility.MoveComponentUp(networkObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorUtility.SetDirty(networkObject.gameObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ namespace Unity.Netcode.Editor
|
|||||||
const string getToolsText = "Access additional tools for multiplayer development by installing the Multiplayer Tools package in the Package Manager.";
|
const string getToolsText = "Access additional tools for multiplayer development by installing the Multiplayer Tools package in the Package Manager.";
|
||||||
const string openDocsButtonText = "Open Docs";
|
const string openDocsButtonText = "Open Docs";
|
||||||
const string dismissButtonText = "Dismiss";
|
const string dismissButtonText = "Dismiss";
|
||||||
const string targetUrl = "https://docs-multiplayer.unity3d.com/netcode/current/tools/install-tools";
|
const string targetUrl = "https://docs-multiplayer.unity3d.com/tools/current/install-tools";
|
||||||
const string infoIconName = "console.infoicon";
|
const string infoIconName = "console.infoicon";
|
||||||
|
|
||||||
if (NetcodeForGameObjectsEditorSettings.GetNetcodeInstallMultiplayerToolTips() != 0)
|
if (NetcodeForGameObjectsEditorSettings.GetNetcodeInstallMultiplayerToolTips() != 0)
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ namespace Unity.Netcode
|
|||||||
public string ToBase64()
|
public string ToBase64()
|
||||||
{
|
{
|
||||||
NetworkConfig config = this;
|
NetworkConfig config = this;
|
||||||
var writer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp);
|
var writer = new FastBufferWriter(1024, Allocator.Temp);
|
||||||
using (writer)
|
using (writer)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(config.ProtocolVersion);
|
writer.WriteValueSafe(config.ProtocolVersion);
|
||||||
@@ -228,7 +228,7 @@ namespace Unity.Netcode
|
|||||||
return m_ConfigHash.Value;
|
return m_ConfigHash.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var writer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, int.MaxValue);
|
var writer = new FastBufferWriter(1024, Allocator.Temp, int.MaxValue);
|
||||||
using (writer)
|
using (writer)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(ProtocolVersion);
|
writer.WriteValueSafe(ProtocolVersion);
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ namespace Unity.Netcode
|
|||||||
/// No oeverride is present
|
/// No oeverride is present
|
||||||
/// </summary>
|
/// </summary>
|
||||||
None,
|
None,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Override the prefab when the given SourcePrefabToOverride is requested
|
/// Override the prefab when the given SourcePrefabToOverride is requested
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Prefab,
|
Prefab,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Override the prefab when the given SourceHashToOverride is requested
|
/// Override the prefab when the given SourceHashToOverride is requested
|
||||||
/// Used in situations where the server assets do not exist in client builds
|
/// Used in situations where the server assets do not exist in client builds
|
||||||
@@ -71,19 +73,23 @@ namespace Unity.Netcode
|
|||||||
switch (Override)
|
switch (Override)
|
||||||
{
|
{
|
||||||
case NetworkPrefabOverride.None:
|
case NetworkPrefabOverride.None:
|
||||||
if (Prefab != null && Prefab.TryGetComponent(out NetworkObject no))
|
|
||||||
{
|
{
|
||||||
return no.GlobalObjectIdHash;
|
if (Prefab != null && Prefab.TryGetComponent(out NetworkObject networkObject))
|
||||||
|
{
|
||||||
|
return networkObject.GlobalObjectIdHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Prefab field isn't set or isn't a Network Object");
|
throw new InvalidOperationException($"Prefab field is not set or is not a {nameof(NetworkObject)}");
|
||||||
|
}
|
||||||
case NetworkPrefabOverride.Prefab:
|
case NetworkPrefabOverride.Prefab:
|
||||||
if (SourcePrefabToOverride != null && SourcePrefabToOverride.TryGetComponent(out no))
|
|
||||||
{
|
{
|
||||||
return no.GlobalObjectIdHash;
|
if (SourcePrefabToOverride != null && SourcePrefabToOverride.TryGetComponent(out NetworkObject networkObject))
|
||||||
|
{
|
||||||
|
return networkObject.GlobalObjectIdHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Source Prefab field isn't set or isn't a Network Object");
|
throw new InvalidOperationException($"Source Prefab field is not set or is not a {nameof(NetworkObject)}");
|
||||||
|
}
|
||||||
case NetworkPrefabOverride.Hash:
|
case NetworkPrefabOverride.Hash:
|
||||||
return SourceHashToOverride;
|
return SourceHashToOverride;
|
||||||
default:
|
default:
|
||||||
@@ -102,12 +108,14 @@ namespace Unity.Netcode
|
|||||||
return 0;
|
return 0;
|
||||||
case NetworkPrefabOverride.Prefab:
|
case NetworkPrefabOverride.Prefab:
|
||||||
case NetworkPrefabOverride.Hash:
|
case NetworkPrefabOverride.Hash:
|
||||||
if (OverridingTargetPrefab != null && OverridingTargetPrefab.TryGetComponent(out NetworkObject no))
|
|
||||||
{
|
{
|
||||||
return no.GlobalObjectIdHash;
|
if (OverridingTargetPrefab != null && OverridingTargetPrefab.TryGetComponent(out NetworkObject networkObject))
|
||||||
|
{
|
||||||
|
return networkObject.GlobalObjectIdHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Target Prefab field isn't set or isn't a Network Object");
|
throw new InvalidOperationException($"Target Prefab field is not set or is not a {nameof(NetworkObject)}");
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
@@ -130,9 +138,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"{NetworkManager.PrefabDebugHelper(this)} is missing " +
|
NetworkLog.LogWarning($"{NetworkPrefabHandler.PrefabDebugHelper(this)} is missing a {nameof(NetworkObject)} component (entry will be ignored).");
|
||||||
$"a {nameof(NetworkObject)} component (entry will be ignored).");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,9 +156,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"{nameof(NetworkPrefab)} {nameof(SourceHashToOverride)} is zero " +
|
NetworkLog.LogWarning($"{nameof(NetworkPrefab)} {nameof(SourceHashToOverride)} is zero (entry will be ignored).");
|
||||||
"(entry will be ignored).");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,9 +186,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"{nameof(NetworkPrefab)} ({SourcePrefabToOverride.name}) " +
|
NetworkLog.LogWarning($"{nameof(NetworkPrefab)} ({SourcePrefabToOverride.name}) is missing a {nameof(NetworkObject)} component (entry will be ignored).");
|
||||||
$"is missing a {nameof(NetworkObject)} component (entry will be ignored).");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,6 +203,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"{nameof(NetworkPrefab)} {nameof(OverridingTargetPrefab)} is null!");
|
NetworkLog.LogWarning($"{nameof(NetworkPrefab)} {nameof(OverridingTargetPrefab)} is null!");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (Override)
|
switch (Override)
|
||||||
{
|
{
|
||||||
case NetworkPrefabOverride.Hash:
|
case NetworkPrefabOverride.Hash:
|
||||||
@@ -208,8 +217,10 @@ namespace Unity.Netcode
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,10 +38,15 @@ namespace Unity.Netcode
|
|||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
private List<NetworkPrefab> m_Prefabs = new List<NetworkPrefab>();
|
private List<NetworkPrefab> m_Prefabs = new List<NetworkPrefab>();
|
||||||
|
|
||||||
|
[NonSerialized]
|
||||||
|
private List<NetworkPrefab> m_RuntimeAddedPrefabs = new List<NetworkPrefab>();
|
||||||
|
|
||||||
private void AddTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
|
private void AddTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
|
||||||
{
|
{
|
||||||
if (AddPrefabRegistration(networkPrefab))
|
if (AddPrefabRegistration(networkPrefab))
|
||||||
{
|
{
|
||||||
|
// Don't add this to m_RuntimeAddedPrefabs
|
||||||
|
// This prefab is now in the PrefabList, so if we shutdown and initialize again, we'll pick it up from there.
|
||||||
m_Prefabs.Add(networkPrefab);
|
m_Prefabs.Add(networkPrefab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,8 +72,6 @@ namespace Unity.Netcode
|
|||||||
list.OnAdd -= AddTriggeredByNetworkPrefabList;
|
list.OnAdd -= AddTriggeredByNetworkPrefabList;
|
||||||
list.OnRemove -= RemoveTriggeredByNetworkPrefabList;
|
list.OnRemove -= RemoveTriggeredByNetworkPrefabList;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkPrefabsLists.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -77,13 +80,7 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Initialize(bool warnInvalid = true)
|
public void Initialize(bool warnInvalid = true)
|
||||||
{
|
{
|
||||||
if (NetworkPrefabsLists.Count != 0 && m_Prefabs.Count > 0)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning("Runtime Network Prefabs was not empty at initialization time. Network " +
|
|
||||||
"Prefab registrations made before initialization will be replaced by NetworkPrefabsList.");
|
|
||||||
m_Prefabs.Clear();
|
m_Prefabs.Clear();
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var list in NetworkPrefabsLists)
|
foreach (var list in NetworkPrefabsLists)
|
||||||
{
|
{
|
||||||
list.OnAdd += AddTriggeredByNetworkPrefabList;
|
list.OnAdd += AddTriggeredByNetworkPrefabList;
|
||||||
@@ -93,7 +90,7 @@ namespace Unity.Netcode
|
|||||||
NetworkPrefabOverrideLinks.Clear();
|
NetworkPrefabOverrideLinks.Clear();
|
||||||
OverrideToNetworkPrefab.Clear();
|
OverrideToNetworkPrefab.Clear();
|
||||||
|
|
||||||
var prefabs = NetworkPrefabsLists.Count != 0 ? new List<NetworkPrefab>() : m_Prefabs;
|
var prefabs = new List<NetworkPrefab>();
|
||||||
|
|
||||||
if (NetworkPrefabsLists.Count != 0)
|
if (NetworkPrefabsLists.Count != 0)
|
||||||
{
|
{
|
||||||
@@ -126,6 +123,18 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var networkPrefab in m_RuntimeAddedPrefabs)
|
||||||
|
{
|
||||||
|
if (AddPrefabRegistration(networkPrefab))
|
||||||
|
{
|
||||||
|
m_Prefabs.Add(networkPrefab);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
removeList?.Add(networkPrefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clear out anything that is invalid or not used
|
// Clear out anything that is invalid or not used
|
||||||
if (removeList?.Count > 0)
|
if (removeList?.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -152,6 +161,7 @@ namespace Unity.Netcode
|
|||||||
if (AddPrefabRegistration(networkPrefab))
|
if (AddPrefabRegistration(networkPrefab))
|
||||||
{
|
{
|
||||||
m_Prefabs.Add(networkPrefab);
|
m_Prefabs.Add(networkPrefab);
|
||||||
|
m_RuntimeAddedPrefabs.Add(networkPrefab);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,6 +185,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_Prefabs.Remove(prefab);
|
m_Prefabs.Remove(prefab);
|
||||||
|
m_RuntimeAddedPrefabs.Remove(prefab);
|
||||||
OverrideToNetworkPrefab.Remove(prefab.TargetPrefabGlobalObjectIdHash);
|
OverrideToNetworkPrefab.Remove(prefab.TargetPrefabGlobalObjectIdHash);
|
||||||
NetworkPrefabOverrideLinks.Remove(prefab.SourcePrefabGlobalObjectIdHash);
|
NetworkPrefabOverrideLinks.Remove(prefab.SourcePrefabGlobalObjectIdHash);
|
||||||
}
|
}
|
||||||
@@ -203,6 +214,15 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_RuntimeAddedPrefabs.Count; i++)
|
||||||
|
{
|
||||||
|
if (m_RuntimeAddedPrefabs[i].Prefab == prefab)
|
||||||
|
{
|
||||||
|
Remove(m_RuntimeAddedPrefabs[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -7,6 +7,32 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NetworkClient
|
public class NetworkClient
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the session instance is considered a server
|
||||||
|
/// </summary>
|
||||||
|
internal bool IsServer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the session instance is considered a client
|
||||||
|
/// </summary>
|
||||||
|
internal bool IsClient { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the session instance is considered a host
|
||||||
|
/// </summary>
|
||||||
|
internal bool IsHost => IsClient && IsServer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When true, the client is connected, approved, and synchronized with
|
||||||
|
/// the server.
|
||||||
|
/// </summary>
|
||||||
|
internal bool IsConnected { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is true when the client has been approved.
|
||||||
|
/// </summary>
|
||||||
|
internal bool IsApproved { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ClientId of the NetworkClient
|
/// The ClientId of the NetworkClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -18,19 +44,33 @@ namespace Unity.Netcode
|
|||||||
public NetworkObject PlayerObject;
|
public NetworkObject PlayerObject;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The NetworkObject's owned by this Client
|
/// The list of NetworkObject's owned by this client instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<NetworkObject> OwnedObjects
|
public List<NetworkObject> OwnedObjects => IsConnected ? SpawnManager.GetClientOwnedObjects(ClientId) : new List<NetworkObject>();
|
||||||
|
|
||||||
|
internal NetworkSpawnManager SpawnManager { get; private set; }
|
||||||
|
|
||||||
|
internal void SetRole(bool isServer, bool isClient, NetworkManager networkManager = null)
|
||||||
{
|
{
|
||||||
get
|
IsServer = isServer;
|
||||||
|
IsClient = isClient;
|
||||||
|
if (!IsServer && !isClient)
|
||||||
{
|
{
|
||||||
if (PlayerObject != null && PlayerObject.NetworkManager != null && PlayerObject.NetworkManager.IsListening)
|
PlayerObject = null;
|
||||||
{
|
ClientId = 0;
|
||||||
return PlayerObject.NetworkManager.SpawnManager.GetClientOwnedObjects(ClientId);
|
IsConnected = false;
|
||||||
|
IsApproved = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new List<NetworkObject>();
|
if (networkManager != null)
|
||||||
}
|
{
|
||||||
|
SpawnManager = networkManager.SpawnManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AssignPlayerObject(ref NetworkObject networkObject)
|
||||||
|
{
|
||||||
|
PlayerObject = networkObject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1109
Runtime/Connection/NetworkConnectionManager.cs
Normal file
1109
Runtime/Connection/NetworkConnectionManager.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Runtime/Connection/NetworkConnectionManager.cs.meta
Normal file
11
Runtime/Connection/NetworkConnectionManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d98eff74d73bc2a42bd5624c47ce8fe1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Server-Side Only:
|
||||||
/// A class representing a client that is currently in the process of connecting
|
/// A class representing a client that is currently in the process of connecting
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PendingClient
|
public class PendingClient
|
||||||
{
|
{
|
||||||
|
internal Coroutine ApprovalCoroutine = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ClientId of the client
|
/// The ClientId of the client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void SetDefaults()
|
public static void SetDefaults()
|
||||||
{
|
{
|
||||||
SetDefault<IDeferredMessageManager>(networkManager => new DeferredMessageManager(networkManager));
|
SetDefault<IDeferredNetworkMessageManager>(networkManager => new DeferredMessageManager(networkManager));
|
||||||
|
|
||||||
SetDefault<IRealTimeProvider>(networkManager => new RealTimeProvider());
|
SetDefault<IRealTimeProvider>(networkManager => new RealTimeProvider());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -13,6 +12,18 @@ namespace Unity.Netcode
|
|||||||
public abstract class NetworkBehaviour : MonoBehaviour
|
public abstract class NetworkBehaviour : MonoBehaviour
|
||||||
{
|
{
|
||||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
|
|
||||||
|
// RuntimeAccessModifiersILPP will make this `public`
|
||||||
|
internal delegate void RpcReceiveHandler(NetworkBehaviour behaviour, FastBufferReader reader, __RpcParams parameters);
|
||||||
|
|
||||||
|
// RuntimeAccessModifiersILPP will make this `public`
|
||||||
|
internal static readonly Dictionary<Type, Dictionary<uint, RpcReceiveHandler>> __rpc_func_table = new Dictionary<Type, Dictionary<uint, RpcReceiveHandler>>();
|
||||||
|
|
||||||
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
|
// RuntimeAccessModifiersILPP will make this `public`
|
||||||
|
internal static readonly Dictionary<Type, Dictionary<uint, string>> __rpc_name_table = new Dictionary<Type, Dictionary<uint, string>>();
|
||||||
|
#endif
|
||||||
|
|
||||||
// RuntimeAccessModifiersILPP will make this `protected`
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
internal enum __RpcExecStage
|
internal enum __RpcExecStage
|
||||||
{
|
{
|
||||||
@@ -20,8 +31,6 @@ namespace Unity.Netcode
|
|||||||
Server = 1,
|
Server = 1,
|
||||||
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);
|
||||||
|
|
||||||
@@ -65,7 +74,7 @@ namespace Unity.Netcode
|
|||||||
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
||||||
break;
|
break;
|
||||||
case RpcDelivery.Unreliable:
|
case RpcDelivery.Unreliable:
|
||||||
if (bufferWriter.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE)
|
if (bufferWriter.Length > NetworkManager.MessageManager.NonFragmentedMessageMaxSize)
|
||||||
{
|
{
|
||||||
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
||||||
}
|
}
|
||||||
@@ -86,7 +95,7 @@ namespace Unity.Netcode
|
|||||||
SystemOwner = NetworkManager,
|
SystemOwner = NetworkManager,
|
||||||
// header information isn't valid since it's not a real message.
|
// header information isn't valid since it's not a real message.
|
||||||
// RpcMessage doesn't access this stuff so it's just left empty.
|
// RpcMessage doesn't access this stuff so it's just left empty.
|
||||||
Header = new MessageHeader(),
|
Header = new NetworkMessageHeader(),
|
||||||
SerializedHeaderSize = 0,
|
SerializedHeaderSize = 0,
|
||||||
MessageSize = 0
|
MessageSize = 0
|
||||||
};
|
};
|
||||||
@@ -96,13 +105,12 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rpcWriteSize = NetworkManager.SendMessage(ref serverRpcMessage, networkDelivery, NetworkManager.ServerClientId);
|
rpcWriteSize = NetworkManager.ConnectionManager.SendMessage(ref serverRpcMessage, networkDelivery, NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferWriter.Dispose();
|
bufferWriter.Dispose();
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName))
|
||||||
{
|
{
|
||||||
NetworkManager.NetworkMetrics.TrackRpcSent(
|
NetworkManager.NetworkMetrics.TrackRpcSent(
|
||||||
NetworkManager.ServerClientId,
|
NetworkManager.ServerClientId,
|
||||||
@@ -146,7 +154,7 @@ namespace Unity.Netcode
|
|||||||
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
||||||
break;
|
break;
|
||||||
case RpcDelivery.Unreliable:
|
case RpcDelivery.Unreliable:
|
||||||
if (bufferWriter.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE)
|
if (bufferWriter.Length > NetworkManager.MessageManager.NonFragmentedMessageMaxSize)
|
||||||
{
|
{
|
||||||
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
||||||
}
|
}
|
||||||
@@ -176,7 +184,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds);
|
rpcWriteSize = NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds);
|
||||||
}
|
}
|
||||||
else if (clientRpcParams.Send.TargetClientIdsNativeArray != null)
|
else if (clientRpcParams.Send.TargetClientIdsNativeArray != null)
|
||||||
{
|
{
|
||||||
@@ -195,7 +203,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value);
|
rpcWriteSize = NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -208,7 +216,7 @@ namespace Unity.Netcode
|
|||||||
shouldSendToHost = true;
|
shouldSendToHost = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rpcWriteSize = NetworkManager.MessagingSystem.SendMessage(ref clientRpcMessage, networkDelivery, observerEnumerator.Current);
|
rpcWriteSize = NetworkManager.ConnectionManager.SendMessage(ref clientRpcMessage, networkDelivery, observerEnumerator.Current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +231,7 @@ namespace Unity.Netcode
|
|||||||
SystemOwner = NetworkManager,
|
SystemOwner = NetworkManager,
|
||||||
// header information isn't valid since it's not a real message.
|
// header information isn't valid since it's not a real message.
|
||||||
// RpcMessage doesn't access this stuff so it's just left empty.
|
// RpcMessage doesn't access this stuff so it's just left empty.
|
||||||
Header = new MessageHeader(),
|
Header = new NetworkMessageHeader(),
|
||||||
SerializedHeaderSize = 0,
|
SerializedHeaderSize = 0,
|
||||||
MessageSize = 0
|
MessageSize = 0
|
||||||
};
|
};
|
||||||
@@ -232,9 +240,8 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
bufferWriter.Dispose();
|
bufferWriter.Dispose();
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
if (__rpc_name_table[GetType()].TryGetValue(rpcMethodId, out var rpcMethodName))
|
||||||
{
|
{
|
||||||
if (clientRpcParams.Send.TargetClientIds != null)
|
if (clientRpcParams.Send.TargetClientIds != null)
|
||||||
{
|
{
|
||||||
@@ -277,6 +284,14 @@ namespace Unity.Netcode
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
|
internal static NativeList<T> __createNativeList<T>() where T : unmanaged
|
||||||
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
|
{
|
||||||
|
return new NativeList<T>(Allocator.Temp);
|
||||||
|
}
|
||||||
|
|
||||||
internal string GenerateObserverErrorMessage(ClientRpcParams clientRpcParams, ulong targetClientId)
|
internal string GenerateObserverErrorMessage(ClientRpcParams clientRpcParams, ulong targetClientId)
|
||||||
{
|
{
|
||||||
var containerNameHoldingId = clientRpcParams.Send.TargetClientIds != null ? nameof(ClientRpcParams.Send.TargetClientIds) : nameof(ClientRpcParams.Send.TargetClientIdsNativeArray);
|
var containerNameHoldingId = clientRpcParams.Send.TargetClientIds != null ? nameof(ClientRpcParams.Send.TargetClientIds) : nameof(ClientRpcParams.Send.TargetClientIdsNativeArray);
|
||||||
@@ -530,6 +545,23 @@ namespace Unity.Netcode
|
|||||||
OnGainedOwnership();
|
OnGainedOwnership();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked on all clients, override this method to be notified of any
|
||||||
|
/// ownership changes (even if the instance was niether the previous or
|
||||||
|
/// newly assigned current owner).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="previous">the previous owner</param>
|
||||||
|
/// <param name="current">the current owner</param>
|
||||||
|
protected virtual void OnOwnershipChanged(ulong previous, ulong current)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void InternalOnOwnershipChanged(ulong previous, ulong current)
|
||||||
|
{
|
||||||
|
OnOwnershipChanged(previous, current);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when we loose ownership of this object
|
/// Gets called when we loose ownership of this object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -551,35 +583,44 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private readonly List<HashSet<int>> m_DeliveryMappedNetworkVariableIndices = new List<HashSet<int>>();
|
private readonly List<HashSet<int>> m_DeliveryMappedNetworkVariableIndices = new List<HashSet<int>>();
|
||||||
private readonly List<NetworkDelivery> m_DeliveryTypesForNetworkVariableGroups = new List<NetworkDelivery>();
|
private readonly List<NetworkDelivery> m_DeliveryTypesForNetworkVariableGroups = new List<NetworkDelivery>();
|
||||||
|
|
||||||
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
internal readonly List<NetworkVariableBase> NetworkVariableFields = new List<NetworkVariableBase>();
|
internal readonly List<NetworkVariableBase> NetworkVariableFields = new List<NetworkVariableBase>();
|
||||||
|
|
||||||
private static Dictionary<Type, FieldInfo[]> s_FieldTypes = new Dictionary<Type, FieldInfo[]>();
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
private static FieldInfo[] GetFieldInfoForType(Type type)
|
internal virtual void __initializeVariables()
|
||||||
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
{
|
{
|
||||||
if (!s_FieldTypes.ContainsKey(type))
|
// ILPP generates code for all NetworkBehaviour subtypes to initialize each type's network variables.
|
||||||
{
|
|
||||||
s_FieldTypes.Add(type, GetFieldInfoForTypeRecursive(type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s_FieldTypes[type];
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
|
internal virtual void __initializeRpcs()
|
||||||
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
|
{
|
||||||
|
// ILPP generates code for all NetworkBehaviour subtypes to initialize each type's RPCs.
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FieldInfo[] GetFieldInfoForTypeRecursive(Type type, List<FieldInfo> list = null)
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
|
internal void __registerRpc(uint hash, RpcReceiveHandler handler, string rpcMethodName)
|
||||||
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
{
|
{
|
||||||
if (list == null)
|
__rpc_func_table[GetType()][hash] = handler;
|
||||||
{
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
list = new List<FieldInfo>();
|
__rpc_name_table[GetType()][hash] = rpcMethodName;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
list.AddRange(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly));
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
if (type.BaseType != null && type.BaseType != typeof(NetworkBehaviour))
|
// Using this method here because ILPP doesn't seem to let us do visibility modification on properties.
|
||||||
|
internal void __nameNetworkVariable(NetworkVariableBase variable, string varName)
|
||||||
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
{
|
{
|
||||||
return GetFieldInfoForTypeRecursive(type.BaseType, list);
|
variable.Name = varName;
|
||||||
}
|
|
||||||
|
|
||||||
return list.OrderBy(x => x.Name, StringComparer.Ordinal).ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitializeVariables()
|
internal void InitializeVariables()
|
||||||
@@ -591,22 +632,15 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
m_VarInit = true;
|
m_VarInit = true;
|
||||||
|
|
||||||
var sortedFields = GetFieldInfoForType(GetType());
|
if (!__rpc_func_table.ContainsKey(GetType()))
|
||||||
for (int i = 0; i < sortedFields.Length; i++)
|
|
||||||
{
|
{
|
||||||
var fieldType = sortedFields[i].FieldType;
|
__rpc_func_table[GetType()] = new Dictionary<uint, RpcReceiveHandler>();
|
||||||
if (fieldType.IsSubclassOf(typeof(NetworkVariableBase)))
|
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||||
{
|
__rpc_name_table[GetType()] = new Dictionary<uint, string>();
|
||||||
var instance = (NetworkVariableBase)sortedFields[i].GetValue(this) ?? throw new Exception($"{GetType().FullName}.{sortedFields[i].Name} cannot be null. All {nameof(NetworkVariableBase)} instances must be initialized.");
|
#endif
|
||||||
instance.Initialize(this);
|
__initializeRpcs();
|
||||||
|
|
||||||
var instanceNameProperty = fieldType.GetProperty(nameof(NetworkVariableBase.Name));
|
|
||||||
var sanitizedVariableName = sortedFields[i].Name.Replace("<", string.Empty).Replace(">k__BackingField", string.Empty);
|
|
||||||
instanceNameProperty?.SetValue(instance, sanitizedVariableName);
|
|
||||||
|
|
||||||
NetworkVariableFields.Add(instance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
__initializeVariables();
|
||||||
|
|
||||||
{
|
{
|
||||||
// Create index map for delivery types
|
// Create index map for delivery types
|
||||||
@@ -718,7 +752,7 @@ namespace Unity.Netcode
|
|||||||
// so we don't have to do this serialization work if we're not going to use the result.
|
// so we don't have to do this serialization work if we're not going to use the result.
|
||||||
if (IsServer && targetClientId == NetworkManager.ServerClientId)
|
if (IsServer && targetClientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
var tmpWriter = new FastBufferWriter(NetworkManager.MessageManager.NonFragmentedMessageMaxSize, Allocator.Temp, NetworkManager.MessageManager.FragmentedMessageMaxSize);
|
||||||
using (tmpWriter)
|
using (tmpWriter)
|
||||||
{
|
{
|
||||||
message.Serialize(tmpWriter, message.Version);
|
message.Serialize(tmpWriter, message.Version);
|
||||||
@@ -726,7 +760,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NetworkManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], targetClientId);
|
NetworkManager.ConnectionManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], targetClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -959,6 +993,8 @@ namespace Unity.Netcode
|
|||||||
if (finalPosition == positionBeforeSynchronize || threwException)
|
if (finalPosition == positionBeforeSynchronize || threwException)
|
||||||
{
|
{
|
||||||
writer.Seek(positionBeforeWrite);
|
writer.Seek(positionBeforeWrite);
|
||||||
|
// Truncate back to the size before
|
||||||
|
writer.Truncate();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NetworkBehaviourUpdater
|
public class NetworkBehaviourUpdater
|
||||||
{
|
{
|
||||||
|
private NetworkManager m_NetworkManager;
|
||||||
|
private NetworkConnectionManager m_ConnectionManager;
|
||||||
private HashSet<NetworkObject> m_DirtyNetworkObjects = new HashSet<NetworkObject>();
|
private HashSet<NetworkObject> m_DirtyNetworkObjects = new HashSet<NetworkObject>();
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
@@ -19,7 +21,7 @@ namespace Unity.Netcode
|
|||||||
m_DirtyNetworkObjects.Add(networkObject);
|
m_DirtyNetworkObjects.Add(networkObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void NetworkBehaviourUpdate(NetworkManager networkManager)
|
internal void NetworkBehaviourUpdate()
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
m_NetworkBehaviourUpdate.Begin();
|
m_NetworkBehaviourUpdate.Begin();
|
||||||
@@ -30,7 +32,7 @@ namespace Unity.Netcode
|
|||||||
// trying to process them, even if they were previously marked as dirty.
|
// trying to process them, even if they were previously marked as dirty.
|
||||||
m_DirtyNetworkObjects.RemoveWhere((sobj) => sobj == null);
|
m_DirtyNetworkObjects.RemoveWhere((sobj) => sobj == null);
|
||||||
|
|
||||||
if (networkManager.IsServer)
|
if (m_ConnectionManager.LocalClient.IsServer)
|
||||||
{
|
{
|
||||||
foreach (var dirtyObj in m_DirtyNetworkObjects)
|
foreach (var dirtyObj in m_DirtyNetworkObjects)
|
||||||
{
|
{
|
||||||
@@ -39,9 +41,9 @@ namespace Unity.Netcode
|
|||||||
dirtyObj.ChildNetworkBehaviours[k].PreVariableUpdate();
|
dirtyObj.ChildNetworkBehaviours[k].PreVariableUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < networkManager.ConnectedClientsList.Count; i++)
|
for (int i = 0; i < m_ConnectionManager.ConnectedClientsList.Count; i++)
|
||||||
{
|
{
|
||||||
var client = networkManager.ConnectedClientsList[i];
|
var client = m_ConnectionManager.ConnectedClientsList[i];
|
||||||
|
|
||||||
if (dirtyObj.IsNetworkVisibleTo(client.ClientId))
|
if (dirtyObj.IsNetworkVisibleTo(client.ClientId))
|
||||||
{
|
{
|
||||||
@@ -104,5 +106,26 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void Initialize(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
m_NetworkManager = networkManager;
|
||||||
|
m_ConnectionManager = networkManager.ConnectionManager;
|
||||||
|
m_NetworkManager.NetworkTickSystem.Tick += NetworkBehaviourUpdater_Tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Shutdown()
|
||||||
|
{
|
||||||
|
m_NetworkManager.NetworkTickSystem.Tick -= NetworkBehaviourUpdater_Tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order of operations requires NetworkVariable updates first then showing NetworkObjects
|
||||||
|
private void NetworkBehaviourUpdater_Tick()
|
||||||
|
{
|
||||||
|
// First update NetworkVariables
|
||||||
|
NetworkBehaviourUpdate();
|
||||||
|
|
||||||
|
// Then show any NetworkObjects queued to be made visible/shown
|
||||||
|
m_NetworkManager.SpawnManager.HandleNetworkObjectShow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
#if UNITY_2021_2_OR_NEWER
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
#else
|
||||||
|
using UnityEditor.Experimental.SceneManagement;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -37,30 +46,171 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool m_IsPrefab;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
private void OnValidate()
|
private const string k_GlobalIdTemplate = "GlobalObjectId_V1-{0}-{1}-{2}-{3}";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Object Types <see href="https://docs.unity3d.com/ScriptReference/GlobalObjectId.html"/>
|
||||||
|
/// Parameter 0 of <see cref="k_GlobalIdTemplate"/>
|
||||||
|
/// </summary>
|
||||||
|
// 0 = Null (when considered a null object type we can ignore)
|
||||||
|
// 1 = Imported Asset
|
||||||
|
// 2 = Scene Object
|
||||||
|
// 3 = Source Asset.
|
||||||
|
private const int k_NullObjectType = 0;
|
||||||
|
private const int k_ImportedAssetObjectType = 1;
|
||||||
|
private const int k_SceneObjectType = 2;
|
||||||
|
private const int k_SourceAssetObjectType = 3;
|
||||||
|
|
||||||
|
[ContextMenu("Refresh In-Scene Prefab Instances")]
|
||||||
|
internal void RefreshAllPrefabInstances()
|
||||||
{
|
{
|
||||||
GenerateGlobalObjectIdHash();
|
var instanceGlobalId = GlobalObjectId.GetGlobalObjectIdSlow(this);
|
||||||
|
if (!PrefabUtility.IsPartOfAnyPrefab(this) || instanceGlobalId.identifierType != k_ImportedAssetObjectType)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog("Network Prefab Assets Only", "This action can only be performed on a network prefab asset.", "Ok");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void GenerateGlobalObjectIdHash()
|
// Handle updating the currently active scene
|
||||||
|
var networkObjects = FindObjectsByType<NetworkObject>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||||
|
foreach (var networkObject in networkObjects)
|
||||||
|
{
|
||||||
|
networkObject.OnValidate();
|
||||||
|
}
|
||||||
|
NetworkObjectRefreshTool.ProcessActiveScene();
|
||||||
|
|
||||||
|
// Refresh all build settings scenes
|
||||||
|
var activeScene = SceneManager.GetActiveScene();
|
||||||
|
foreach (var editorScene in EditorBuildSettings.scenes)
|
||||||
|
{
|
||||||
|
// skip disabled scenes and the currently active scene
|
||||||
|
if (!editorScene.enabled || activeScene.path == editorScene.path)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Add the scene to be processed
|
||||||
|
NetworkObjectRefreshTool.ProcessScene(editorScene.path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all added scenes
|
||||||
|
NetworkObjectRefreshTool.ProcessScenes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
{
|
{
|
||||||
// do NOT regenerate GlobalObjectIdHash for NetworkPrefabs while Editor is in PlayMode
|
// do NOT regenerate GlobalObjectIdHash for NetworkPrefabs while Editor is in PlayMode
|
||||||
if (UnityEditor.EditorApplication.isPlaying && !string.IsNullOrEmpty(gameObject.scene.name))
|
if (EditorApplication.isPlaying && !string.IsNullOrEmpty(gameObject.scene.name))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do NOT regenerate GlobalObjectIdHash if Editor is transitioning into or out of PlayMode
|
// do NOT regenerate GlobalObjectIdHash if Editor is transitioning into or out of PlayMode
|
||||||
if (!UnityEditor.EditorApplication.isPlaying && UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
|
if (!EditorApplication.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalObjectIdString = UnityEditor.GlobalObjectId.GetGlobalObjectIdSlow(this).ToString();
|
// Get a global object identifier for this network prefab
|
||||||
GlobalObjectIdHash = XXHash.Hash32(globalObjectIdString);
|
var globalId = GetGlobalId();
|
||||||
|
|
||||||
|
|
||||||
|
// if the identifier type is 0, then don't update the GlobalObjectIdHash
|
||||||
|
if (globalId.identifierType == k_NullObjectType)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldValue = GlobalObjectIdHash;
|
||||||
|
GlobalObjectIdHash = globalId.ToString().Hash32();
|
||||||
|
|
||||||
|
// If the GlobalObjectIdHash value changed, then mark the asset dirty
|
||||||
|
if (GlobalObjectIdHash != oldValue)
|
||||||
|
{
|
||||||
|
// Check if this is an in-scnee placed NetworkObject (Special Case for In-Scene Placed)
|
||||||
|
if (!IsEditingPrefab() && gameObject.scene.name != null && gameObject.scene.name != gameObject.name)
|
||||||
|
{
|
||||||
|
// Sanity check to make sure this is a scene placed object
|
||||||
|
if (globalId.identifierType != k_SceneObjectType)
|
||||||
|
{
|
||||||
|
// This should never happen, but in the event it does throw and error
|
||||||
|
Debug.LogError($"[{gameObject.name}] is detected as an in-scene placed object but its identifier is of type {globalId.identifierType}! **Report this error**");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a prefab instance
|
||||||
|
if (PrefabUtility.IsPartOfAnyPrefab(this))
|
||||||
|
{
|
||||||
|
// We must invoke this in order for the modifications to get saved with the scene (does not mark scene as dirty)
|
||||||
|
PrefabUtility.RecordPrefabInstancePropertyModifications(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Otherwise, this is a standard network prefab asset so we just mark it dirty for the AssetDatabase to update it
|
||||||
|
{
|
||||||
|
EditorUtility.SetDirty(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsEditingPrefab()
|
||||||
|
{
|
||||||
|
// Check if we are directly editing the prefab
|
||||||
|
var stage = PrefabStageUtility.GetPrefabStage(gameObject);
|
||||||
|
|
||||||
|
// if we are not editing the prefab directly (or a sub-prefab), then return the object identifier
|
||||||
|
if (stage == null || stage.assetPath == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GlobalObjectId GetGlobalId()
|
||||||
|
{
|
||||||
|
var instanceGlobalId = GlobalObjectId.GetGlobalObjectIdSlow(this);
|
||||||
|
|
||||||
|
// If not editing a prefab, then just use the generated id
|
||||||
|
if (!IsEditingPrefab())
|
||||||
|
{
|
||||||
|
return instanceGlobalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the asset doesn't exist at the given path, then return the object identifier
|
||||||
|
var prefabStageAssetPath = PrefabStageUtility.GetPrefabStage(gameObject).assetPath;
|
||||||
|
// If (for some reason) the asset path is null return the generated id
|
||||||
|
if (prefabStageAssetPath == null)
|
||||||
|
{
|
||||||
|
return instanceGlobalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var theAsset = AssetDatabase.LoadAssetAtPath<NetworkObject>(prefabStageAssetPath);
|
||||||
|
// If there is no asset at that path (for some odd/edge case reason), return the generated id
|
||||||
|
if (theAsset == null)
|
||||||
|
{
|
||||||
|
return instanceGlobalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't get the asset GUID and/or the file identifier, then return the object identifier
|
||||||
|
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(theAsset, out var guid, out long localFileId))
|
||||||
|
{
|
||||||
|
return instanceGlobalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: If we reached this point, then we are most likely opening a prefab to edit.
|
||||||
|
// The instanceGlobalId will be constructed as if it is a scene object, however when it
|
||||||
|
// is serialized its value will be treated as a file asset (the "why" to the below code).
|
||||||
|
|
||||||
|
// Construct an imported asset identifier with the type being a source asset object type
|
||||||
|
var prefabGlobalIdText = string.Format(k_GlobalIdTemplate, k_SourceAssetObjectType, guid, (ulong)localFileId, 0);
|
||||||
|
|
||||||
|
// If we can't parse the result log an error and return the instanceGlobalId
|
||||||
|
if (!GlobalObjectId.TryParse(prefabGlobalIdText, out var prefabGlobalId))
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GlobalObjectId Gen] Failed to parse ({prefabGlobalIdText}) returning default ({instanceGlobalId})! ** Please Report This Error **");
|
||||||
|
return instanceGlobalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, return the constructed identifier for the source prefab asset
|
||||||
|
return prefabGlobalId;
|
||||||
}
|
}
|
||||||
#endif // UNITY_EDITOR
|
#endif // UNITY_EDITOR
|
||||||
|
|
||||||
@@ -188,6 +338,12 @@ namespace Unity.Netcode
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public Action OnMigratedToNewScene;
|
public Action OnMigratedToNewScene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When set to false, the NetworkObject will be spawned with no observers initially (other than the server)
|
||||||
|
/// </summary>
|
||||||
|
[Tooltip("When false, the NetworkObject will spawn with no observers initially. (default is true)")]
|
||||||
|
public bool SpawnWithObservers = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate type for checking visibility
|
/// Delegate type for checking visibility
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -361,8 +517,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
NetworkManager.SpawnManager.MarkObjectForShowingTo(this, clientId);
|
||||||
NetworkManager.MarkObjectForShowingTo(this, clientId);
|
|
||||||
Observers.Add(clientId);
|
Observers.Add(clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +607,7 @@ namespace Unity.Netcode
|
|||||||
throw new VisibilityChangeException("Cannot hide an object from the server");
|
throw new VisibilityChangeException("Cannot hide an object from the server");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NetworkManager.RemoveObjectFromShowingTo(this, clientId))
|
if (!NetworkManager.SpawnManager.RemoveObjectFromShowingTo(this, clientId))
|
||||||
{
|
{
|
||||||
if (!Observers.Contains(clientId))
|
if (!Observers.Contains(clientId))
|
||||||
{
|
{
|
||||||
@@ -466,7 +621,7 @@ namespace Unity.Netcode
|
|||||||
DestroyGameObject = !IsSceneObject.Value
|
DestroyGameObject = !IsSceneObject.Value
|
||||||
};
|
};
|
||||||
// Send destroy call
|
// Send destroy call
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
|
||||||
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -532,14 +687,30 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned &&
|
// If no NetworkManager is assigned, then just exit early
|
||||||
(IsSceneObject == null || (IsSceneObject.Value != true)))
|
if (!NetworkManager)
|
||||||
{
|
{
|
||||||
throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead.");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager != null && NetworkManager.SpawnManager != null &&
|
if (NetworkManager.IsListening && !NetworkManager.IsServer && IsSpawned &&
|
||||||
NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
(IsSceneObject == null || (IsSceneObject.Value != true)))
|
||||||
|
{
|
||||||
|
// Clients should not despawn NetworkObjects while connected to a session, but we don't want to destroy the current call stack
|
||||||
|
// if this happens. Instead, we should just generate a network log error and exit early (as long as we are not shutting down).
|
||||||
|
if (!NetworkManager.ShutdownInProgress)
|
||||||
|
{
|
||||||
|
// Since we still have a session connection, log locally and on the server to inform user of this issue.
|
||||||
|
if (NetworkManager.LogLevel <= LogLevel.Error)
|
||||||
|
{
|
||||||
|
NetworkLog.LogErrorServer($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead.");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Otherwise, clients can despawn NetworkObjects while shutting down and should not generate any messages when this happens
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkManager.SpawnManager != null && NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
||||||
{
|
{
|
||||||
if (this == networkObject)
|
if (this == networkObject)
|
||||||
{
|
{
|
||||||
@@ -663,6 +834,21 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void InvokeOwnershipChanged(ulong previous, ulong next)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
|
{
|
||||||
|
if (ChildNetworkBehaviours[i].gameObject.activeInHierarchy)
|
||||||
|
{
|
||||||
|
ChildNetworkBehaviours[i].InternalOnOwnershipChanged(previous, next);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"{ChildNetworkBehaviours[i].gameObject.name} is disabled! Netcode for GameObjects does not support disabled NetworkBehaviours! The {ChildNetworkBehaviours[i].GetType().Name} component was skipped during ownership assignment!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal void InvokeBehaviourOnNetworkObjectParentChanged(NetworkObject parentNetworkObject)
|
internal void InvokeBehaviourOnNetworkObjectParentChanged(NetworkObject parentNetworkObject)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
@@ -712,6 +898,12 @@ namespace Unity.Netcode
|
|||||||
/// <returns>Whether or not reparenting was successful.</returns>
|
/// <returns>Whether or not reparenting was successful.</returns>
|
||||||
public bool TrySetParent(Transform parent, bool worldPositionStays = true)
|
public bool TrySetParent(Transform parent, bool worldPositionStays = true)
|
||||||
{
|
{
|
||||||
|
// If we are removing ourself from a parent
|
||||||
|
if (parent == null)
|
||||||
|
{
|
||||||
|
return TrySetParent((NetworkObject)null, worldPositionStays);
|
||||||
|
}
|
||||||
|
|
||||||
var networkObject = parent.GetComponent<NetworkObject>();
|
var networkObject = parent.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
// If the parent doesn't have a NetworkObjet then return false, otherwise continue trying to parent
|
// If the parent doesn't have a NetworkObjet then return false, otherwise continue trying to parent
|
||||||
@@ -898,7 +1090,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientIds, idx);
|
NetworkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientIds, idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1163,7 +1355,7 @@ namespace Unity.Netcode
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal NetworkBehaviour GetNetworkBehaviourAtOrderIndex(ushort index)
|
public NetworkBehaviour GetNetworkBehaviourAtOrderIndex(ushort index)
|
||||||
{
|
{
|
||||||
if (index >= ChildNetworkBehaviours.Count)
|
if (index >= ChildNetworkBehaviours.Count)
|
||||||
{
|
{
|
||||||
@@ -1171,7 +1363,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
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.");
|
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)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
var currentKnownChildren = new System.Text.StringBuilder();
|
var currentKnownChildren = new System.Text.StringBuilder();
|
||||||
@@ -1184,7 +1375,6 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
NetworkLog.LogInfo(currentKnownChildren.ToString());
|
NetworkLog.LogInfo(currentKnownChildren.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
118
Runtime/Core/NetworkObjectRefreshTool.cs
Normal file
118
Runtime/Core/NetworkObjectRefreshTool.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#if UNITY_EDITOR
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is a helper tool to update all in-scene placed instances of a prefab that
|
||||||
|
/// originally did not have a NetworkObject component but one was added to the prefab
|
||||||
|
/// later.
|
||||||
|
/// </summary>
|
||||||
|
internal class NetworkObjectRefreshTool
|
||||||
|
{
|
||||||
|
private static List<string> s_ScenesToUpdate = new List<string>();
|
||||||
|
private static bool s_ProcessScenes;
|
||||||
|
private static bool s_CloseScenes;
|
||||||
|
|
||||||
|
internal static Action AllScenesProcessed;
|
||||||
|
|
||||||
|
internal static void ProcessScene(string scenePath, bool processScenes = true)
|
||||||
|
{
|
||||||
|
if (!s_ScenesToUpdate.Contains(scenePath))
|
||||||
|
{
|
||||||
|
if (s_ScenesToUpdate.Count == 0)
|
||||||
|
{
|
||||||
|
EditorSceneManager.sceneOpened += EditorSceneManager_sceneOpened;
|
||||||
|
EditorSceneManager.sceneSaved += EditorSceneManager_sceneSaved;
|
||||||
|
}
|
||||||
|
s_ScenesToUpdate.Add(scenePath);
|
||||||
|
}
|
||||||
|
s_ProcessScenes = processScenes;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ProcessActiveScene()
|
||||||
|
{
|
||||||
|
var activeScene = SceneManager.GetActiveScene();
|
||||||
|
if (s_ScenesToUpdate.Contains(activeScene.path) && s_ProcessScenes)
|
||||||
|
{
|
||||||
|
SceneOpened(activeScene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ProcessScenes()
|
||||||
|
{
|
||||||
|
if (s_ScenesToUpdate.Count != 0)
|
||||||
|
{
|
||||||
|
s_CloseScenes = true;
|
||||||
|
var scenePath = s_ScenesToUpdate.First();
|
||||||
|
EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_CloseScenes = false;
|
||||||
|
EditorSceneManager.sceneSaved -= EditorSceneManager_sceneSaved;
|
||||||
|
EditorSceneManager.sceneOpened -= EditorSceneManager_sceneOpened;
|
||||||
|
AllScenesProcessed?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FinishedProcessingScene(Scene scene, bool refreshed = false)
|
||||||
|
{
|
||||||
|
if (s_ScenesToUpdate.Contains(scene.path))
|
||||||
|
{
|
||||||
|
// Provide a log of all scenes that were modified to the user
|
||||||
|
if (refreshed)
|
||||||
|
{
|
||||||
|
Debug.Log($"Refreshed and saved updates to scene: {scene.name}");
|
||||||
|
}
|
||||||
|
s_ProcessScenes = false;
|
||||||
|
s_ScenesToUpdate.Remove(scene.path);
|
||||||
|
|
||||||
|
if (scene != SceneManager.GetActiveScene())
|
||||||
|
{
|
||||||
|
EditorSceneManager.CloseScene(scene, s_CloseScenes);
|
||||||
|
}
|
||||||
|
ProcessScenes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EditorSceneManager_sceneSaved(Scene scene)
|
||||||
|
{
|
||||||
|
FinishedProcessingScene(scene, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SceneOpened(Scene scene)
|
||||||
|
{
|
||||||
|
if (s_ScenesToUpdate.Contains(scene.path))
|
||||||
|
{
|
||||||
|
if (s_ProcessScenes)
|
||||||
|
{
|
||||||
|
if (!EditorSceneManager.MarkSceneDirty(scene))
|
||||||
|
{
|
||||||
|
Debug.Log($"Scene {scene.name} did not get marked as dirty!");
|
||||||
|
FinishedProcessingScene(scene);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorSceneManager.SaveScene(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FinishedProcessingScene(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EditorSceneManager_sceneOpened(Scene scene, OpenSceneMode mode)
|
||||||
|
{
|
||||||
|
SceneOpened(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // UNITY_EDITOR
|
||||||
11
Runtime/Core/NetworkObjectRefreshTool.cs.meta
Normal file
11
Runtime/Core/NetworkObjectRefreshTool.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d24d5e8371c3cca4890e2713bdeda288
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -51,35 +51,58 @@ namespace Unity.Netcode
|
|||||||
/// <param name="message">The message to log</param>
|
/// <param name="message">The message to log</param>
|
||||||
public static void LogErrorServer(string message) => LogServer(message, LogType.Error);
|
public static void LogErrorServer(string message) => LogServer(message, LogType.Error);
|
||||||
|
|
||||||
|
internal static NetworkManager NetworkManagerOverride;
|
||||||
|
|
||||||
private static void LogServer(string message, LogType logType)
|
private static void LogServer(string message, LogType logType)
|
||||||
{
|
{
|
||||||
|
var networkManager = NetworkManagerOverride ??= NetworkManager.Singleton;
|
||||||
// Get the sender of the local log
|
// Get the sender of the local log
|
||||||
ulong localId = NetworkManager.Singleton != null ? NetworkManager.Singleton.LocalClientId : 0;
|
ulong localId = networkManager?.LocalClientId ?? 0;
|
||||||
|
bool isServer = networkManager?.IsServer ?? true;
|
||||||
switch (logType)
|
switch (logType)
|
||||||
{
|
{
|
||||||
case LogType.Info:
|
case LogType.Info:
|
||||||
|
if (isServer)
|
||||||
|
{
|
||||||
LogInfoServerLocal(message, localId);
|
LogInfoServerLocal(message, localId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogInfo(message);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LogType.Warning:
|
case LogType.Warning:
|
||||||
|
if (isServer)
|
||||||
|
{
|
||||||
LogWarningServerLocal(message, localId);
|
LogWarningServerLocal(message, localId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogWarning(message);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LogType.Error:
|
case LogType.Error:
|
||||||
|
if (isServer)
|
||||||
|
{
|
||||||
LogErrorServerLocal(message, localId);
|
LogErrorServerLocal(message, localId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogError(message);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager.Singleton != null && !NetworkManager.Singleton.IsServer && NetworkManager.Singleton.NetworkConfig.EnableNetworkLogs)
|
if (!isServer && networkManager.NetworkConfig.EnableNetworkLogs)
|
||||||
{
|
{
|
||||||
|
|
||||||
var networkMessage = new ServerLogMessage
|
var networkMessage = new ServerLogMessage
|
||||||
{
|
{
|
||||||
LogType = logType,
|
LogType = logType,
|
||||||
Message = message
|
Message = message
|
||||||
};
|
};
|
||||||
var size = NetworkManager.Singleton.SendMessage(ref networkMessage, NetworkDelivery.ReliableFragmentedSequenced, NetworkManager.ServerClientId);
|
var size = networkManager.ConnectionManager.SendMessage(ref networkMessage, NetworkDelivery.ReliableFragmentedSequenced, NetworkManager.ServerClientId);
|
||||||
|
|
||||||
NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.ServerClientId, (uint)logType, size);
|
networkManager.NetworkMetrics.TrackServerLogSent(NetworkManager.ServerClientId, (uint)logType, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
SendData = messageBuffer
|
SendData = messageBuffer
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, networkDelivery, clientIds);
|
var size = m_NetworkManager.ConnectionManager.SendMessage(ref message, networkDelivery, clientIds);
|
||||||
|
|
||||||
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
@@ -123,7 +123,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
SendData = messageBuffer
|
SendData = messageBuffer
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, networkDelivery, clientId);
|
var size = m_NetworkManager.ConnectionManager.SendMessage(ref message, networkDelivery, clientId);
|
||||||
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
{
|
||||||
@@ -275,7 +275,7 @@ namespace Unity.Netcode
|
|||||||
Hash = hash,
|
Hash = hash,
|
||||||
SendData = messageStream,
|
SendData = messageStream,
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, networkDelivery, clientId);
|
var size = m_NetworkManager.ConnectionManager.SendMessage(ref message, networkDelivery, clientId);
|
||||||
|
|
||||||
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
@@ -333,7 +333,7 @@ namespace Unity.Netcode
|
|||||||
Hash = hash,
|
Hash = hash,
|
||||||
SendData = messageStream
|
SendData = messageStream
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, networkDelivery, clientIds);
|
var size = m_NetworkManager.ConnectionManager.SendMessage(ref message, networkDelivery, clientIds);
|
||||||
|
|
||||||
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
|
|||||||
26
Runtime/Messaging/DefaultMessageSender.cs
Normal file
26
Runtime/Messaging/DefaultMessageSender.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default NetworkTransport Message Sender
|
||||||
|
/// <see cref="NetworkMessageManager"/>
|
||||||
|
/// </summary>
|
||||||
|
internal class DefaultMessageSender : INetworkMessageSender
|
||||||
|
{
|
||||||
|
private NetworkTransport m_NetworkTransport;
|
||||||
|
private NetworkConnectionManager m_ConnectionManager;
|
||||||
|
|
||||||
|
public DefaultMessageSender(NetworkManager manager)
|
||||||
|
{
|
||||||
|
m_NetworkTransport = manager.NetworkConfig.NetworkTransport;
|
||||||
|
m_ConnectionManager = manager.ConnectionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData)
|
||||||
|
{
|
||||||
|
var sendBuffer = batchData.ToTempByteArray();
|
||||||
|
|
||||||
|
m_NetworkTransport.Send(m_ConnectionManager.ClientIdToTransportId(clientId), sendBuffer, delivery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Runtime/Messaging/DefaultMessageSender.cs.meta
Normal file
11
Runtime/Messaging/DefaultMessageSender.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5ea6bdd38832d9947bb21c4b35bf61d0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -3,12 +3,12 @@ using Unity.Collections;
|
|||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
internal class DeferredMessageManager : IDeferredMessageManager
|
internal class DeferredMessageManager : IDeferredNetworkMessageManager
|
||||||
{
|
{
|
||||||
protected struct TriggerData
|
protected struct TriggerData
|
||||||
{
|
{
|
||||||
public FastBufferReader Reader;
|
public FastBufferReader Reader;
|
||||||
public MessageHeader Header;
|
public NetworkMessageHeader Header;
|
||||||
public ulong SenderId;
|
public ulong SenderId;
|
||||||
public float Timestamp;
|
public float Timestamp;
|
||||||
public int SerializedHeaderSize;
|
public int SerializedHeaderSize;
|
||||||
@@ -19,7 +19,7 @@ namespace Unity.Netcode
|
|||||||
public NativeList<TriggerData> TriggerData;
|
public NativeList<TriggerData> TriggerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly Dictionary<IDeferredMessageManager.TriggerType, Dictionary<ulong, TriggerInfo>> m_Triggers = new Dictionary<IDeferredMessageManager.TriggerType, Dictionary<ulong, TriggerInfo>>();
|
protected readonly Dictionary<IDeferredNetworkMessageManager.TriggerType, Dictionary<ulong, TriggerInfo>> m_Triggers = new Dictionary<IDeferredNetworkMessageManager.TriggerType, Dictionary<ulong, TriggerInfo>>();
|
||||||
|
|
||||||
private readonly NetworkManager m_NetworkManager;
|
private readonly NetworkManager m_NetworkManager;
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ namespace Unity.Netcode
|
|||||||
/// There is a one second maximum lifetime of triggers to avoid memory leaks. After one second has passed
|
/// There is a one second maximum lifetime of triggers to avoid memory leaks. After one second has passed
|
||||||
/// without the requested object ID being spawned, the triggers for it are automatically deleted.
|
/// without the requested object ID being spawned, the triggers for it are automatically deleted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual unsafe void DeferMessage(IDeferredMessageManager.TriggerType trigger, ulong key, FastBufferReader reader, ref NetworkContext context)
|
public virtual unsafe void DeferMessage(IDeferredNetworkMessageManager.TriggerType trigger, ulong key, FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
if (!m_Triggers.TryGetValue(trigger, out var triggers))
|
if (!m_Triggers.TryGetValue(trigger, out var triggers))
|
||||||
{
|
{
|
||||||
@@ -90,7 +90,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void PurgeTrigger(IDeferredMessageManager.TriggerType triggerType, ulong key, TriggerInfo triggerInfo)
|
protected virtual void PurgeTrigger(IDeferredNetworkMessageManager.TriggerType triggerType, ulong key, TriggerInfo triggerInfo)
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
@@ -105,7 +105,7 @@ namespace Unity.Netcode
|
|||||||
triggerInfo.TriggerData.Dispose();
|
triggerInfo.TriggerData.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void ProcessTriggers(IDeferredMessageManager.TriggerType trigger, ulong key)
|
public virtual void ProcessTriggers(IDeferredNetworkMessageManager.TriggerType trigger, ulong key)
|
||||||
{
|
{
|
||||||
if (m_Triggers.TryGetValue(trigger, out var triggers))
|
if (m_Triggers.TryGetValue(trigger, out var triggers))
|
||||||
{
|
{
|
||||||
@@ -116,7 +116,7 @@ namespace Unity.Netcode
|
|||||||
foreach (var deferredMessage in triggerInfo.TriggerData)
|
foreach (var deferredMessage in triggerInfo.TriggerData)
|
||||||
{
|
{
|
||||||
// Reader will be disposed within HandleMessage
|
// Reader will be disposed within HandleMessage
|
||||||
m_NetworkManager.MessagingSystem.HandleMessage(deferredMessage.Header, deferredMessage.Reader, deferredMessage.SenderId, deferredMessage.Timestamp, deferredMessage.SerializedHeaderSize);
|
m_NetworkManager.ConnectionManager.MessageManager.HandleMessage(deferredMessage.Header, deferredMessage.Reader, deferredMessage.SenderId, deferredMessage.Timestamp, deferredMessage.SerializedHeaderSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerInfo.TriggerData.Dispose();
|
triggerInfo.TriggerData.Dispose();
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
string reasonSent = Reason ?? string.Empty;
|
string reasonSent = Reason ?? string.Empty;
|
||||||
|
|
||||||
// Since we don't send a ConnectionApprovedMessage, the version for this message is encded with the message
|
// Since we don't send a ConnectionApprovedMessage, the version for this message is encded with the message itself.
|
||||||
// itself. However, note that we HAVE received a ConnectionRequestMessage, so we DO have a valid targetVersion
|
// However, note that we HAVE received a ConnectionRequestMessage, so we DO have a valid targetVersion on this side of things.
|
||||||
// on this side of things - we just have to make sure the receiving side knows what version we sent it,
|
// 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.
|
||||||
// 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);
|
BytePacker.WriteValueBitPacked(writer, Version);
|
||||||
|
|
||||||
if (writer.TryBeginWrite(FastBufferWriter.GetWriteSize(reasonSent)))
|
if (writer.TryBeginWrite(FastBufferWriter.GetWriteSize(reasonSent)))
|
||||||
@@ -24,15 +22,14 @@ namespace Unity.Netcode
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(string.Empty);
|
writer.WriteValueSafe(string.Empty);
|
||||||
NetworkLog.LogWarning(
|
NetworkLog.LogWarning("Disconnect reason didn't fit. Disconnected without sending a reason. Consider shortening the reason string.");
|
||||||
"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)
|
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
|
// Since we don't get a ConnectionApprovedMessage, the version for this message is encded with the message itself.
|
||||||
// itself. This will override what we got from MessagingSystem... which will always be 0 here.
|
// This will override what we got from MessageManager... which will always be 0 here.
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out receivedMessageVersion);
|
ByteUnpacker.ReadValueBitPacked(reader, out receivedMessageVersion);
|
||||||
reader.ReadValueSafe(out Reason);
|
reader.ReadValueSafe(out Reason);
|
||||||
return true;
|
return true;
|
||||||
@@ -40,7 +37,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
((NetworkManager)context.SystemOwner).DisconnectReason = Reason;
|
((NetworkManager)context.SystemOwner).ConnectionManager.DisconnectReason = Reason;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Marks a generic parameter in this class as a type that should be serialized through
|
||||||
|
/// <see cref="NetworkVariableSerialization{T}"/>. This enables the use of the following methods to support
|
||||||
|
/// serialization within a Network Variable type:
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// <see cref="NetworkVariableSerialization{T}"/>.<see cref="NetworkVariableSerialization{T}.Read"/>
|
||||||
|
/// <br/>
|
||||||
|
/// <see cref="NetworkVariableSerialization{T}"/>.<see cref="NetworkVariableSerialization{T}.Write"/>
|
||||||
|
/// <br/>
|
||||||
|
/// <see cref="NetworkVariableSerialization{T}"/>.<see cref="NetworkVariableSerialization{T}.AreEqual"/>
|
||||||
|
/// <br/>
|
||||||
|
/// <see cref="NetworkVariableSerialization{T}"/>.<see cref="NetworkVariableSerialization{T}.Duplicate"/>
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// The parameter is indicated by index (and is 0-indexed); for example:
|
||||||
|
/// <br/>
|
||||||
|
/// <code>
|
||||||
|
/// [SerializesGenericParameter(1)]
|
||||||
|
/// public class MyClass<TTypeOne, TTypeTwo>
|
||||||
|
/// {
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// <br/>
|
||||||
|
/// This tells the code generation for <see cref="NetworkVariableSerialization{T}"/> to generate
|
||||||
|
/// serialized code for <b>TTypeTwo</b> (generic parameter 1).
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// Note that this is primarily intended to support subtypes of <see cref="NetworkVariableBase"/>,
|
||||||
|
/// and as such, the type resolution is done by examining fields of <see cref="NetworkBehaviour"/>
|
||||||
|
/// subclasses. If your type is not used in a <see cref="NetworkBehaviour"/>, the codegen will
|
||||||
|
/// not find the types, even with this attribute.
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// This attribute is properly inherited by subclasses. For example:
|
||||||
|
/// <br/>
|
||||||
|
/// <code>
|
||||||
|
/// [SerializesGenericParameter(0)]
|
||||||
|
/// public class MyClass<T>
|
||||||
|
/// {
|
||||||
|
/// }
|
||||||
|
/// <br/>
|
||||||
|
/// public class MySubclass1 : MyClass<Foo>
|
||||||
|
/// {
|
||||||
|
/// }
|
||||||
|
/// <br/>
|
||||||
|
/// public class MySubclass2<T> : MyClass<T>
|
||||||
|
/// {
|
||||||
|
/// }
|
||||||
|
/// <br/>
|
||||||
|
/// [SerializesGenericParameter(1)]
|
||||||
|
/// public class MySubclass3<TTypeOne, TTypeTwo> : MyClass<TTypeOne>
|
||||||
|
/// {
|
||||||
|
/// }
|
||||||
|
/// <br/>
|
||||||
|
/// public class MyBehaviour : NetworkBehaviour
|
||||||
|
/// {
|
||||||
|
/// public MySubclass1 TheValue;
|
||||||
|
/// public MySubclass2<Bar> TheValue;
|
||||||
|
/// public MySubclass3<Baz, Qux> TheValue;
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// <br/>
|
||||||
|
/// The above code will trigger generation of serialization code for <b>Foo</b> (passed directly to the
|
||||||
|
/// base class), <b>Bar</b> (passed indirectly to the base class), <b>Baz</b> (passed indirectly to the base class),
|
||||||
|
/// and <b>Qux</b> (marked as serializable in the subclass).
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
|
||||||
|
public class GenerateSerializationForGenericParameterAttribute : Attribute
|
||||||
|
{
|
||||||
|
internal int ParameterIndex;
|
||||||
|
|
||||||
|
public GenerateSerializationForGenericParameterAttribute(int parameterIndex)
|
||||||
|
{
|
||||||
|
ParameterIndex = parameterIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 18cdaa9c2f6446279b0c5948fcd34eec
|
||||||
|
timeCreated: 1694029524
|
||||||
26
Runtime/Messaging/GenerateSerializationForTypeAttribute.cs
Normal file
26
Runtime/Messaging/GenerateSerializationForTypeAttribute.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies a specific type that needs serialization to be generated by codegen.
|
||||||
|
/// This is only needed in special circumstances where manual serialization is being done.
|
||||||
|
/// If you are making a generic network variable-style class, use <see cref="GenerateSerializationForGenericParameterAttribute"/>.
|
||||||
|
/// <br />
|
||||||
|
/// <br />
|
||||||
|
/// This attribute can be attached to any class or method anywhere in the codebase and
|
||||||
|
/// will trigger codegen to generate serialization code for the provided type. It only needs
|
||||||
|
/// to be included once type per codebase, but including it multiple times for the same type
|
||||||
|
/// is safe.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method, AllowMultiple = true)]
|
||||||
|
public class GenerateSerializationForTypeAttribute : Attribute
|
||||||
|
{
|
||||||
|
internal Type Type;
|
||||||
|
|
||||||
|
public GenerateSerializationForTypeAttribute(Type type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1bd80306706f4054b9ba514a72076df5
|
||||||
|
timeCreated: 1694103021
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
internal interface IDeferredMessageManager
|
internal interface IDeferredNetworkMessageManager
|
||||||
{
|
{
|
||||||
internal enum TriggerType
|
internal enum TriggerType
|
||||||
{
|
{
|
||||||
@@ -1,17 +1,39 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
internal struct ILPPMessageProvider : IMessageProvider
|
internal struct ILPPMessageProvider : INetworkMessageProvider
|
||||||
{
|
{
|
||||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
// This is NOT modified by RuntimeAccessModifiersILPP right now, but is populated by ILPP.
|
// This is NOT modified by RuntimeAccessModifiersILPP right now, but is populated by ILPP.
|
||||||
internal static readonly List<MessagingSystem.MessageWithHandler> __network_message_types = new List<MessagingSystem.MessageWithHandler>();
|
internal static readonly List<NetworkMessageManager.MessageWithHandler> __network_message_types = new List<NetworkMessageManager.MessageWithHandler>();
|
||||||
#pragma warning restore IDE1006 // restore naming rule violation check
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
|
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
return __network_message_types;
|
return __network_message_types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
public static void NotifyOnPlayStateChange()
|
||||||
|
{
|
||||||
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OnPlayModeStateChanged(PlayModeStateChange change)
|
||||||
|
{
|
||||||
|
if (change == PlayModeStateChange.ExitingPlayMode)
|
||||||
|
{
|
||||||
|
// Clear out the network message types, because ILPP-generated RuntimeInitializeOnLoad code will
|
||||||
|
// run again and add more messages to it.
|
||||||
|
__network_message_types.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal interface IMessageProvider
|
|
||||||
{
|
|
||||||
List<MessagingSystem.MessageWithHandler> GetMessages();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using Unity.Collections;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
|
|||||||
9
Runtime/Messaging/INetworkMessageProvider.cs
Normal file
9
Runtime/Messaging/INetworkMessageProvider.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
internal interface INetworkMessageProvider
|
||||||
|
{
|
||||||
|
List<NetworkMessageManager.MessageWithHandler> GetMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
internal interface IMessageSender
|
internal interface INetworkMessageSender
|
||||||
{
|
{
|
||||||
void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData);
|
void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData);
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ namespace Unity.Netcode
|
|||||||
ByteUnpacker.ReadValueBitPacked(reader, out OwnerClientId);
|
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(IDeferredNetworkMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +60,8 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
networkObject.InvokeOwnershipChanged(originalOwner, OwnerClientId);
|
||||||
|
|
||||||
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(context.SenderId, networkObject, context.MessageSize);
|
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(context.SenderId, networkObject, context.MessageSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ namespace Unity.Netcode
|
|||||||
BytePacker.WriteValueBitPacked(writer, NetworkTick);
|
BytePacker.WriteValueBitPacked(writer, NetworkTick);
|
||||||
|
|
||||||
uint sceneObjectCount = 0;
|
uint sceneObjectCount = 0;
|
||||||
|
|
||||||
|
// When SpawnedObjectsList is not null then scene management is disabled. Provide a list of
|
||||||
|
// all observed and spawned NetworkObjects that the approved client needs to synchronize.
|
||||||
if (SpawnedObjectsList != null)
|
if (SpawnedObjectsList != null)
|
||||||
{
|
{
|
||||||
var pos = writer.Position;
|
var pos = writer.Position;
|
||||||
@@ -45,7 +48,7 @@ namespace Unity.Netcode
|
|||||||
// Serialize NetworkVariable data
|
// Serialize NetworkVariable data
|
||||||
foreach (var sobj in SpawnedObjectsList)
|
foreach (var sobj in SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(OwnerClientId))
|
if (sobj.SpawnWithObservers && (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(OwnerClientId)))
|
||||||
{
|
{
|
||||||
sobj.Observers.Add(OwnerClientId);
|
sobj.Observers.Add(OwnerClientId);
|
||||||
var sceneObject = sobj.GetMessageSceneObject(OwnerClientId);
|
var sceneObject = sobj.GetMessageSceneObject(OwnerClientId);
|
||||||
@@ -84,18 +87,18 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
var messageVersion = new MessageVersionData();
|
var messageVersion = new MessageVersionData();
|
||||||
messageVersion.Deserialize(reader);
|
messageVersion.Deserialize(reader);
|
||||||
networkManager.MessagingSystem.SetVersion(context.SenderId, messageVersion.Hash, messageVersion.Version);
|
networkManager.ConnectionManager.MessageManager.SetVersion(context.SenderId, messageVersion.Hash, messageVersion.Version);
|
||||||
messageHashesInOrder[i] = messageVersion.Hash;
|
messageHashesInOrder[i] = messageVersion.Hash;
|
||||||
|
|
||||||
// Update the received version since this message will always be passed version 0, due to the map not
|
// Update the received version since this message will always be passed version 0, due to the map not
|
||||||
// being initialized until just now.
|
// being initialized until just now.
|
||||||
var messageType = networkManager.MessagingSystem.GetMessageForHash(messageVersion.Hash);
|
var messageType = networkManager.ConnectionManager.MessageManager.GetMessageForHash(messageVersion.Hash);
|
||||||
if (messageType == typeof(ConnectionApprovedMessage))
|
if (messageType == typeof(ConnectionApprovedMessage))
|
||||||
{
|
{
|
||||||
receivedMessageVersion = messageVersion.Version;
|
receivedMessageVersion = messageVersion.Version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
networkManager.MessagingSystem.SetServerMessageOrder(messageHashesInOrder);
|
networkManager.ConnectionManager.MessageManager.SetServerMessageOrder(messageHashesInOrder);
|
||||||
messageHashesInOrder.Dispose();
|
messageHashesInOrder.Dispose();
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// END FORBIDDEN SEGMENT
|
// END FORBIDDEN SEGMENT
|
||||||
@@ -117,8 +120,11 @@ namespace Unity.Netcode
|
|||||||
networkManager.NetworkTimeSystem.Reset(time.Time, 0.15f); // Start with a constant RTT of 150 until we receive values from the transport.
|
networkManager.NetworkTimeSystem.Reset(time.Time, 0.15f); // Start with a constant RTT of 150 until we receive values from the transport.
|
||||||
networkManager.NetworkTickSystem.Reset(networkManager.NetworkTimeSystem.LocalTime, networkManager.NetworkTimeSystem.ServerTime);
|
networkManager.NetworkTickSystem.Reset(networkManager.NetworkTimeSystem.LocalTime, networkManager.NetworkTimeSystem.ServerTime);
|
||||||
|
|
||||||
networkManager.LocalClient = new NetworkClient() { ClientId = networkManager.LocalClientId };
|
networkManager.ConnectionManager.LocalClient.SetRole(false, true, networkManager);
|
||||||
networkManager.IsApproved = true;
|
networkManager.ConnectionManager.LocalClient.IsApproved = true;
|
||||||
|
networkManager.ConnectionManager.LocalClient.ClientId = OwnerClientId;
|
||||||
|
// Stop the client-side approval timeout coroutine since we are approved.
|
||||||
|
networkManager.ConnectionManager.StopClientApprovalCoroutine();
|
||||||
|
|
||||||
// Only if scene management is disabled do we handle NetworkObject synchronization at this point
|
// Only if scene management is disabled do we handle NetworkObject synchronization at this point
|
||||||
if (!networkManager.NetworkConfig.EnableSceneManagement)
|
if (!networkManager.NetworkConfig.EnableSceneManagement)
|
||||||
@@ -138,7 +144,7 @@ namespace Unity.Netcode
|
|||||||
// Mark the client being connected
|
// Mark the client being connected
|
||||||
networkManager.IsConnectedClient = true;
|
networkManager.IsConnectedClient = true;
|
||||||
// When scene management is disabled we notify after everything is synchronized
|
// When scene management is disabled we notify after everything is synchronized
|
||||||
networkManager.InvokeOnClientConnectedCallback(context.SenderId);
|
networkManager.ConnectionManager.InvokeOnClientConnectedCallback(context.SenderId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,11 +59,11 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
var messageVersion = new MessageVersionData();
|
var messageVersion = new MessageVersionData();
|
||||||
messageVersion.Deserialize(reader);
|
messageVersion.Deserialize(reader);
|
||||||
networkManager.MessagingSystem.SetVersion(context.SenderId, messageVersion.Hash, messageVersion.Version);
|
networkManager.ConnectionManager.MessageManager.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
|
// Update the received version since this message will always be passed version 0, due to the map not
|
||||||
// being initialized until just now.
|
// being initialized until just now.
|
||||||
var messageType = networkManager.MessagingSystem.GetMessageForHash(messageVersion.Hash);
|
var messageType = networkManager.ConnectionManager.MessageManager.GetMessageForHash(messageVersion.Hash);
|
||||||
if (messageType == typeof(ConnectionRequestMessage))
|
if (messageType == typeof(ConnectionRequestMessage))
|
||||||
{
|
{
|
||||||
receivedMessageVersion = messageVersion.Version;
|
receivedMessageVersion = messageVersion.Version;
|
||||||
@@ -135,7 +135,7 @@ namespace Unity.Netcode
|
|||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
var senderId = context.SenderId;
|
var senderId = context.SenderId;
|
||||||
|
|
||||||
if (networkManager.PendingClients.TryGetValue(senderId, out PendingClient client))
|
if (networkManager.ConnectionManager.PendingClients.TryGetValue(senderId, out PendingClient client))
|
||||||
{
|
{
|
||||||
// Set to pending approval to prevent future connection requests from being approved
|
// Set to pending approval to prevent future connection requests from being approved
|
||||||
client.ConnectionState = PendingClient.State.PendingApproval;
|
client.ConnectionState = PendingClient.State.PendingApproval;
|
||||||
@@ -143,17 +143,8 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (networkManager.NetworkConfig.ConnectionApproval)
|
if (networkManager.NetworkConfig.ConnectionApproval)
|
||||||
{
|
{
|
||||||
// Note: Delegate creation allocates.
|
var messageRequest = this;
|
||||||
// Note: ToArray() also allocates. :(
|
networkManager.ConnectionManager.ApproveConnection(ref messageRequest, ref context);
|
||||||
var response = new NetworkManager.ConnectionApprovalResponse();
|
|
||||||
networkManager.ClientsToApprove[senderId] = response;
|
|
||||||
|
|
||||||
networkManager.ConnectionApprovalCallback(
|
|
||||||
new NetworkManager.ConnectionApprovalRequest
|
|
||||||
{
|
|
||||||
Payload = ConnectionData,
|
|
||||||
ClientNetworkId = senderId
|
|
||||||
}, response);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -162,7 +153,7 @@ namespace Unity.Netcode
|
|||||||
Approved = true,
|
Approved = true,
|
||||||
CreatePlayerObject = networkManager.NetworkConfig.PlayerPrefab != null
|
CreatePlayerObject = networkManager.NetworkConfig.PlayerPrefab != null
|
||||||
};
|
};
|
||||||
networkManager.HandleConnectionApproval(senderId, response);
|
networkManager.ConnectionManager.HandleConnectionApproval(senderId, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,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.Hash, reader, ref context);
|
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab, ObjectInfo.Hash, reader, ref context);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_ReceivedNetworkVariableData = reader;
|
m_ReceivedNetworkVariableData = reader;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
||||||
{
|
{
|
||||||
networkManager.DeferredMessageManager.DeferMessage(IDeferredMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context);
|
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -23,8 +23,12 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ref NetworkContext context)
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
|
if (!networkManager.ShutdownInProgress)
|
||||||
{
|
{
|
||||||
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(Hash, context.SenderId, m_ReceiveData, context.SerializedHeaderSize);
|
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(Hash, context.SenderId, m_ReceiveData, context.SerializedHeaderSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ namespace Unity.Netcode
|
|||||||
// The object containing the behaviour we're about to process is about to be shown to this client
|
// The object containing the behaviour we're about to process is about to be shown to this client
|
||||||
// As a result, the client will get the fully serialized NetworkVariable and would be confused by
|
// As a result, the client will get the fully serialized NetworkVariable and would be confused by
|
||||||
// an extraneous delta
|
// an extraneous delta
|
||||||
if (NetworkBehaviour.NetworkManager.ObjectsToShowToClient.ContainsKey(TargetClientId) &&
|
if (NetworkBehaviour.NetworkManager.SpawnManager.ObjectsToShowToClient.ContainsKey(TargetClientId) &&
|
||||||
NetworkBehaviour.NetworkManager.ObjectsToShowToClient[TargetClientId]
|
NetworkBehaviour.NetworkManager.SpawnManager.ObjectsToShowToClient[TargetClientId]
|
||||||
.Contains(NetworkBehaviour.NetworkObject))
|
.Contains(NetworkBehaviour.NetworkObject))
|
||||||
{
|
{
|
||||||
shouldWrite = false;
|
shouldWrite = false;
|
||||||
@@ -90,7 +90,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
var tempWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
var tempWriter = new FastBufferWriter(NetworkBehaviour.NetworkManager.MessageManager.NonFragmentedMessageMaxSize, Allocator.Temp, NetworkBehaviour.NetworkManager.MessageManager.FragmentedMessageMaxSize);
|
||||||
NetworkBehaviour.NetworkVariableFields[i].WriteDelta(tempWriter);
|
NetworkBehaviour.NetworkVariableFields[i].WriteDelta(tempWriter);
|
||||||
BytePacker.WriteValueBitPacked(writer, tempWriter.Length);
|
BytePacker.WriteValueBitPacked(writer, tempWriter.Length);
|
||||||
|
|
||||||
@@ -105,7 +105,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
networkVariable.WriteDelta(writer);
|
networkVariable.WriteDelta(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
||||||
TargetClientId,
|
TargetClientId,
|
||||||
NetworkBehaviour.NetworkObject,
|
NetworkBehaviour.NetworkObject,
|
||||||
@@ -207,7 +206,6 @@ namespace Unity.Netcode
|
|||||||
networkBehaviour.__getTypeName(),
|
networkBehaviour.__getTypeName(),
|
||||||
context.MessageSize);
|
context.MessageSize);
|
||||||
|
|
||||||
|
|
||||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
if (m_ReceivedNetworkVariableData.Position > (readStartPos + varSize))
|
if (m_ReceivedNetworkVariableData.Position > (readStartPos + varSize))
|
||||||
@@ -234,7 +232,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
networkManager.DeferredMessageManager.DeferMessage(IDeferredMessageManager.TriggerType.OnSpawn, NetworkObjectId, m_ReceivedNetworkVariableData, ref context);
|
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, NetworkObjectId, m_ReceivedNetworkVariableData, ref context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
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(IDeferredNetworkMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace Unity.Netcode
|
|||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(metadata.NetworkObjectId))
|
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(metadata.NetworkObjectId))
|
||||||
{
|
{
|
||||||
networkManager.DeferredMessageManager.DeferMessage(IDeferredMessageManager.TriggerType.OnSpawn, metadata.NetworkObjectId, reader, ref context);
|
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, metadata.NetworkObjectId, reader, ref context);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ namespace Unity.Netcode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NetworkManager.__rpc_func_table.ContainsKey(metadata.NetworkRpcMethodId))
|
if (!NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()].ContainsKey(metadata.NetworkRpcMethodId))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ namespace Unity.Netcode
|
|||||||
payload = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.None, reader.Length - reader.Position);
|
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 (NetworkBehaviour.__rpc_name_table[networkBehaviour.GetType()].TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName))
|
||||||
{
|
{
|
||||||
networkManager.NetworkMetrics.TrackRpcReceived(
|
networkManager.NetworkMetrics.TrackRpcReceived(
|
||||||
context.SenderId,
|
context.SenderId,
|
||||||
@@ -67,11 +67,19 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
NetworkManager.__rpc_func_table[metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams);
|
NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogException(new Exception("Unhandled RPC exception!", ex));
|
Debug.LogException(new Exception("Unhandled RPC exception!", ex));
|
||||||
|
if (networkManager.LogLevel == LogLevel.Developer)
|
||||||
|
{
|
||||||
|
Debug.Log($"RPC Table Contents");
|
||||||
|
foreach (var entry in NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()])
|
||||||
|
{
|
||||||
|
Debug.Log($"{entry.Key} | {entry.Value.Method.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Header placed at the start of each message batch
|
/// Header placed at the start of each message batch
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal struct BatchHeader : INetworkSerializeByMemcpy
|
internal struct NetworkBatchHeader : INetworkSerializeByMemcpy
|
||||||
{
|
{
|
||||||
internal const ushort MagicValue = 0x1160;
|
internal const ushort MagicValue = 0x1160;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -12,6 +12,11 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort Magic;
|
public ushort Magic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of messages in the batch.
|
||||||
|
/// </summary>
|
||||||
|
public ushort BatchCount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total number of bytes in the batch.
|
/// Total number of bytes in the batch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -22,9 +27,5 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong BatchHash;
|
public ulong BatchHash;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total number of messages in the batch.
|
|
||||||
/// </summary>
|
|
||||||
public ushort BatchCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ namespace Unity.Netcode
|
|||||||
internal ref struct NetworkContext
|
internal ref struct NetworkContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An opaque object used to represent the owner of the MessagingSystem that's receiving the message.
|
/// An opaque object used to represent the owner of the NetworkMessageManager that's receiving the message.
|
||||||
/// Outside of testing environments, the type of this variable will be <see cref="NetworkManager"/>
|
/// Outside of testing environments, the type of this variable will be <see cref="NetworkManager"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object SystemOwner;
|
public object SystemOwner;
|
||||||
@@ -24,7 +24,7 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The header data that was sent with the message
|
/// The header data that was sent with the message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MessageHeader Header;
|
public NetworkMessageHeader Header;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The actual serialized size of the header when packed into the buffer
|
/// The actual serialized size of the header when packed into the buffer
|
||||||
|
|||||||
119
Runtime/Messaging/NetworkManagerHooks.cs
Normal file
119
Runtime/Messaging/NetworkManagerHooks.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
internal class NetworkManagerHooks : INetworkHooks
|
||||||
|
{
|
||||||
|
private NetworkManager m_NetworkManager;
|
||||||
|
|
||||||
|
internal NetworkManagerHooks(NetworkManager manager)
|
||||||
|
{
|
||||||
|
m_NetworkManager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
|
||||||
|
{
|
||||||
|
return !m_NetworkManager.MessageManager.StopProcessing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnVerifyCanReceive(ulong senderId, Type messageType, FastBufferReader messageContent, ref NetworkContext context)
|
||||||
|
{
|
||||||
|
if (m_NetworkManager.IsServer)
|
||||||
|
{
|
||||||
|
if (messageType == typeof(ConnectionApprovedMessage))
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"A {nameof(ConnectionApprovedMessage)} was received from a client on the server side. This should not happen. Please report this to the Netcode for GameObjects team at https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues and include the following data: Message Size: {messageContent.Length}. Message Content: {NetworkMessageManager.ByteArrayToString(messageContent.ToArray(), 0, messageContent.Length)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_NetworkManager.ConnectionManager.PendingClients.TryGetValue(senderId, out PendingClient client) && (client.ConnectionState == PendingClient.State.PendingApproval || (client.ConnectionState == PendingClient.State.PendingConnection && messageType != typeof(ConnectionRequestMessage))))
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"Message received from {nameof(senderId)}={senderId} before it has been accepted");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_NetworkManager.ConnectedClients.TryGetValue(senderId, out NetworkClient connectedClient) && messageType == typeof(ConnectionRequestMessage))
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"A {nameof(ConnectionRequestMessage)} was received from a client when the connection has already been established. This should not happen. Please report this to the Netcode for GameObjects team at https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues and include the following data: Message Size: {messageContent.Length}. Message Content: {NetworkMessageManager.ByteArrayToString(messageContent.ToArray(), 0, messageContent.Length)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (messageType == typeof(ConnectionRequestMessage))
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"A {nameof(ConnectionRequestMessage)} was received from the server on the client side. This should not happen. Please report this to the Netcode for GameObjects team at https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues and include the following data: Message Size: {messageContent.Length}. Message Content: {NetworkMessageManager.ByteArrayToString(messageContent.ToArray(), 0, messageContent.Length)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_NetworkManager.IsConnectedClient && messageType == typeof(ConnectionApprovedMessage))
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"A {nameof(ConnectionApprovedMessage)} was received from the server when the connection has already been established. This should not happen. Please report this to the Netcode for GameObjects team at https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues and include the following data: Message Size: {messageContent.Length}. Message Content: {NetworkMessageManager.ByteArrayToString(messageContent.ToArray(), 0, messageContent.Length)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !m_NetworkManager.MessageManager.StopProcessing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Runtime/Messaging/NetworkManagerHooks.cs.meta
Normal file
11
Runtime/Messaging/NetworkManagerHooks.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c2be7fa492911d549bfca52be96c0906
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -3,13 +3,11 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is the header data that's serialized to the network when sending an <see cref="INetworkMessage"/>
|
/// This is the header data that's serialized to the network when sending an <see cref="INetworkMessage"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal struct MessageHeader : INetworkSerializeByMemcpy
|
internal struct NetworkMessageHeader : INetworkSerializeByMemcpy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The byte representation of the message type. This is automatically assigned to each message
|
/// The byte representation of the message type. This is automatically assigned to each message by the NetworkMessageManager.
|
||||||
/// by the MessagingSystem. This value is deterministic only so long as the list of messages remains
|
/// This value is deterministic only so long as the list of messages remains unchanged - if new messages are added or messages are removed, MessageType assignments may be calculated differently.
|
||||||
/// unchanged - if new messages are added or messages are removed, MessageType assignments may be
|
|
||||||
/// calculated differently.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint MessageType;
|
public uint MessageType;
|
||||||
|
|
||||||
@@ -11,22 +11,34 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal class HandlerNotRegisteredException : SystemException
|
internal class HandlerNotRegisteredException : SystemException
|
||||||
{
|
{
|
||||||
public HandlerNotRegisteredException() { }
|
public HandlerNotRegisteredException()
|
||||||
public HandlerNotRegisteredException(string issue) : base(issue) { }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandlerNotRegisteredException(string issue) : base(issue)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class InvalidMessageStructureException : SystemException
|
internal class InvalidMessageStructureException : SystemException
|
||||||
{
|
{
|
||||||
public InvalidMessageStructureException() { }
|
public InvalidMessageStructureException()
|
||||||
public InvalidMessageStructureException(string issue) : base(issue) { }
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MessagingSystem : IDisposable
|
public InvalidMessageStructureException(string issue) : base(issue)
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NetworkMessageManager : IDisposable
|
||||||
|
{
|
||||||
|
public bool StopProcessing = false;
|
||||||
|
|
||||||
private struct ReceiveQueueItem
|
private struct ReceiveQueueItem
|
||||||
{
|
{
|
||||||
public FastBufferReader Reader;
|
public FastBufferReader Reader;
|
||||||
public MessageHeader Header;
|
public NetworkMessageHeader Header;
|
||||||
public ulong SenderId;
|
public ulong SenderId;
|
||||||
public float Timestamp;
|
public float Timestamp;
|
||||||
public int MessageHeaderSerializedSize;
|
public int MessageHeaderSerializedSize;
|
||||||
@@ -34,7 +46,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private struct SendQueueItem
|
private struct SendQueueItem
|
||||||
{
|
{
|
||||||
public BatchHeader BatchHeader;
|
public NetworkBatchHeader BatchHeader;
|
||||||
public FastBufferWriter Writer;
|
public FastBufferWriter Writer;
|
||||||
public readonly NetworkDelivery NetworkDelivery;
|
public readonly NetworkDelivery NetworkDelivery;
|
||||||
|
|
||||||
@@ -42,11 +54,12 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
Writer = new FastBufferWriter(writerSize, writerAllocator, maxWriterSize);
|
Writer = new FastBufferWriter(writerSize, writerAllocator, maxWriterSize);
|
||||||
NetworkDelivery = delivery;
|
NetworkDelivery = delivery;
|
||||||
BatchHeader = new BatchHeader { Magic = BatchHeader.MagicValue };
|
BatchHeader = new NetworkBatchHeader { Magic = NetworkBatchHeader.MagicValue };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal delegate void MessageHandler(FastBufferReader reader, ref NetworkContext context, MessagingSystem system);
|
internal delegate void MessageHandler(FastBufferReader reader, ref NetworkContext context, NetworkMessageManager manager);
|
||||||
|
|
||||||
internal delegate int VersionGetter();
|
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);
|
||||||
@@ -58,6 +71,8 @@ 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>>();
|
||||||
|
|
||||||
|
private HashSet<ulong> m_DisconnectedClients = new HashSet<ulong>();
|
||||||
|
|
||||||
// This is m_PerClientMessageVersion[clientId][messageType] = version
|
// This is m_PerClientMessageVersion[clientId][messageType] = version
|
||||||
private Dictionary<ulong, Dictionary<Type, int>> m_PerClientMessageVersions = new Dictionary<ulong, Dictionary<Type, int>>();
|
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<uint, Type> m_MessagesByHash = new Dictionary<uint, Type>();
|
||||||
@@ -67,7 +82,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private uint m_HighMessageType;
|
private uint m_HighMessageType;
|
||||||
private object m_Owner;
|
private object m_Owner;
|
||||||
private IMessageSender m_MessageSender;
|
private INetworkMessageSender m_Sender;
|
||||||
private bool m_Disposed;
|
private bool m_Disposed;
|
||||||
|
|
||||||
internal Type[] MessageTypes => m_ReverseTypeMap;
|
internal Type[] MessageTypes => m_ReverseTypeMap;
|
||||||
@@ -80,8 +95,11 @@ namespace Unity.Netcode
|
|||||||
return m_MessageTypes[t];
|
return m_MessageTypes[t];
|
||||||
}
|
}
|
||||||
|
|
||||||
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
public const int DefaultNonFragmentedMessageMaxSize = 1300 & ~7; // Round down to nearest word aligned size (1296)
|
||||||
public const int FRAGMENTED_MESSAGE_MAX_SIZE = int.MaxValue;
|
public int NonFragmentedMessageMaxSize = DefaultNonFragmentedMessageMaxSize;
|
||||||
|
public int FragmentedMessageMaxSize = int.MaxValue;
|
||||||
|
|
||||||
|
public Dictionary<ulong, int> PeerMTUSizes = new Dictionary<ulong, int>();
|
||||||
|
|
||||||
internal struct MessageWithHandler
|
internal struct MessageWithHandler
|
||||||
{
|
{
|
||||||
@@ -94,7 +112,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
var prioritizedTypes = new List<MessageWithHandler>();
|
var prioritizedTypes = new List<MessageWithHandler>();
|
||||||
|
|
||||||
// first pass puts the priority message in the first indices
|
// First pass puts the priority message in the first indices
|
||||||
// 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)
|
||||||
{
|
{
|
||||||
@@ -117,17 +135,18 @@ namespace Unity.Netcode
|
|||||||
return prioritizedTypes;
|
return prioritizedTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessagingSystem(IMessageSender messageSender, object owner, IMessageProvider provider = null)
|
public NetworkMessageManager(INetworkMessageSender sender, object owner, INetworkMessageProvider provider = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_MessageSender = messageSender;
|
m_Sender = sender;
|
||||||
m_Owner = owner;
|
m_Owner = owner;
|
||||||
|
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
{
|
{
|
||||||
provider = new ILPPMessageProvider();
|
provider = new ILPPMessageProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
var allowedTypes = provider.GetMessages();
|
var allowedTypes = provider.GetMessages();
|
||||||
|
|
||||||
allowedTypes.Sort((a, b) => string.CompareOrdinal(a.MessageType.FullName, b.MessageType.FullName));
|
allowedTypes.Sort((a, b) => string.CompareOrdinal(a.MessageType.FullName, b.MessageType.FullName));
|
||||||
@@ -144,20 +163,21 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (m_Disposed)
|
if (m_Disposed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't just iterate SendQueues or SendQueues.Keys because ClientDisconnected removes
|
// Can't just iterate SendQueues or SendQueues.Keys because ClientDisconnected removes from the queue.
|
||||||
// from the queue.
|
|
||||||
foreach (var kvp in m_SendQueues)
|
foreach (var kvp in m_SendQueues)
|
||||||
{
|
{
|
||||||
CleanupDisconnectedClient(kvp.Key);
|
ClientDisconnected(kvp.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CleanupDisconnectedClients();
|
||||||
|
|
||||||
for (var queueIndex = 0; queueIndex < m_IncomingMessageQueue.Length; ++queueIndex)
|
for (var queueIndex = 0; queueIndex < m_IncomingMessageQueue.Length; ++queueIndex)
|
||||||
{
|
{
|
||||||
// Avoid copies...
|
// Avoid copies...
|
||||||
@@ -169,7 +189,7 @@ namespace Unity.Netcode
|
|||||||
m_Disposed = true;
|
m_Disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
~MessagingSystem()
|
~NetworkMessageManager()
|
||||||
{
|
{
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
@@ -186,7 +206,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private void RegisterMessageType(MessageWithHandler messageWithHandler)
|
private void RegisterMessageType(MessageWithHandler messageWithHandler)
|
||||||
{
|
{
|
||||||
// if we are out of space, perform amortized linear growth
|
// If we are out of space, perform amortized linear growth
|
||||||
if (m_HighMessageType == m_MessageHandlers.Length)
|
if (m_HighMessageType == m_MessageHandlers.Length)
|
||||||
{
|
{
|
||||||
Array.Resize(ref m_MessageHandlers, 2 * m_MessageHandlers.Length);
|
Array.Resize(ref m_MessageHandlers, 2 * m_MessageHandlers.Length);
|
||||||
@@ -220,19 +240,18 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* nativeData = data.Array)
|
fixed (byte* dataPtr = data.Array)
|
||||||
{
|
{
|
||||||
var batchReader =
|
var batchReader = new FastBufferReader(dataPtr + data.Offset, Allocator.None, data.Count);
|
||||||
new FastBufferReader(nativeData + data.Offset, Allocator.None, data.Count);
|
if (!batchReader.TryBeginRead(sizeof(NetworkBatchHeader)))
|
||||||
if (!batchReader.TryBeginRead(sizeof(BatchHeader)))
|
|
||||||
{
|
{
|
||||||
NetworkLog.LogError("Received a packet too small to contain a BatchHeader. Ignoring it.");
|
NetworkLog.LogError("Received a packet too small to contain a BatchHeader. Ignoring it.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
batchReader.ReadValue(out BatchHeader batchHeader);
|
batchReader.ReadValue(out NetworkBatchHeader batchHeader);
|
||||||
|
|
||||||
if (batchHeader.Magic != BatchHeader.MagicValue)
|
if (batchHeader.Magic != NetworkBatchHeader.MagicValue)
|
||||||
{
|
{
|
||||||
NetworkLog.LogError($"Received a packet with an invalid Magic Value. Please report this to the Netcode for GameObjects team at https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues and include the following data: Offset: {data.Offset}, Size: {data.Count}, Full receive array: {ByteArrayToString(data.Array, 0, data.Array.Length)}");
|
NetworkLog.LogError($"Received a packet with an invalid Magic Value. Please report this to the Netcode for GameObjects team at https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues and include the following data: Offset: {data.Offset}, Size: {data.Count}, Full receive array: {ByteArrayToString(data.Array, 0, data.Array.Length)}");
|
||||||
return;
|
return;
|
||||||
@@ -259,8 +278,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (var messageIdx = 0; messageIdx < batchHeader.BatchCount; ++messageIdx)
|
for (var messageIdx = 0; messageIdx < batchHeader.BatchCount; ++messageIdx)
|
||||||
{
|
{
|
||||||
|
var messageHeader = new NetworkMessageHeader();
|
||||||
var messageHeader = new MessageHeader();
|
|
||||||
var position = batchReader.Position;
|
var position = batchReader.Position;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -280,19 +298,20 @@ namespace Unity.Netcode
|
|||||||
NetworkLog.LogError("Received a message that claimed a size larger than the packet, ending early!");
|
NetworkLog.LogError("Received a message that claimed a size larger than the packet, ending early!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_IncomingMessageQueue.Add(new ReceiveQueueItem
|
m_IncomingMessageQueue.Add(new ReceiveQueueItem
|
||||||
{
|
{
|
||||||
Header = messageHeader,
|
Header = messageHeader,
|
||||||
SenderId = clientId,
|
SenderId = clientId,
|
||||||
Timestamp = receiveTime,
|
Timestamp = receiveTime,
|
||||||
// Copy the data for this message into a new FastBufferReader that owns that memory.
|
// Copy the data for this message into a new FastBufferReader that owns that memory.
|
||||||
// We can't guarantee the memory in the ArraySegment stays valid because we don't own it,
|
// We can't guarantee the memory in the ArraySegment stays valid because we don't own it, so we must move it to memory we do own.
|
||||||
// so we must move it to memory we do own.
|
|
||||||
Reader = new FastBufferReader(batchReader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, (int)messageHeader.MessageSize),
|
Reader = new FastBufferReader(batchReader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, (int)messageHeader.MessageSize),
|
||||||
MessageHeaderSerializedSize = receivedHeaderSize,
|
MessageHeaderSerializedSize = receivedHeaderSize,
|
||||||
});
|
});
|
||||||
batchReader.Seek(batchReader.Position + (int)messageHeader.MessageSize);
|
batchReader.Seek(batchReader.Position + (int)messageHeader.MessageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
m_Hooks[hookIdx].OnAfterReceiveBatch(clientId, batchHeader.BatchCount, batchReader.Length);
|
m_Hooks[hookIdx].OnAfterReceiveBatch(clientId, batchHeader.BatchCount, batchReader.Length);
|
||||||
@@ -320,6 +339,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_MessagesByHash[messageHash];
|
return m_MessagesByHash[messageHash];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +349,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageType = m_MessagesByHash[messageHash];
|
var messageType = m_MessagesByHash[messageHash];
|
||||||
|
|
||||||
if (!m_PerClientMessageVersions.ContainsKey(clientId))
|
if (!m_PerClientMessageVersions.ContainsKey(clientId))
|
||||||
@@ -353,6 +374,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageType = m_MessagesByHash[messagesInIdOrder[i]];
|
var messageType = m_MessagesByHash[messagesInIdOrder[i]];
|
||||||
var oldId = oldTypes[messageType];
|
var oldId = oldTypes[messageType];
|
||||||
var handler = oldHandlers[oldId];
|
var handler = oldHandlers[oldId];
|
||||||
@@ -363,7 +385,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleMessage(in MessageHeader header, FastBufferReader reader, ulong senderId, float timestamp, int serializedHeaderSize)
|
public void HandleMessage(in NetworkMessageHeader header, FastBufferReader reader, ulong senderId, float timestamp, int serializedHeaderSize)
|
||||||
{
|
{
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
@@ -372,6 +394,7 @@ namespace Unity.Netcode
|
|||||||
Debug.LogWarning($"Received a message with invalid message type value {header.MessageType}");
|
Debug.LogWarning($"Received a message with invalid message type value {header.MessageType}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var context = new NetworkContext
|
var context = new NetworkContext
|
||||||
{
|
{
|
||||||
SystemOwner = m_Owner,
|
SystemOwner = m_Owner,
|
||||||
@@ -391,13 +414,12 @@ namespace Unity.Netcode
|
|||||||
var handler = m_MessageHandlers[header.MessageType];
|
var handler = m_MessageHandlers[header.MessageType];
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type, reader.Length + FastBufferWriter.GetWriteSize<MessageHeader>());
|
m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type, reader.Length + FastBufferWriter.GetWriteSize<NetworkMessageHeader>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will also log an exception is if the server knows about a message type the client doesn't know
|
// This will also log an exception is if the server knows about a message type the client doesn't know about.
|
||||||
// about. In this case the handler will be null. It is still an issue the user must deal with: If the
|
// In this case the handler will be null. It is still an issue the user must deal with:
|
||||||
// two connecting builds know about different messages, the server should not send a message to a client
|
// If the two connecting builds know about different messages, the server should not send a message to a client that doesn't know about it
|
||||||
// that doesn't know about it
|
|
||||||
if (handler == null)
|
if (handler == null)
|
||||||
{
|
{
|
||||||
Debug.LogException(new HandlerNotRegisteredException(header.MessageType.ToString()));
|
Debug.LogException(new HandlerNotRegisteredException(header.MessageType.ToString()));
|
||||||
@@ -406,9 +428,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
// No user-land message handler exceptions should escape the receive loop.
|
// No user-land message handler exceptions should escape the receive loop.
|
||||||
// If an exception is throw, the message is ignored.
|
// If an exception is throw, the message is ignored.
|
||||||
// Example use case: A bad message is received that can't be deserialized and throws
|
// Example use case: A bad message is received that can't be deserialized and throws an OverflowException because it specifies a length greater than the number of bytes in it for some dynamic-length value.
|
||||||
// an OverflowException because it specifies a length greater than the number of bytes in it
|
|
||||||
// for some dynamic-length value.
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
handler.Invoke(reader, ref context, this);
|
handler.Invoke(reader, ref context, this);
|
||||||
@@ -418,15 +438,21 @@ namespace Unity.Netcode
|
|||||||
Debug.LogException(e);
|
Debug.LogException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
m_Hooks[hookIdx].OnAfterReceiveMessage(senderId, type, reader.Length + FastBufferWriter.GetWriteSize<MessageHeader>());
|
m_Hooks[hookIdx].OnAfterReceiveMessage(senderId, type, reader.Length + FastBufferWriter.GetWriteSize<NetworkMessageHeader>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe void ProcessIncomingMessageQueue()
|
internal void ProcessIncomingMessageQueue()
|
||||||
{
|
{
|
||||||
|
if (StopProcessing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (var index = 0; index < m_IncomingMessageQueue.Length; ++index)
|
for (var index = 0; index < m_IncomingMessageQueue.Length; ++index)
|
||||||
{
|
{
|
||||||
// Avoid copies...
|
// Avoid copies...
|
||||||
@@ -447,21 +473,22 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_SendQueues[clientId] = new NativeList<SendQueueItem>(16, Allocator.Persistent);
|
m_SendQueues[clientId] = new NativeList<SendQueueItem>(16, Allocator.Persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ClientDisconnected(ulong clientId)
|
internal void ClientDisconnected(ulong clientId)
|
||||||
{
|
{
|
||||||
if (!m_SendQueues.ContainsKey(clientId))
|
m_DisconnectedClients.Add(clientId);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CleanupDisconnectedClient(clientId);
|
|
||||||
m_SendQueues.Remove(clientId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CleanupDisconnectedClient(ulong clientId)
|
private void CleanupDisconnectedClient(ulong clientId)
|
||||||
{
|
{
|
||||||
|
if (!m_SendQueues.ContainsKey(clientId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -469,23 +496,20 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
queue.Dispose();
|
queue.Dispose();
|
||||||
|
m_SendQueues.Remove(clientId);
|
||||||
|
|
||||||
|
m_PerClientMessageVersions.Remove(clientId);
|
||||||
|
PeerMTUSizes.Remove(clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CleanupDisconnectedClients()
|
internal void CleanupDisconnectedClients()
|
||||||
{
|
{
|
||||||
var removeList = new NativeList<ulong>(Allocator.Temp);
|
foreach (var clientId in m_DisconnectedClients)
|
||||||
foreach (var clientId in m_PerClientMessageVersions.Keys)
|
|
||||||
{
|
{
|
||||||
if (!m_SendQueues.ContainsKey(clientId))
|
CleanupDisconnectedClient(clientId);
|
||||||
{
|
|
||||||
removeList.Add(clientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var clientId in removeList)
|
m_DisconnectedClients.Clear();
|
||||||
{
|
|
||||||
m_PerClientMessageVersions.Remove(clientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int CreateMessageAndGetVersion<T>() where T : INetworkMessage, new()
|
public static int CreateMessageAndGetVersion<T>() where T : INetworkMessage, new()
|
||||||
@@ -496,18 +520,22 @@ namespace Unity.Netcode
|
|||||||
internal int GetMessageVersion(Type type, ulong clientId, bool forReceive = false)
|
internal int GetMessageVersion(Type type, ulong clientId, bool forReceive = false)
|
||||||
{
|
{
|
||||||
if (!m_PerClientMessageVersions.TryGetValue(clientId, out var versionMap))
|
if (!m_PerClientMessageVersions.TryGetValue(clientId, out var versionMap))
|
||||||
|
{
|
||||||
|
var networkManager = NetworkManager.Singleton;
|
||||||
|
if (networkManager != null && networkManager.LogLevel == LogLevel.Developer)
|
||||||
{
|
{
|
||||||
if (forReceive)
|
if (forReceive)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Trying to receive {type.Name} from client {clientId} which is not in a connected state.");
|
NetworkLog.LogWarning($"Trying to receive {type.Name} from client {clientId} which is not in a connected state.");
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Trying to send {type.Name} to client {clientId} which is not in a connected state.");
|
NetworkLog.LogWarning($"Trying to send {type.Name} to client {clientId} which is not in a connected state.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!versionMap.TryGetValue(type, out var messageVersion))
|
if (!versionMap.TryGetValue(type, out var messageVersion))
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@@ -516,7 +544,7 @@ namespace Unity.Netcode
|
|||||||
return messageVersion;
|
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, NetworkMessageManager manager) where T : INetworkMessage, new()
|
||||||
{
|
{
|
||||||
var message = new T();
|
var message = new T();
|
||||||
var messageVersion = 0;
|
var messageVersion = 0;
|
||||||
@@ -525,24 +553,25 @@ namespace Unity.Netcode
|
|||||||
// and can't change.
|
// and can't change.
|
||||||
if (typeof(T) != typeof(ConnectionRequestMessage) && typeof(T) != typeof(ConnectionApprovedMessage) && typeof(T) != typeof(DisconnectReasonMessage))
|
if (typeof(T) != typeof(ConnectionRequestMessage) && typeof(T) != typeof(ConnectionApprovedMessage) && typeof(T) != typeof(DisconnectReasonMessage))
|
||||||
{
|
{
|
||||||
messageVersion = system.GetMessageVersion(typeof(T), context.SenderId, true);
|
messageVersion = manager.GetMessageVersion(typeof(T), context.SenderId, true);
|
||||||
if (messageVersion < 0)
|
if (messageVersion < 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.Deserialize(reader, ref context, messageVersion))
|
if (message.Deserialize(reader, ref context, messageVersion))
|
||||||
{
|
{
|
||||||
for (var hookIdx = 0; hookIdx < system.m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < manager.m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
system.m_Hooks[hookIdx].OnBeforeHandleMessage(ref message, ref context);
|
manager.m_Hooks[hookIdx].OnBeforeHandleMessage(ref message, ref context);
|
||||||
}
|
}
|
||||||
|
|
||||||
message.Handle(ref context);
|
message.Handle(ref context);
|
||||||
|
|
||||||
for (var hookIdx = 0; hookIdx < system.m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < manager.m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
system.m_Hooks[hookIdx].OnAfterHandleMessage(ref message, ref context);
|
manager.m_Hooks[hookIdx].OnAfterHandleMessage(ref message, ref context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -574,9 +603,8 @@ namespace Unity.Netcode
|
|||||||
for (var i = 0; i < clientIds.Count; ++i)
|
for (var i = 0; i < clientIds.Count; ++i)
|
||||||
{
|
{
|
||||||
var messageVersion = 0;
|
var messageVersion = 0;
|
||||||
// Special case because this is the message that carries the version info - thus the version info isn't
|
// Special case because this is the message that carries the version info - thus the version info isn't populated yet when we get this.
|
||||||
// populated yet when we get this. The first part of this message always has to be the version data
|
// The first part of this message always has to be the version data and can't change.
|
||||||
// and can't change.
|
|
||||||
if (typeof(TMessageType) != typeof(ConnectionRequestMessage))
|
if (typeof(TMessageType) != typeof(ConnectionRequestMessage))
|
||||||
{
|
{
|
||||||
messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]);
|
messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]);
|
||||||
@@ -594,9 +622,9 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
sentMessageVersions.Add(messageVersion);
|
sentMessageVersions.Add(messageVersion);
|
||||||
|
|
||||||
var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? FRAGMENTED_MESSAGE_MAX_SIZE : NON_FRAGMENTED_MESSAGE_MAX_SIZE;
|
var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? FragmentedMessageMaxSize : NonFragmentedMessageMaxSize;
|
||||||
|
|
||||||
using var tmpSerializer = new FastBufferWriter(NON_FRAGMENTED_MESSAGE_MAX_SIZE - FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp, maxSize - FastBufferWriter.GetWriteSize<MessageHeader>());
|
using var tmpSerializer = new FastBufferWriter(NonFragmentedMessageMaxSize - FastBufferWriter.GetWriteSize<NetworkMessageHeader>(), Allocator.Temp, maxSize - FastBufferWriter.GetWriteSize<NetworkMessageHeader>());
|
||||||
|
|
||||||
message.Serialize(tmpSerializer, messageVersion);
|
message.Serialize(tmpSerializer, messageVersion);
|
||||||
|
|
||||||
@@ -612,9 +640,9 @@ namespace Unity.Netcode
|
|||||||
internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter tmpSerializer, int maxSize, ref TMessageType message, NetworkDelivery delivery, in IReadOnlyList<ulong> clientIds, int messageVersionFilter)
|
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<NetworkMessageHeader>(), Allocator.Temp);
|
||||||
|
|
||||||
var header = new MessageHeader
|
var header = new NetworkMessageHeader
|
||||||
{
|
{
|
||||||
MessageSize = (uint)tmpSerializer.Length,
|
MessageSize = (uint)tmpSerializer.Length,
|
||||||
MessageType = m_MessageTypes[typeof(TMessageType)],
|
MessageType = m_MessageTypes[typeof(TMessageType)],
|
||||||
@@ -624,13 +652,16 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (var i = 0; i < clientIds.Count; ++i)
|
for (var i = 0; i < clientIds.Count; ++i)
|
||||||
{
|
{
|
||||||
var messageVersion = 0;
|
if (m_DisconnectedClients.Contains(clientIds[i]))
|
||||||
// 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
|
continue;
|
||||||
// and can't change.
|
}
|
||||||
|
|
||||||
|
// 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))
|
if (typeof(TMessageType) != typeof(ConnectionRequestMessage))
|
||||||
{
|
{
|
||||||
messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]);
|
var messageVersion = GetMessageVersion(typeof(TMessageType), clientIds[i]);
|
||||||
if (messageVersion < 0)
|
if (messageVersion < 0)
|
||||||
{
|
{
|
||||||
// Client doesn't know this message exists, don't send it at all.
|
// Client doesn't know this message exists, don't send it at all.
|
||||||
@@ -650,6 +681,21 @@ namespace Unity.Netcode
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var startSize = NonFragmentedMessageMaxSize;
|
||||||
|
if (delivery != NetworkDelivery.ReliableFragmentedSequenced)
|
||||||
|
{
|
||||||
|
if (PeerMTUSizes.TryGetValue(clientId, out var clientMaxSize))
|
||||||
|
{
|
||||||
|
maxSize = clientMaxSize;
|
||||||
|
}
|
||||||
|
startSize = maxSize;
|
||||||
|
if (tmpSerializer.Position >= maxSize)
|
||||||
|
{
|
||||||
|
Debug.LogError($"MTU size for {clientId} is too small to contain a message of type {typeof(TMessageType).FullName}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
m_Hooks[hookIdx].OnBeforeSendMessage(clientId, ref message, delivery);
|
m_Hooks[hookIdx].OnBeforeSendMessage(clientId, ref message, delivery);
|
||||||
@@ -658,20 +704,16 @@ namespace Unity.Netcode
|
|||||||
var sendQueueItem = m_SendQueues[clientId];
|
var sendQueueItem = m_SendQueues[clientId];
|
||||||
if (sendQueueItem.Length == 0)
|
if (sendQueueItem.Length == 0)
|
||||||
{
|
{
|
||||||
sendQueueItem.Add(new SendQueueItem(delivery, NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.TempJob,
|
sendQueueItem.Add(new SendQueueItem(delivery, startSize, Allocator.TempJob, maxSize));
|
||||||
maxSize));
|
sendQueueItem.ElementAt(0).Writer.Seek(sizeof(NetworkBatchHeader));
|
||||||
sendQueueItem.ElementAt(0).Writer.Seek(sizeof(BatchHeader));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ref var lastQueueItem = ref sendQueueItem.ElementAt(sendQueueItem.Length - 1);
|
ref var lastQueueItem = ref sendQueueItem.ElementAt(sendQueueItem.Length - 1);
|
||||||
if (lastQueueItem.NetworkDelivery != delivery ||
|
if (lastQueueItem.NetworkDelivery != delivery || lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position < tmpSerializer.Length + headerSerializer.Length)
|
||||||
lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position
|
|
||||||
< tmpSerializer.Length + headerSerializer.Length)
|
|
||||||
{
|
{
|
||||||
sendQueueItem.Add(new SendQueueItem(delivery, NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.TempJob,
|
sendQueueItem.Add(new SendQueueItem(delivery, startSize, Allocator.TempJob, maxSize));
|
||||||
maxSize));
|
sendQueueItem.ElementAt(sendQueueItem.Length - 1).Writer.Seek(sizeof(NetworkBatchHeader));
|
||||||
sendQueueItem.ElementAt(sendQueueItem.Length - 1).Writer.Seek(sizeof(BatchHeader));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,6 +810,11 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal unsafe void ProcessSendQueues()
|
internal unsafe void ProcessSendQueues()
|
||||||
{
|
{
|
||||||
|
if (StopProcessing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var kvp in m_SendQueues)
|
foreach (var kvp in m_SendQueues)
|
||||||
{
|
{
|
||||||
var clientId = kvp.Key;
|
var clientId = kvp.Key;
|
||||||
@@ -775,6 +822,15 @@ namespace Unity.Netcode
|
|||||||
for (var i = 0; i < sendQueueItem.Length; ++i)
|
for (var i = 0; i < sendQueueItem.Length; ++i)
|
||||||
{
|
{
|
||||||
ref var queueItem = ref sendQueueItem.ElementAt(i);
|
ref var queueItem = ref sendQueueItem.ElementAt(i);
|
||||||
|
// This is checked at every iteration because
|
||||||
|
// 1) each writer needs to be disposed, so we have to do the full loop regardless, and
|
||||||
|
// 2) the call to m_MessageSender.Send() may result in calling ClientDisconnected(), so the result of this check may change partway through iteration
|
||||||
|
if (m_DisconnectedClients.Contains(clientId))
|
||||||
|
{
|
||||||
|
queueItem.Writer.Dispose();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (queueItem.BatchHeader.BatchCount == 0)
|
if (queueItem.BatchHeader.BatchCount == 0)
|
||||||
{
|
{
|
||||||
queueItem.Writer.Dispose();
|
queueItem.Writer.Dispose();
|
||||||
@@ -789,18 +845,24 @@ namespace Unity.Netcode
|
|||||||
queueItem.Writer.Seek(0);
|
queueItem.Writer.Seek(0);
|
||||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||||
// Skipping the Verify and sneaking the write mark in because we know it's fine.
|
// Skipping the Verify and sneaking the write mark in because we know it's fine.
|
||||||
queueItem.Writer.Handle->AllowedWriteMark = sizeof(BatchHeader);
|
queueItem.Writer.Handle->AllowedWriteMark = sizeof(NetworkBatchHeader);
|
||||||
#endif
|
#endif
|
||||||
queueItem.BatchHeader.BatchHash = XXHash.Hash64(queueItem.Writer.GetUnsafePtr() + sizeof(BatchHeader), queueItem.Writer.Length - sizeof(BatchHeader));
|
|
||||||
|
|
||||||
queueItem.BatchHeader.BatchSize = queueItem.Writer.Length;
|
|
||||||
|
var alignedLength = (queueItem.Writer.Length + 7) & ~7;
|
||||||
|
queueItem.Writer.TryBeginWrite(alignedLength);
|
||||||
|
|
||||||
|
queueItem.BatchHeader.BatchHash = XXHash.Hash64(queueItem.Writer.GetUnsafePtr() + sizeof(NetworkBatchHeader), alignedLength - sizeof(NetworkBatchHeader));
|
||||||
|
|
||||||
|
queueItem.BatchHeader.BatchSize = alignedLength;
|
||||||
|
|
||||||
queueItem.Writer.WriteValue(queueItem.BatchHeader);
|
queueItem.Writer.WriteValue(queueItem.BatchHeader);
|
||||||
|
queueItem.Writer.Seek(alignedLength);
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_MessageSender.Send(clientId, queueItem.NetworkDelivery, queueItem.Writer);
|
m_Sender.Send(clientId, queueItem.NetworkDelivery, queueItem.Writer);
|
||||||
|
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
@@ -812,6 +874,7 @@ namespace Unity.Netcode
|
|||||||
queueItem.Writer.Dispose();
|
queueItem.Writer.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendQueueItem.Clear();
|
sendQueueItem.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
44
Runtime/Metrics/NetworkMetricsManager.cs
Normal file
44
Runtime/Metrics/NetworkMetricsManager.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
using Unity.Multiplayer.Tools;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This probably needs to all be migrated into <see cref="NetworkConnectionManager"/>, but
|
||||||
|
/// keeping it separated for the time being
|
||||||
|
/// </summary>
|
||||||
|
internal class NetworkMetricsManager
|
||||||
|
{
|
||||||
|
internal INetworkMetrics NetworkMetrics { get; private set; }
|
||||||
|
|
||||||
|
private NetworkManager m_NetworkManager;
|
||||||
|
|
||||||
|
public void UpdateMetrics()
|
||||||
|
{
|
||||||
|
NetworkMetrics.UpdateNetworkObjectsCount(m_NetworkManager.SpawnManager.SpawnedObjects.Count);
|
||||||
|
NetworkMetrics.UpdateConnectionsCount((m_NetworkManager.IsServer) ? m_NetworkManager.ConnectionManager.ConnectedClients.Count : 1);
|
||||||
|
NetworkMetrics.DispatchFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
m_NetworkManager = networkManager;
|
||||||
|
if (NetworkMetrics == null)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
NetworkMetrics = new NetworkMetrics();
|
||||||
|
#else
|
||||||
|
NetworkMetrics = new NullNetworkMetrics();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
NetworkSolutionInterface.SetInterface(new NetworkSolutionInterfaceParameters
|
||||||
|
{
|
||||||
|
NetworkObjectProvider = new NetworkObjectProvider(networkManager),
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Runtime/Metrics/NetworkMetricsManager.cs.meta
Normal file
11
Runtime/Metrics/NetworkMetricsManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3b20a0fe127a24d48867c2ee448bdb1b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
@@ -9,6 +8,7 @@ namespace Unity.Netcode
|
|||||||
/// Event based NetworkVariable container for syncing Lists
|
/// Event based NetworkVariable container for syncing Lists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type for the list</typeparam>
|
/// <typeparam name="T">The type for the list</typeparam>
|
||||||
|
[GenerateSerializationForGenericParameter(0)]
|
||||||
public class NetworkList<T> : NetworkVariableBase where T : unmanaged, IEquatable<T>
|
public class NetworkList<T> : NetworkVariableBase where T : unmanaged, IEquatable<T>
|
||||||
{
|
{
|
||||||
private NativeList<T> m_List = new NativeList<T>(64, Allocator.Persistent);
|
private NativeList<T> m_List = new NativeList<T>(64, Allocator.Persistent);
|
||||||
@@ -68,14 +68,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void MarkNetworkObjectDirty()
|
internal void MarkNetworkObjectDirty()
|
||||||
{
|
{
|
||||||
if (m_NetworkBehaviour == null)
|
MarkNetworkBehaviourDirty();
|
||||||
{
|
|
||||||
Debug.LogWarning($"NetworkList is written to, but doesn't know its NetworkBehaviour yet. " +
|
|
||||||
"Are you modifying a NetworkList before the NetworkObject is spawned?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_NetworkBehaviour.NetworkManager.MarkNetworkObjectDirty(m_NetworkBehaviour.NetworkObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -490,12 +483,14 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var value = m_List[index];
|
||||||
m_List.RemoveAt(index);
|
m_List.RemoveAt(index);
|
||||||
|
|
||||||
var listEvent = new NetworkListEvent<T>()
|
var listEvent = new NetworkListEvent<T>()
|
||||||
{
|
{
|
||||||
Type = NetworkListEvent<T>.EventType.RemoveAt,
|
Type = NetworkListEvent<T>.EventType.RemoveAt,
|
||||||
Index = index
|
Index = index,
|
||||||
|
Value = value
|
||||||
};
|
};
|
||||||
|
|
||||||
HandleAddListEvent(listEvent);
|
HandleAddListEvent(listEvent);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">the unmanaged type for <see cref="NetworkVariable{T}"/> </typeparam>
|
/// <typeparam name="T">the unmanaged type for <see cref="NetworkVariable{T}"/> </typeparam>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
[GenerateSerializationForGenericParameter(0)]
|
||||||
public class NetworkVariable<T> : NetworkVariableBase
|
public class NetworkVariable<T> : NetworkVariableBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -33,6 +34,13 @@ namespace Unity.Netcode
|
|||||||
: base(readPerm, writePerm)
|
: base(readPerm, writePerm)
|
||||||
{
|
{
|
||||||
m_InternalValue = value;
|
m_InternalValue = value;
|
||||||
|
// Since we start with IsDirty = true, this doesn't need to be duplicated
|
||||||
|
// right away. It won't get read until after ResetDirty() is called, and
|
||||||
|
// the duplicate will be made there. Avoiding calling
|
||||||
|
// NetworkVariableSerialization<T>.Duplicate() is important because calling
|
||||||
|
// it in the constructor might not give users enough time to set the
|
||||||
|
// DuplicateValue callback if they're using UserNetworkVariableSerialization
|
||||||
|
m_PreviousValue = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,6 +49,11 @@ namespace Unity.Netcode
|
|||||||
[SerializeField]
|
[SerializeField]
|
||||||
private protected T m_InternalValue;
|
private protected T m_InternalValue;
|
||||||
|
|
||||||
|
private protected T m_PreviousValue;
|
||||||
|
|
||||||
|
private bool m_HasPreviousValue;
|
||||||
|
private bool m_IsDisposed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The value of the NetworkVariable container
|
/// The value of the NetworkVariable container
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -61,6 +74,83 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
Set(value);
|
Set(value);
|
||||||
|
m_IsDisposed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ref T RefValue()
|
||||||
|
{
|
||||||
|
return ref m_InternalValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
if (m_IsDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_IsDisposed = true;
|
||||||
|
if (m_InternalValue is IDisposable internalValueDisposable)
|
||||||
|
{
|
||||||
|
internalValueDisposable.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_InternalValue = default;
|
||||||
|
if (m_HasPreviousValue && m_PreviousValue is IDisposable previousValueDisposable)
|
||||||
|
{
|
||||||
|
m_HasPreviousValue = false;
|
||||||
|
previousValueDisposable.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PreviousValue = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
~NetworkVariable()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets Whether or not the container is dirty
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Whether or not the container is dirty</returns>
|
||||||
|
public override bool IsDirty()
|
||||||
|
{
|
||||||
|
// For most cases we can use the dirty flag.
|
||||||
|
// This doesn't work for cases where we're wrapping more complex types
|
||||||
|
// like INetworkSerializable, NativeList, NativeArray, etc.
|
||||||
|
// Changes to the values in those types don't call the Value.set method,
|
||||||
|
// so we can't catch those changes and need to compare the current value
|
||||||
|
// against the previous one.
|
||||||
|
if (base.IsDirty())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the dirty value so we don't perform this again if we already know we're dirty
|
||||||
|
// Unfortunately we can't cache the NOT dirty state, because that might change
|
||||||
|
// in between to checks... but the DIRTY state won't change until ResetDirty()
|
||||||
|
// is called.
|
||||||
|
var dirty = !NetworkVariableSerialization<T>.AreEqual(ref m_PreviousValue, ref m_InternalValue);
|
||||||
|
SetDirty(dirty);
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the dirty state and marks the variable as synced / clean
|
||||||
|
/// </summary>
|
||||||
|
public override void ResetDirty()
|
||||||
|
{
|
||||||
|
base.ResetDirty();
|
||||||
|
// Resetting the dirty value declares that the current value is not dirty
|
||||||
|
// Therefore, we set the m_PreviousValue field to a duplicate of the current
|
||||||
|
// field, so that our next dirty check is made against the current "not dirty"
|
||||||
|
// value.
|
||||||
|
if (!m_HasPreviousValue || !NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref m_PreviousValue))
|
||||||
|
{
|
||||||
|
m_HasPreviousValue = true;
|
||||||
|
NetworkVariableSerialization<T>.Duplicate(m_InternalValue, ref m_PreviousValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ namespace Unity.Netcode
|
|||||||
m_IsDirty = isDirty;
|
m_IsDirty = isDirty;
|
||||||
|
|
||||||
if (m_IsDirty)
|
if (m_IsDirty)
|
||||||
|
{
|
||||||
|
MarkNetworkBehaviourDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void MarkNetworkBehaviourDirty()
|
||||||
{
|
{
|
||||||
if (m_NetworkBehaviour == null)
|
if (m_NetworkBehaviour == null)
|
||||||
{
|
{
|
||||||
@@ -94,8 +100,8 @@ namespace Unity.Netcode
|
|||||||
"Are you modifying a NetworkVariable before the NetworkObject is spawned?");
|
"Are you modifying a NetworkVariable before the NetworkObject is spawned?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_NetworkBehaviour.NetworkManager.MarkNetworkObjectDirty(m_NetworkBehaviour.NetworkObject);
|
|
||||||
}
|
m_NetworkBehaviour.NetworkManager.BehaviourUpdater.AddForUpdate(m_NetworkBehaviour.NetworkObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ namespace Unity.Netcode
|
|||||||
// of it to pass it as a ref parameter.
|
// of it to pass it as a ref parameter.
|
||||||
public void Write(FastBufferWriter writer, ref T value);
|
public void Write(FastBufferWriter writer, ref T value);
|
||||||
public void Read(FastBufferReader reader, ref T value);
|
public void Read(FastBufferReader reader, ref T value);
|
||||||
|
internal void ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator);
|
||||||
|
public void Duplicate(in T value, ref T duplicatedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -35,6 +37,16 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<short>.ReadWithAllocator(FastBufferReader reader, out short value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in short value, ref short duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -50,6 +62,16 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<ushort>.ReadWithAllocator(FastBufferReader reader, out ushort value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in ushort value, ref ushort duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -65,6 +87,16 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<int>.ReadWithAllocator(FastBufferReader reader, out int value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in int value, ref int duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -80,6 +112,16 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<uint>.ReadWithAllocator(FastBufferReader reader, out uint value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in uint value, ref uint duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -95,6 +137,16 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<long>.ReadWithAllocator(FastBufferReader reader, out long value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in long value, ref long duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -110,6 +162,16 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
ByteUnpacker.ReadValueBitPacked(reader, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<ulong>.ReadWithAllocator(FastBufferReader reader, out ulong value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in ulong value, ref ulong duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -130,8 +192,84 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
reader.ReadUnmanagedSafe(out value);
|
reader.ReadUnmanagedSafe(out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<T>.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in T value, ref T duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class UnmanagedArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref NativeArray<T> value)
|
||||||
|
{
|
||||||
|
writer.WriteUnmanagedSafe(value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref NativeArray<T> value)
|
||||||
|
{
|
||||||
|
value.Dispose();
|
||||||
|
reader.ReadUnmanagedSafe(out value, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<NativeArray<T>>.ReadWithAllocator(FastBufferReader reader, out NativeArray<T> value, Allocator allocator)
|
||||||
|
{
|
||||||
|
reader.ReadUnmanagedSafe(out value, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in NativeArray<T> value, ref NativeArray<T> duplicatedValue)
|
||||||
|
{
|
||||||
|
if (!duplicatedValue.IsCreated || duplicatedValue.Length != value.Length)
|
||||||
|
{
|
||||||
|
if (duplicatedValue.IsCreated)
|
||||||
|
{
|
||||||
|
duplicatedValue.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue = new NativeArray<T>(value.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue.CopyFrom(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
internal class UnmanagedListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref NativeList<T> value)
|
||||||
|
{
|
||||||
|
writer.WriteUnmanagedSafe(value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref NativeList<T> value)
|
||||||
|
{
|
||||||
|
reader.ReadUnmanagedSafeInPlace(ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<NativeList<T>>.ReadWithAllocator(FastBufferReader reader, out NativeList<T> value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in NativeList<T> value, ref NativeList<T> duplicatedValue)
|
||||||
|
{
|
||||||
|
if (!duplicatedValue.IsCreated)
|
||||||
|
{
|
||||||
|
duplicatedValue = new NativeList<T>(value.Length, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
else if (value.Length != duplicatedValue.Length)
|
||||||
|
{
|
||||||
|
duplicatedValue.ResizeUninitialized(value.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue.CopyFrom(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializer for FixedStrings
|
/// Serializer for FixedStrings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -146,8 +284,92 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
reader.ReadValueSafeInPlace(ref value);
|
reader.ReadValueSafeInPlace(ref value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<T>.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in T value, ref T duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serializer for FixedStrings
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
internal class FixedStringArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref NativeArray<T> value)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref NativeArray<T> value)
|
||||||
|
{
|
||||||
|
value.Dispose();
|
||||||
|
reader.ReadValueSafe(out value, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<NativeArray<T>>.ReadWithAllocator(FastBufferReader reader, out NativeArray<T> value, Allocator allocator)
|
||||||
|
{
|
||||||
|
reader.ReadValueSafe(out value, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in NativeArray<T> value, ref NativeArray<T> duplicatedValue)
|
||||||
|
{
|
||||||
|
if (!duplicatedValue.IsCreated || duplicatedValue.Length != value.Length)
|
||||||
|
{
|
||||||
|
if (duplicatedValue.IsCreated)
|
||||||
|
{
|
||||||
|
duplicatedValue.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue = new NativeArray<T>(value.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue.CopyFrom(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Serializer for FixedStrings
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
internal class FixedStringListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref NativeList<T> value)
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref NativeList<T> value)
|
||||||
|
{
|
||||||
|
reader.ReadValueSafeInPlace(ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<NativeList<T>>.ReadWithAllocator(FastBufferReader reader, out NativeList<T> value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in NativeList<T> value, ref NativeList<T> duplicatedValue)
|
||||||
|
{
|
||||||
|
if (!duplicatedValue.IsCreated)
|
||||||
|
{
|
||||||
|
duplicatedValue = new NativeList<T>(value.Length, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
else if (value.Length != duplicatedValue.Length)
|
||||||
|
{
|
||||||
|
duplicatedValue.ResizeUninitialized(value.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue.CopyFrom(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializer for unmanaged INetworkSerializable types
|
/// Serializer for unmanaged INetworkSerializable types
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -163,10 +385,93 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
|
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
|
||||||
value.NetworkSerialize(bufferSerializer);
|
value.NetworkSerialize(bufferSerializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<T>.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in T value, ref T duplicatedValue)
|
||||||
|
{
|
||||||
|
duplicatedValue = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serializer for unmanaged INetworkSerializable types
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
internal class UnmanagedNetworkSerializableArraySerializer<T> : INetworkVariableSerializer<NativeArray<T>> where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref NativeArray<T> value)
|
||||||
|
{
|
||||||
|
writer.WriteNetworkSerializable(value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref NativeArray<T> value)
|
||||||
|
{
|
||||||
|
value.Dispose();
|
||||||
|
reader.ReadNetworkSerializable(out value, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<NativeArray<T>>.ReadWithAllocator(FastBufferReader reader, out NativeArray<T> value, Allocator allocator)
|
||||||
|
{
|
||||||
|
reader.ReadNetworkSerializable(out value, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in NativeArray<T> value, ref NativeArray<T> duplicatedValue)
|
||||||
|
{
|
||||||
|
if (!duplicatedValue.IsCreated || duplicatedValue.Length != value.Length)
|
||||||
|
{
|
||||||
|
if (duplicatedValue.IsCreated)
|
||||||
|
{
|
||||||
|
duplicatedValue.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue = new NativeArray<T>(value.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue.CopyFrom(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Serializer for unmanaged INetworkSerializable types
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
internal class UnmanagedNetworkSerializableListSerializer<T> : INetworkVariableSerializer<NativeList<T>> where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
public void Write(FastBufferWriter writer, ref NativeList<T> value)
|
||||||
|
{
|
||||||
|
writer.WriteNetworkSerializable(value);
|
||||||
|
}
|
||||||
|
public void Read(FastBufferReader reader, ref NativeList<T> value)
|
||||||
|
{
|
||||||
|
reader.ReadNetworkSerializableInPlace(ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<NativeList<T>>.ReadWithAllocator(FastBufferReader reader, out NativeList<T> value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in NativeList<T> value, ref NativeList<T> duplicatedValue)
|
||||||
|
{
|
||||||
|
if (!duplicatedValue.IsCreated)
|
||||||
|
{
|
||||||
|
duplicatedValue = new NativeList<T>(value.Length, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
else if (value.Length != duplicatedValue.Length)
|
||||||
|
{
|
||||||
|
duplicatedValue.ResizeUninitialized(value.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
duplicatedValue.CopyFrom(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializer for managed INetworkSerializable types, which differs from the unmanaged implementation in that it
|
/// Serializer for managed INetworkSerializable types, which differs from the unmanaged implementation in that it
|
||||||
/// has to be null-aware
|
/// has to be null-aware
|
||||||
@@ -201,6 +506,21 @@ namespace Unity.Netcode
|
|||||||
value.NetworkSerialize(bufferSerializer);
|
value.NetworkSerialize(bufferSerializer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<T>.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in T value, ref T duplicatedValue)
|
||||||
|
{
|
||||||
|
using var writer = new FastBufferWriter(256, Allocator.Temp, int.MaxValue);
|
||||||
|
var refValue = value;
|
||||||
|
Write(writer, ref refValue);
|
||||||
|
|
||||||
|
using var reader = new FastBufferReader(writer, Allocator.None);
|
||||||
|
Read(reader, ref duplicatedValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -227,14 +547,26 @@ namespace Unity.Netcode
|
|||||||
public delegate void ReadValueDelegate(FastBufferReader reader, out T value);
|
public delegate void ReadValueDelegate(FastBufferReader reader, out T value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="WriteValueDelegate"/> delegate handler declaration
|
/// The read value delegate handler definition
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The <see cref="FastBufferReader"/> to read the value of type `T`</param>
|
||||||
|
/// <param name="value">The value of type `T` to be read</param>
|
||||||
|
public delegate void DuplicateValueDelegate(in T value, ref T duplicatedValue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback to write a value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static WriteValueDelegate WriteValue;
|
public static WriteValueDelegate WriteValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="ReadValueDelegate"/> delegate handler declaration
|
/// Callback to read a value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ReadValueDelegate ReadValue;
|
public static ReadValueDelegate ReadValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback to create a duplicate of a value, used to check for dirty status.
|
||||||
|
/// </summary>
|
||||||
|
public static DuplicateValueDelegate DuplicateValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -248,22 +580,41 @@ namespace Unity.Netcode
|
|||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
internal class FallbackSerializer<T> : INetworkVariableSerializer<T>
|
internal class FallbackSerializer<T> : INetworkVariableSerializer<T>
|
||||||
{
|
{
|
||||||
|
private void ThrowArgumentError()
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Serialization has not been generated for type {typeof(T).FullName}. This can be addressed by adding a [{nameof(GenerateSerializationForGenericParameterAttribute)}] to your generic class that serializes this value (if you are using one), adding [{nameof(GenerateSerializationForTypeAttribute)}(typeof({typeof(T).FullName})] to the class or method that is attempting to serialize it, or creating a field on a {nameof(NetworkBehaviour)} of type {nameof(NetworkVariable<T>)}. If this error continues to appear after doing one of those things and this is a type you can change, then either implement {nameof(INetworkSerializable)} or mark it as serializable by memcpy by adding {nameof(INetworkSerializeByMemcpy)} to its interface list to enable automatic serialization generation. If not, assign serialization code to {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.WriteValue)}, {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.ReadValue)}, and {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.DuplicateValue)}, or if it's serializable by memcpy (contains no pointers), wrap it in {typeof(ForceNetworkSerializeByMemcpy<>).Name}.");
|
||||||
|
}
|
||||||
|
|
||||||
public void Write(FastBufferWriter writer, ref T value)
|
public void Write(FastBufferWriter writer, ref T value)
|
||||||
{
|
{
|
||||||
if (UserNetworkVariableSerialization<T>.ReadValue == null || UserNetworkVariableSerialization<T>.WriteValue == null)
|
if (UserNetworkVariableSerialization<T>.ReadValue == null || UserNetworkVariableSerialization<T>.WriteValue == null || UserNetworkVariableSerialization<T>.DuplicateValue == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Type {typeof(T).FullName} is not supported by {typeof(NetworkVariable<>).Name}. If this is a type you can change, then either implement {nameof(INetworkSerializable)} or mark it as serializable by memcpy by adding {nameof(INetworkSerializeByMemcpy)} to its interface list. If not, assign serialization code to {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.WriteValue)} and {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.ReadValue)}, or if it's serializable by memcpy (contains no pointers), wrap it in {typeof(ForceNetworkSerializeByMemcpy<>).Name}.");
|
ThrowArgumentError();
|
||||||
}
|
}
|
||||||
UserNetworkVariableSerialization<T>.WriteValue(writer, value);
|
UserNetworkVariableSerialization<T>.WriteValue(writer, value);
|
||||||
}
|
}
|
||||||
public void Read(FastBufferReader reader, ref T value)
|
public void Read(FastBufferReader reader, ref T value)
|
||||||
{
|
{
|
||||||
if (UserNetworkVariableSerialization<T>.ReadValue == null || UserNetworkVariableSerialization<T>.WriteValue == null)
|
if (UserNetworkVariableSerialization<T>.ReadValue == null || UserNetworkVariableSerialization<T>.WriteValue == null || UserNetworkVariableSerialization<T>.DuplicateValue == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Type {typeof(T).FullName} is not supported by {typeof(NetworkVariable<>).Name}. If this is a type you can change, then either implement {nameof(INetworkSerializable)} or mark it as serializable by memcpy by adding {nameof(INetworkSerializeByMemcpy)} to its interface list. If not, assign serialization code to {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.WriteValue)} and {nameof(UserNetworkVariableSerialization<T>)}.{nameof(UserNetworkVariableSerialization<T>.ReadValue)}, or if it's serializable by memcpy (contains no pointers), wrap it in {typeof(ForceNetworkSerializeByMemcpy<>).Name}.");
|
ThrowArgumentError();
|
||||||
}
|
}
|
||||||
UserNetworkVariableSerialization<T>.ReadValue(reader, out value);
|
UserNetworkVariableSerialization<T>.ReadValue(reader, out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INetworkVariableSerializer<T>.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Duplicate(in T value, ref T duplicatedValue)
|
||||||
|
{
|
||||||
|
if (UserNetworkVariableSerialization<T>.ReadValue == null || UserNetworkVariableSerialization<T>.WriteValue == null || UserNetworkVariableSerialization<T>.DuplicateValue == null)
|
||||||
|
{
|
||||||
|
ThrowArgumentError();
|
||||||
|
}
|
||||||
|
UserNetworkVariableSerialization<T>.DuplicateValue(value, ref duplicatedValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -309,6 +660,26 @@ namespace Unity.Netcode
|
|||||||
NetworkVariableSerialization<T>.Serializer = new UnmanagedTypeSerializer<T>();
|
NetworkVariableSerialization<T>.Serializer = new UnmanagedTypeSerializer<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registeres an unmanaged type that will be serialized by a direct memcpy into a buffer
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeSerializer_UnmanagedByMemcpyArray<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer = new UnmanagedArraySerializer<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Registeres an unmanaged type that will be serialized by a direct memcpy into a buffer
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeSerializer_UnmanagedByMemcpyList<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.Serializer = new UnmanagedListSerializer<T>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers an unmanaged type that implements INetworkSerializable and will be serialized through a call to
|
/// Registers an unmanaged type that implements INetworkSerializable and will be serialized through a call to
|
||||||
/// NetworkSerialize
|
/// NetworkSerialize
|
||||||
@@ -319,6 +690,28 @@ namespace Unity.Netcode
|
|||||||
NetworkVariableSerialization<T>.Serializer = new UnmanagedNetworkSerializableSerializer<T>();
|
NetworkVariableSerialization<T>.Serializer = new UnmanagedNetworkSerializableSerializer<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an unmanaged type that implements INetworkSerializable and will be serialized through a call to
|
||||||
|
/// NetworkSerialize
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeSerializer_UnmanagedINetworkSerializableArray<T>() where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer = new UnmanagedNetworkSerializableArraySerializer<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an unmanaged type that implements INetworkSerializable and will be serialized through a call to
|
||||||
|
/// NetworkSerialize
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeSerializer_UnmanagedINetworkSerializableList<T>() where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.Serializer = new UnmanagedNetworkSerializableListSerializer<T>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a managed type that implements INetworkSerializable and will be serialized through a call to
|
/// Registers a managed type that implements INetworkSerializable and will be serialized through a call to
|
||||||
/// NetworkSerialize
|
/// NetworkSerialize
|
||||||
@@ -339,6 +732,28 @@ namespace Unity.Netcode
|
|||||||
NetworkVariableSerialization<T>.Serializer = new FixedStringSerializer<T>();
|
NetworkVariableSerialization<T>.Serializer = new FixedStringSerializer<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a FixedString type that will be serialized through FastBufferReader/FastBufferWriter's FixedString
|
||||||
|
/// serializers
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeSerializer_FixedStringArray<T>() where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer = new FixedStringArraySerializer<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a FixedString type that will be serialized through FastBufferReader/FastBufferWriter's FixedString
|
||||||
|
/// serializers
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeSerializer_FixedStringList<T>() where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.Serializer = new FixedStringListSerializer<T>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a managed type that will be checked for equality using T.Equals()
|
/// Registers a managed type that will be checked for equality using T.Equals()
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -357,6 +772,26 @@ namespace Unity.Netcode
|
|||||||
NetworkVariableSerialization<T>.AreEqual = NetworkVariableSerialization<T>.EqualityEquals;
|
NetworkVariableSerialization<T>.AreEqual = NetworkVariableSerialization<T>.EqualityEquals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an unmanaged type that will be checked for equality using T.Equals()
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeEqualityChecker_UnmanagedIEquatableArray<T>() where T : unmanaged, IEquatable<T>
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.AreEqual = NetworkVariableSerialization<T>.EqualityEqualsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an unmanaged type that will be checked for equality using T.Equals()
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeEqualityChecker_UnmanagedIEquatableList<T>() where T : unmanaged, IEquatable<T>
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.AreEqual = NetworkVariableSerialization<T>.EqualityEqualsList;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers an unmanaged type that will be checked for equality using memcmp and only considered
|
/// Registers an unmanaged type that will be checked for equality using memcmp and only considered
|
||||||
/// equal if they are bitwise equivalent in memory
|
/// equal if they are bitwise equivalent in memory
|
||||||
@@ -367,6 +802,28 @@ namespace Unity.Netcode
|
|||||||
NetworkVariableSerialization<T>.AreEqual = NetworkVariableSerialization<T>.ValueEquals;
|
NetworkVariableSerialization<T>.AreEqual = NetworkVariableSerialization<T>.ValueEquals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an unmanaged type that will be checked for equality using memcmp and only considered
|
||||||
|
/// equal if they are bitwise equivalent in memory
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeEqualityChecker_UnmanagedValueEqualsArray<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.AreEqual = NetworkVariableSerialization<T>.ValueEqualsArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Registers an unmanaged type that will be checked for equality using memcmp and only considered
|
||||||
|
/// equal if they are bitwise equivalent in memory
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public static void InitializeEqualityChecker_UnmanagedValueEqualsList<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.AreEqual = NetworkVariableSerialization<T>.ValueEqualsList;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers a managed type that will be checked for equality using the == operator
|
/// Registers a managed type that will be checked for equality using the == operator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -389,8 +846,95 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal static INetworkVariableSerializer<T> Serializer = new FallbackSerializer<T>();
|
internal static INetworkVariableSerializer<T> Serializer = new FallbackSerializer<T>();
|
||||||
|
|
||||||
internal delegate bool EqualsDelegate(ref T a, ref T b);
|
/// <summary>
|
||||||
internal static EqualsDelegate AreEqual;
|
/// A callback to check if two values are equal.
|
||||||
|
/// </summary>
|
||||||
|
public delegate bool EqualsDelegate(ref T a, ref T b);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses the most efficient mechanism for a given type to determine if two values are equal.
|
||||||
|
/// For types that implement <see cref="IEquatable{T}"/>, it will call the Equals() method.
|
||||||
|
/// For unmanaged types, it will do a bytewise memory comparison.
|
||||||
|
/// For other types, it will call the == operator.
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// Note: If you are using this in a custom generic class, please make sure your class is
|
||||||
|
/// decorated with <see cref="GenerateSerializationForGenericParameterAttribute"/> so that codegen can
|
||||||
|
/// initialize the serialization mechanisms correctly. If your class is NOT
|
||||||
|
/// generic, it is better to check their equality yourself.
|
||||||
|
/// </summary>
|
||||||
|
public static EqualsDelegate AreEqual { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialize a value using the best-known serialization method for a generic value.
|
||||||
|
/// Will reliably serialize any value that is passed to it correctly with no boxing.
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// Note: If you are using this in a custom generic class, please make sure your class is
|
||||||
|
/// decorated with <see cref="GenerateSerializationForGenericParameterAttribute"/> so that codegen can
|
||||||
|
/// initialize the serialization mechanisms correctly. If your class is NOT
|
||||||
|
/// generic, it is better to use FastBufferWriter directly.
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// If the codegen is unable to determine a serializer for a type,
|
||||||
|
/// <see cref="UserNetworkVariableSerialization{T}"/>.<see cref="UserNetworkVariableSerialization{T}.WriteValue"/> is called, which, by default,
|
||||||
|
/// will throw an exception, unless you have assigned a user serialization callback to it at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public static void Write(FastBufferWriter writer, ref T value)
|
||||||
|
{
|
||||||
|
Serializer.Write(writer, ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialize a value using the best-known serialization method for a generic value.
|
||||||
|
/// Will reliably deserialize any value that is passed to it correctly with no boxing.
|
||||||
|
/// For types whose deserialization can be determined by codegen (which is most types),
|
||||||
|
/// GC will only be incurred if the type is a managed type and the ref value passed in is `null`,
|
||||||
|
/// in which case a new value is created; otherwise, it will be deserialized in-place.
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// Note: If you are using this in a custom generic class, please make sure your class is
|
||||||
|
/// decorated with <see cref="GenerateSerializationForGenericParameterAttribute"/> so that codegen can
|
||||||
|
/// initialize the serialization mechanisms correctly. If your class is NOT
|
||||||
|
/// generic, it is better to use FastBufferReader directly.
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// If the codegen is unable to determine a serializer for a type,
|
||||||
|
/// <see cref="UserNetworkVariableSerialization{T}"/>.<see cref="UserNetworkVariableSerialization{T}.ReadValue"/> is called, which, by default,
|
||||||
|
/// will throw an exception, unless you have assigned a user deserialization callback to it at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public static void Read(FastBufferReader reader, ref T value)
|
||||||
|
{
|
||||||
|
Serializer.Read(reader, ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Duplicates a value using the most efficient means of creating a complete copy.
|
||||||
|
/// For most types this is a simple assignment or memcpy.
|
||||||
|
/// For managed types, this is will serialize and then deserialize the value to ensure
|
||||||
|
/// a correct copy.
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// Note: If you are using this in a custom generic class, please make sure your class is
|
||||||
|
/// decorated with <see cref="GenerateSerializationForGenericParameterAttribute"/> so that codegen can
|
||||||
|
/// initialize the serialization mechanisms correctly. If your class is NOT
|
||||||
|
/// generic, it is better to duplicate it directly.
|
||||||
|
/// <br/>
|
||||||
|
/// <br/>
|
||||||
|
/// If the codegen is unable to determine a serializer for a type,
|
||||||
|
/// <see cref="UserNetworkVariableSerialization{T}"/>.<see cref="UserNetworkVariableSerialization{T}.DuplicateValue"/> is called, which, by default,
|
||||||
|
/// will throw an exception, unless you have assigned a user duplication callback to it at runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="duplicatedValue"></param>
|
||||||
|
public static void Duplicate(in T value, ref T duplicatedValue)
|
||||||
|
{
|
||||||
|
Serializer.Duplicate(value, ref duplicatedValue);
|
||||||
|
}
|
||||||
|
|
||||||
// Compares two values of the same unmanaged type by underlying memory
|
// Compares two values of the same unmanaged type by underlying memory
|
||||||
// Ignoring any overridden value checks
|
// Ignoring any overridden value checks
|
||||||
@@ -405,6 +949,58 @@ namespace Unity.Netcode
|
|||||||
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(TValueType)) == 0;
|
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(TValueType)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
// Compares two values of the same unmanaged type by underlying memory
|
||||||
|
// Ignoring any overridden value checks
|
||||||
|
// Size is fixed
|
||||||
|
internal static unsafe bool ValueEqualsList<TValueType>(ref NativeList<TValueType> a, ref NativeList<TValueType> b) where TValueType : unmanaged
|
||||||
|
{
|
||||||
|
if (a.IsCreated != b.IsCreated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a.IsCreated)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.Length != b.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var aptr = (TValueType*)a.GetUnsafePtr();
|
||||||
|
var bptr = (TValueType*)b.GetUnsafePtr();
|
||||||
|
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(TValueType) * a.Length) == 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Compares two values of the same unmanaged type by underlying memory
|
||||||
|
// Ignoring any overridden value checks
|
||||||
|
// Size is fixed
|
||||||
|
internal static unsafe bool ValueEqualsArray<TValueType>(ref NativeArray<TValueType> a, ref NativeArray<TValueType> b) where TValueType : unmanaged
|
||||||
|
{
|
||||||
|
if (a.IsCreated != b.IsCreated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a.IsCreated)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.Length != b.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var aptr = (TValueType*)a.GetUnsafePtr();
|
||||||
|
var bptr = (TValueType*)b.GetUnsafePtr();
|
||||||
|
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(TValueType) * a.Length) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool EqualityEqualsObject<TValueType>(ref TValueType a, ref TValueType b) where TValueType : class, IEquatable<TValueType>
|
internal static bool EqualityEqualsObject<TValueType>(ref TValueType a, ref TValueType b) where TValueType : class, IEquatable<TValueType>
|
||||||
{
|
{
|
||||||
if (a == null)
|
if (a == null)
|
||||||
@@ -425,19 +1021,93 @@ namespace Unity.Netcode
|
|||||||
return a.Equals(b);
|
return a.Equals(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
// Compares two values of the same unmanaged type by underlying memory
|
||||||
|
// Ignoring any overridden value checks
|
||||||
|
// Size is fixed
|
||||||
|
internal static unsafe bool EqualityEqualsList<TValueType>(ref NativeList<TValueType> a, ref NativeList<TValueType> b) where TValueType : unmanaged, IEquatable<TValueType>
|
||||||
|
{
|
||||||
|
if (a.IsCreated != b.IsCreated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a.IsCreated)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.Length != b.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var aptr = (TValueType*)a.GetUnsafePtr();
|
||||||
|
var bptr = (TValueType*)b.GetUnsafePtr();
|
||||||
|
for (var i = 0; i < a.Length; ++i)
|
||||||
|
{
|
||||||
|
if (!EqualityEquals(ref aptr[i], ref bptr[i]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Compares two values of the same unmanaged type by underlying memory
|
||||||
|
// Ignoring any overridden value checks
|
||||||
|
// Size is fixed
|
||||||
|
internal static unsafe bool EqualityEqualsArray<TValueType>(ref NativeArray<TValueType> a, ref NativeArray<TValueType> b) where TValueType : unmanaged, IEquatable<TValueType>
|
||||||
|
{
|
||||||
|
if (a.IsCreated != b.IsCreated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!a.IsCreated)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.Length != b.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var aptr = (TValueType*)a.GetUnsafePtr();
|
||||||
|
var bptr = (TValueType*)b.GetUnsafePtr();
|
||||||
|
for (var i = 0; i < a.Length; ++i)
|
||||||
|
{
|
||||||
|
if (!EqualityEquals(ref aptr[i], ref bptr[i]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool ClassEquals<TValueType>(ref TValueType a, ref TValueType b) where TValueType : class
|
internal static bool ClassEquals<TValueType>(ref TValueType a, ref TValueType b) where TValueType : class
|
||||||
{
|
{
|
||||||
return a == b;
|
return a == b;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Write(FastBufferWriter writer, ref T value)
|
|
||||||
{
|
|
||||||
Serializer.Write(writer, ref value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Read(FastBufferReader reader, ref T value)
|
// RuntimeAccessModifiersILPP will make this `public`
|
||||||
|
// This is just pass-through to NetworkVariableSerialization<T> but is here becaues I could not get ILPP
|
||||||
|
// to generate code that would successfully call Type<T>.Method(T), but it has no problem calling Type.Method<T>(T)
|
||||||
|
internal class RpcFallbackSerialization
|
||||||
{
|
{
|
||||||
Serializer.Read(reader, ref value);
|
public static void Write<T>(FastBufferWriter writer, ref T value)
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<T>.Write(writer, ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Read<T>(FastBufferReader reader, ref T value)
|
||||||
|
{
|
||||||
|
NetworkVariableSerialization<T>.Read(reader, ref value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
private Dictionary<Type, ProfilerMarker> m_HandlerProfilerMarkers = new Dictionary<Type, ProfilerMarker>();
|
private Dictionary<Type, ProfilerMarker> m_HandlerProfilerMarkers = new Dictionary<Type, ProfilerMarker>();
|
||||||
private Dictionary<Type, ProfilerMarker> m_SenderProfilerMarkers = new Dictionary<Type, ProfilerMarker>();
|
private Dictionary<Type, ProfilerMarker> m_SenderProfilerMarkers = new Dictionary<Type, ProfilerMarker>();
|
||||||
private readonly ProfilerMarker m_SendBatch = new ProfilerMarker($"{nameof(MessagingSystem)}.SendBatch");
|
private readonly ProfilerMarker m_SendBatch = new ProfilerMarker($"{nameof(NetworkMessageManager)}.SendBatch");
|
||||||
private readonly ProfilerMarker m_ReceiveBatch = new ProfilerMarker($"{nameof(MessagingSystem)}.ReceiveBatchBatch");
|
private readonly ProfilerMarker m_ReceiveBatch = new ProfilerMarker($"{nameof(NetworkMessageManager)}.ReceiveBatchBatch");
|
||||||
|
|
||||||
private ProfilerMarker GetHandlerProfilerMarker(Type type)
|
private ProfilerMarker GetHandlerProfilerMarker(Type type)
|
||||||
{
|
{
|
||||||
@@ -19,7 +19,7 @@ namespace Unity.Netcode
|
|||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
marker = new ProfilerMarker($"{nameof(MessagingSystem)}.DeserializeAndHandle.{type.Name}");
|
marker = new ProfilerMarker($"{nameof(NetworkMessageManager)}.DeserializeAndHandle.{type.Name}");
|
||||||
m_HandlerProfilerMarkers[type] = marker;
|
m_HandlerProfilerMarkers[type] = marker;
|
||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ namespace Unity.Netcode
|
|||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
marker = new ProfilerMarker($"{nameof(MessagingSystem)}.SerializeAndEnqueue.{type.Name}");
|
marker = new ProfilerMarker($"{nameof(NetworkMessageManager)}.SerializeAndEnqueue.{type.Name}");
|
||||||
m_SenderProfilerMarkers[type] = marker;
|
m_SenderProfilerMarkers[type] = marker;
|
||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else // Warn users if they are changing this after there are clients already connected and synchronized
|
else // Warn users if they are changing this after there are clients already connected and synchronized
|
||||||
if (networkManager.ConnectedClientsIds.Count > (networkManager.IsServer ? 0 : 1) && sceneManager.ClientSynchronizationMode != mode)
|
if (networkManager.ConnectedClientsIds.Count > (networkManager.IsHost ? 1 : 0) && sceneManager.ClientSynchronizationMode != mode)
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -524,7 +524,7 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal Dictionary<uint, SceneEventData> SceneEventDataStore;
|
internal Dictionary<uint, SceneEventData> SceneEventDataStore;
|
||||||
|
|
||||||
private NetworkManager m_NetworkManager { get; }
|
internal readonly NetworkManager NetworkManager;
|
||||||
|
|
||||||
// Keep track of this scene until the NetworkSceneManager is destroyed.
|
// Keep track of this scene until the NetworkSceneManager is destroyed.
|
||||||
internal Scene DontDestroyOnLoadScene;
|
internal Scene DontDestroyOnLoadScene;
|
||||||
@@ -575,7 +575,7 @@ namespace Unity.Netcode
|
|||||||
/// <returns>SceneEventData instance</returns>
|
/// <returns>SceneEventData instance</returns>
|
||||||
internal SceneEventData BeginSceneEvent()
|
internal SceneEventData BeginSceneEvent()
|
||||||
{
|
{
|
||||||
var sceneEventData = new SceneEventData(m_NetworkManager);
|
var sceneEventData = new SceneEventData(NetworkManager);
|
||||||
SceneEventDataStore.Add(sceneEventData.SceneEventId, sceneEventData);
|
SceneEventDataStore.Add(sceneEventData.SceneEventId, sceneEventData);
|
||||||
return sceneEventData;
|
return sceneEventData;
|
||||||
}
|
}
|
||||||
@@ -720,18 +720,18 @@ namespace Unity.Netcode
|
|||||||
/// <param name="mode"><see cref="LoadSceneMode"/> for initial client synchronization</param>
|
/// <param name="mode"><see cref="LoadSceneMode"/> for initial client synchronization</param>
|
||||||
public void SetClientSynchronizationMode(LoadSceneMode mode)
|
public void SetClientSynchronizationMode(LoadSceneMode mode)
|
||||||
{
|
{
|
||||||
var networkManager = m_NetworkManager;
|
var networkManager = NetworkManager;
|
||||||
SceneManagerHandler.SetClientSynchronizationMode(ref networkManager, mode);
|
SceneManagerHandler.SetClientSynchronizationMode(ref networkManager, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="networkManager">one <see cref="NetworkManager"/> instance per <see cref="NetworkSceneManager"/> instance</param>
|
/// <param name="networkManager">one <see cref="Netcode.NetworkManager"/> instance per <see cref="NetworkSceneManager"/> instance</param>
|
||||||
/// <param name="sceneEventDataPoolSize">maximum <see cref="SceneEventData"/> pool size</param>
|
/// <param name="sceneEventDataPoolSize">maximum <see cref="SceneEventData"/> pool size</param>
|
||||||
internal NetworkSceneManager(NetworkManager networkManager)
|
internal NetworkSceneManager(NetworkManager networkManager)
|
||||||
{
|
{
|
||||||
m_NetworkManager = networkManager;
|
NetworkManager = networkManager;
|
||||||
SceneEventDataStore = new Dictionary<uint, SceneEventData>();
|
SceneEventDataStore = new Dictionary<uint, SceneEventData>();
|
||||||
|
|
||||||
// Generates the scene name to hash value
|
// Generates the scene name to hash value
|
||||||
@@ -740,6 +740,14 @@ namespace Unity.Netcode
|
|||||||
// Since NetworkManager is now always migrated to the DDOL we will use this to get the DDOL scene
|
// Since NetworkManager is now always migrated to the DDOL we will use this to get the DDOL scene
|
||||||
DontDestroyOnLoadScene = networkManager.gameObject.scene;
|
DontDestroyOnLoadScene = networkManager.gameObject.scene;
|
||||||
|
|
||||||
|
// Since the server tracks loaded scenes, we need to add the currently active scene
|
||||||
|
// to the list of scenes that can be unloaded.
|
||||||
|
if (networkManager.IsServer)
|
||||||
|
{
|
||||||
|
var activeScene = SceneManager.GetActiveScene();
|
||||||
|
ScenesLoaded.Add(activeScene.handle, activeScene);
|
||||||
|
}
|
||||||
|
|
||||||
// Add to the server to client scene handle table
|
// Add to the server to client scene handle table
|
||||||
UpdateServerClientSceneHandle(DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene);
|
UpdateServerClientSceneHandle(DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene);
|
||||||
}
|
}
|
||||||
@@ -750,7 +758,7 @@ namespace Unity.Netcode
|
|||||||
private void SceneManager_ActiveSceneChanged(Scene current, Scene next)
|
private void SceneManager_ActiveSceneChanged(Scene current, Scene next)
|
||||||
{
|
{
|
||||||
// If no clients are connected, then don't worry about notifications
|
// If no clients are connected, then don't worry about notifications
|
||||||
if (!(m_NetworkManager.ConnectedClientsIds.Count > (m_NetworkManager.IsHost ? 1 : 0)))
|
if (!(NetworkManager.ConnectedClientsIds.Count > (NetworkManager.IsHost ? 1 : 0)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -771,7 +779,7 @@ namespace Unity.Netcode
|
|||||||
var sceneEvent = BeginSceneEvent();
|
var sceneEvent = BeginSceneEvent();
|
||||||
sceneEvent.SceneEventType = SceneEventType.ActiveSceneChanged;
|
sceneEvent.SceneEventType = SceneEventType.ActiveSceneChanged;
|
||||||
sceneEvent.ActiveSceneHash = BuildIndexToHash[next.buildIndex];
|
sceneEvent.ActiveSceneHash = BuildIndexToHash[next.buildIndex];
|
||||||
SendSceneEventData(sceneEvent.SceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
|
SendSceneEventData(sceneEvent.SceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
|
||||||
EndSceneEvent(sceneEvent.SceneEventId);
|
EndSceneEvent(sceneEvent.SceneEventId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -786,9 +794,18 @@ namespace Unity.Netcode
|
|||||||
/// <returns>true (Valid) or false (Invalid)</returns>
|
/// <returns>true (Valid) or false (Invalid)</returns>
|
||||||
internal bool ValidateSceneBeforeLoading(uint sceneHash, LoadSceneMode loadSceneMode)
|
internal bool ValidateSceneBeforeLoading(uint sceneHash, LoadSceneMode loadSceneMode)
|
||||||
{
|
{
|
||||||
var validated = true;
|
|
||||||
var sceneName = SceneNameFromHash(sceneHash);
|
var sceneName = SceneNameFromHash(sceneHash);
|
||||||
var sceneIndex = SceneUtility.GetBuildIndexByScenePath(sceneName);
|
var sceneIndex = SceneUtility.GetBuildIndexByScenePath(sceneName);
|
||||||
|
return ValidateSceneBeforeLoading(sceneIndex, sceneName, loadSceneMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overloaded version that is invoked by <see cref="ValidateSceneBeforeLoading"/> and <see cref="SynchronizeNetworkObjects"/>.
|
||||||
|
/// This specifically is to allow runtime generated scenes to be excluded by the server during synchronization.
|
||||||
|
/// </summary>
|
||||||
|
internal bool ValidateSceneBeforeLoading(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
|
||||||
|
{
|
||||||
|
var validated = true;
|
||||||
if (VerifySceneBeforeLoading != null)
|
if (VerifySceneBeforeLoading != null)
|
||||||
{
|
{
|
||||||
validated = VerifySceneBeforeLoading.Invoke(sceneIndex, sceneName, loadSceneMode);
|
validated = VerifySceneBeforeLoading.Invoke(sceneIndex, sceneName, loadSceneMode);
|
||||||
@@ -796,9 +813,9 @@ namespace Unity.Netcode
|
|||||||
if (!validated && !m_DisableValidationWarningMessages)
|
if (!validated && !m_DisableValidationWarningMessages)
|
||||||
{
|
{
|
||||||
var serverHostorClient = "Client";
|
var serverHostorClient = "Client";
|
||||||
if (m_NetworkManager.IsServer)
|
if (NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
serverHostorClient = m_NetworkManager.IsHost ? "Host" : "Server";
|
serverHostorClient = NetworkManager.IsHost ? "Host" : "Server";
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.LogWarning($"Scene {sceneName} of Scenes in Build Index {sceneIndex} being loaded in {loadSceneMode} mode failed validation on the {serverHostorClient}!");
|
Debug.LogWarning($"Scene {sceneName} of Scenes in Build Index {sceneIndex} being loaded in {loadSceneMode} mode failed validation on the {serverHostorClient}!");
|
||||||
@@ -837,7 +854,7 @@ namespace Unity.Netcode
|
|||||||
if (!ScenesLoaded.ContainsKey(sceneLoaded.handle))
|
if (!ScenesLoaded.ContainsKey(sceneLoaded.handle))
|
||||||
{
|
{
|
||||||
ScenesLoaded.Add(sceneLoaded.handle, sceneLoaded);
|
ScenesLoaded.Add(sceneLoaded.handle, sceneLoaded);
|
||||||
SceneManagerHandler.StartTrackingScene(sceneLoaded, true, m_NetworkManager);
|
SceneManagerHandler.StartTrackingScene(sceneLoaded, true, NetworkManager);
|
||||||
return sceneLoaded;
|
return sceneLoaded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -886,7 +903,7 @@ namespace Unity.Netcode
|
|||||||
// Most common scenario for DontDestroyOnLoad is when NetworkManager is set to not be destroyed
|
// Most common scenario for DontDestroyOnLoad is when NetworkManager is set to not be destroyed
|
||||||
if (serverSceneHandle == DontDestroyOnLoadScene.handle)
|
if (serverSceneHandle == DontDestroyOnLoadScene.handle)
|
||||||
{
|
{
|
||||||
SceneBeingSynchronized = m_NetworkManager.gameObject.scene;
|
SceneBeingSynchronized = NetworkManager.gameObject.scene;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -940,9 +957,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = SceneEventDataStore[sceneEventId]
|
EventData = SceneEventDataStore[sceneEventId]
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, targetClientIds);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, k_DeliveryType, targetClientIds);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(targetClientIds, (uint)SceneEventDataStore[sceneEventId].SceneEventType, SceneNameFromHash(SceneEventDataStore[sceneEventId].SceneHash), size);
|
NetworkManager.NetworkMetrics.TrackSceneEventSent(targetClientIds, (uint)SceneEventDataStore[sceneEventId].SceneEventType, SceneNameFromHash(SceneEventDataStore[sceneEventId].SceneHash), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -952,16 +969,16 @@ namespace Unity.Netcode
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private SceneEventProgress ValidateSceneEventUnloading(Scene scene)
|
private SceneEventProgress ValidateSceneEventUnloading(Scene scene)
|
||||||
{
|
{
|
||||||
if (!m_NetworkManager.IsServer)
|
if (!NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
throw new NotServerException("Only server can start a scene event!");
|
throw new NotServerException("Only server can start a scene event!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_NetworkManager.NetworkConfig.EnableSceneManagement)
|
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
|
||||||
{
|
{
|
||||||
//Log message about enabling SceneManagement
|
//Log message about enabling SceneManagement
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
$"{nameof(NetworkConfig.EnableSceneManagement)} flag is not enabled in the {nameof(NetworkManager)}'s {nameof(NetworkConfig)}. " +
|
$"{nameof(NetworkConfig.EnableSceneManagement)} flag is not enabled in the {nameof(Netcode.NetworkManager)}'s {nameof(NetworkConfig)}. " +
|
||||||
$"Please set {nameof(NetworkConfig.EnableSceneManagement)} flag to true before calling {nameof(LoadScene)} or {nameof(UnloadScene)}.");
|
$"Please set {nameof(NetworkConfig.EnableSceneManagement)} flag to true before calling {nameof(LoadScene)} or {nameof(UnloadScene)}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -981,15 +998,15 @@ namespace Unity.Netcode
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private SceneEventProgress ValidateSceneEventLoading(string sceneName)
|
private SceneEventProgress ValidateSceneEventLoading(string sceneName)
|
||||||
{
|
{
|
||||||
if (!m_NetworkManager.IsServer)
|
if (!NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
throw new NotServerException("Only server can start a scene event!");
|
throw new NotServerException("Only server can start a scene event!");
|
||||||
}
|
}
|
||||||
if (!m_NetworkManager.NetworkConfig.EnableSceneManagement)
|
if (!NetworkManager.NetworkConfig.EnableSceneManagement)
|
||||||
{
|
{
|
||||||
//Log message about enabling SceneManagement
|
//Log message about enabling SceneManagement
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
$"{nameof(NetworkConfig.EnableSceneManagement)} flag is not enabled in the {nameof(NetworkManager)}'s {nameof(NetworkConfig)}. " +
|
$"{nameof(NetworkConfig.EnableSceneManagement)} flag is not enabled in the {nameof(Netcode.NetworkManager)}'s {nameof(NetworkConfig)}. " +
|
||||||
$"Please set {nameof(NetworkConfig.EnableSceneManagement)} flag to true before calling {nameof(LoadScene)} or {nameof(UnloadScene)}.");
|
$"Please set {nameof(NetworkConfig.EnableSceneManagement)} flag to true before calling {nameof(LoadScene)} or {nameof(UnloadScene)}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1017,7 +1034,7 @@ namespace Unity.Netcode
|
|||||||
return new SceneEventProgress(null, SceneEventProgressStatus.InvalidSceneName);
|
return new SceneEventProgress(null, SceneEventProgressStatus.InvalidSceneName);
|
||||||
}
|
}
|
||||||
|
|
||||||
var sceneEventProgress = new SceneEventProgress(m_NetworkManager)
|
var sceneEventProgress = new SceneEventProgress(NetworkManager)
|
||||||
{
|
{
|
||||||
SceneHash = SceneHashFromNameOrPath(sceneName)
|
SceneHash = SceneHashFromNameOrPath(sceneName)
|
||||||
};
|
};
|
||||||
@@ -1052,10 +1069,10 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = sceneEventData
|
EventData = sceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, m_NetworkManager.ConnectedClientsIds);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, k_DeliveryType, NetworkManager.ConnectedClientsIds);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(
|
NetworkManager.NetworkMetrics.TrackSceneEventSent(
|
||||||
m_NetworkManager.ConnectedClientsIds,
|
NetworkManager.ConnectedClientsIds,
|
||||||
(uint)sceneEventProgress.SceneEventType,
|
(uint)sceneEventProgress.SceneEventType,
|
||||||
SceneNameFromHash(sceneEventProgress.SceneHash),
|
SceneNameFromHash(sceneEventProgress.SceneHash),
|
||||||
size);
|
size);
|
||||||
@@ -1116,7 +1133,7 @@ namespace Unity.Netcode
|
|||||||
// Any NetworkObjects marked to not be destroyed with a scene and reside within the scene about to be unloaded
|
// Any NetworkObjects marked to not be destroyed with a scene and reside within the scene about to be unloaded
|
||||||
// should be migrated temporarily into the DDOL, once the scene is unloaded they will be migrated into the
|
// should be migrated temporarily into the DDOL, once the scene is unloaded they will be migrated into the
|
||||||
// currently active scene.
|
// currently active scene.
|
||||||
var networkManager = m_NetworkManager;
|
var networkManager = NetworkManager;
|
||||||
SceneManagerHandler.MoveObjectsFromSceneToDontDestroyOnLoad(ref networkManager, scene);
|
SceneManagerHandler.MoveObjectsFromSceneToDontDestroyOnLoad(ref networkManager, scene);
|
||||||
|
|
||||||
var sceneEventData = BeginSceneEvent();
|
var sceneEventData = BeginSceneEvent();
|
||||||
@@ -1179,18 +1196,18 @@ namespace Unity.Netcode
|
|||||||
// Any NetworkObjects marked to not be destroyed with a scene and reside within the scene about to be unloaded
|
// Any NetworkObjects marked to not be destroyed with a scene and reside within the scene about to be unloaded
|
||||||
// should be migrated temporarily into the DDOL, once the scene is unloaded they will be migrated into the
|
// should be migrated temporarily into the DDOL, once the scene is unloaded they will be migrated into the
|
||||||
// currently active scene.
|
// currently active scene.
|
||||||
var networkManager = m_NetworkManager;
|
var networkManager = NetworkManager;
|
||||||
SceneManagerHandler.MoveObjectsFromSceneToDontDestroyOnLoad(ref networkManager, scene);
|
SceneManagerHandler.MoveObjectsFromSceneToDontDestroyOnLoad(ref networkManager, scene);
|
||||||
|
|
||||||
m_IsSceneEventActive = true;
|
m_IsSceneEventActive = true;
|
||||||
var sceneEventProgress = new SceneEventProgress(m_NetworkManager)
|
var sceneEventProgress = new SceneEventProgress(NetworkManager)
|
||||||
{
|
{
|
||||||
SceneEventId = sceneEventData.SceneEventId,
|
SceneEventId = sceneEventData.SceneEventId,
|
||||||
OnSceneEventCompleted = OnSceneUnloaded
|
OnSceneEventCompleted = OnSceneUnloaded
|
||||||
};
|
};
|
||||||
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(scene, sceneEventProgress);
|
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(scene, sceneEventProgress);
|
||||||
|
|
||||||
SceneManagerHandler.StopTrackingScene(sceneHandle, sceneName, m_NetworkManager);
|
SceneManagerHandler.StopTrackingScene(sceneHandle, sceneName, NetworkManager);
|
||||||
|
|
||||||
// Remove our server to scene handle lookup
|
// Remove our server to scene handle lookup
|
||||||
if (!RemoveServerClientSceneHandle(sceneEventData.SceneHandle, sceneHandle))
|
if (!RemoveServerClientSceneHandle(sceneEventData.SceneHandle, sceneHandle))
|
||||||
@@ -1206,10 +1223,10 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = LoadSceneMode.Additive, // The only scenes unloaded are scenes that were additively loaded
|
LoadSceneMode = LoadSceneMode.Additive, // The only scenes unloaded are scenes that were additively loaded
|
||||||
SceneName = sceneName,
|
SceneName = sceneName,
|
||||||
ClientId = m_NetworkManager.LocalClientId // Server sent this message to the client, but client is executing it
|
ClientId = NetworkManager.LocalClientId // Server sent this message to the client, but client is executing it
|
||||||
});
|
});
|
||||||
|
|
||||||
OnUnload?.Invoke(m_NetworkManager.LocalClientId, sceneName, sceneUnload);
|
OnUnload?.Invoke(NetworkManager.LocalClientId, sceneName, sceneUnload);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1219,7 +1236,7 @@ namespace Unity.Netcode
|
|||||||
private void OnSceneUnloaded(uint sceneEventId)
|
private void OnSceneUnloaded(uint sceneEventId)
|
||||||
{
|
{
|
||||||
// If we are shutdown or about to shutdown, then ignore this event
|
// If we are shutdown or about to shutdown, then ignore this event
|
||||||
if (!m_NetworkManager.IsListening || m_NetworkManager.ShutdownInProgress)
|
if (!NetworkManager.IsListening || NetworkManager.ShutdownInProgress)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1229,15 +1246,15 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
var sceneEventData = SceneEventDataStore[sceneEventId];
|
var sceneEventData = SceneEventDataStore[sceneEventId];
|
||||||
// First thing we do, if we are a server, is to send the unload scene event.
|
// First thing we do, if we are a server, is to send the unload scene event.
|
||||||
if (m_NetworkManager.IsServer)
|
if (NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
// Server sends the unload scene notification after unloading because it will despawn all scene relative in-scene NetworkObjects
|
// Server sends the unload scene notification after unloading because it will despawn all scene relative in-scene NetworkObjects
|
||||||
// If we send this event to all clients before the server is finished unloading they will get warning about an object being
|
// If we send this event to all clients before the server is finished unloading they will get warning about an object being
|
||||||
// despawned that no longer exists
|
// despawned that no longer exists
|
||||||
SendSceneEventData(sceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
|
SendSceneEventData(sceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
|
||||||
|
|
||||||
//Only if we are a host do we want register having loaded for the associated SceneEventProgress
|
//Only if we are a host do we want register having loaded for the associated SceneEventProgress
|
||||||
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && NetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].ClientFinishedSceneEvent(NetworkManager.ServerClientId);
|
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].ClientFinishedSceneEvent(NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
@@ -1252,13 +1269,13 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||||
ClientId = m_NetworkManager.IsServer ? NetworkManager.ServerClientId : m_NetworkManager.LocalClientId
|
ClientId = NetworkManager.IsServer ? NetworkManager.ServerClientId : NetworkManager.LocalClientId
|
||||||
});
|
});
|
||||||
|
|
||||||
OnUnloadComplete?.Invoke(m_NetworkManager.LocalClientId, SceneNameFromHash(sceneEventData.SceneHash));
|
OnUnloadComplete?.Invoke(NetworkManager.LocalClientId, SceneNameFromHash(sceneEventData.SceneHash));
|
||||||
|
|
||||||
// Clients send a notification back to the server they have completed the unload scene event
|
// Clients send a notification back to the server they have completed the unload scene event
|
||||||
if (!m_NetworkManager.IsServer)
|
if (!NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||||
}
|
}
|
||||||
@@ -1288,7 +1305,7 @@ namespace Unity.Netcode
|
|||||||
// Validate the scene as well as ignore the DDOL (which will have a negative buildIndex)
|
// Validate the scene as well as ignore the DDOL (which will have a negative buildIndex)
|
||||||
if (currentActiveScene.name != keyHandleEntry.Value.name && keyHandleEntry.Value.buildIndex >= 0)
|
if (currentActiveScene.name != keyHandleEntry.Value.name && keyHandleEntry.Value.buildIndex >= 0)
|
||||||
{
|
{
|
||||||
var sceneEventProgress = new SceneEventProgress(m_NetworkManager)
|
var sceneEventProgress = new SceneEventProgress(NetworkManager)
|
||||||
{
|
{
|
||||||
SceneEventId = sceneEventId,
|
SceneEventId = sceneEventId,
|
||||||
OnSceneEventCompleted = EmptySceneUnloadedOperation
|
OnSceneEventCompleted = EmptySceneUnloadedOperation
|
||||||
@@ -1299,7 +1316,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
// clear out our scenes loaded list
|
// clear out our scenes loaded list
|
||||||
ScenesLoaded.Clear();
|
ScenesLoaded.Clear();
|
||||||
SceneManagerHandler.ClearSceneTracking(m_NetworkManager);
|
SceneManagerHandler.ClearSceneTracking(NetworkManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1348,7 +1365,7 @@ namespace Unity.Netcode
|
|||||||
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
||||||
|
|
||||||
// Destroy current scene objects before switching.
|
// Destroy current scene objects before switching.
|
||||||
m_NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects();
|
NetworkManager.SpawnManager.ServerDestroySpawnedSceneObjects();
|
||||||
|
|
||||||
// Preserve the objects that should not be destroyed during the scene event
|
// Preserve the objects that should not be destroyed during the scene event
|
||||||
MoveObjectsToDontDestroyOnLoad();
|
MoveObjectsToDontDestroyOnLoad();
|
||||||
@@ -1390,7 +1407,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal static void RegisterScene(NetworkSceneManager networkSceneManager, Scene scene, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation = null)
|
internal static void RegisterScene(NetworkSceneManager networkSceneManager, Scene scene, LoadSceneMode loadSceneMode, AsyncOperation asyncOperation = null)
|
||||||
{
|
{
|
||||||
var networkManager = networkSceneManager.m_NetworkManager;
|
var networkManager = networkSceneManager.NetworkManager;
|
||||||
if (!s_Instances.ContainsKey(networkManager))
|
if (!s_Instances.ContainsKey(networkManager))
|
||||||
{
|
{
|
||||||
s_Instances.Add(networkManager, new List<SceneUnloadEventHandler>());
|
s_Instances.Add(networkManager, new List<SceneUnloadEventHandler>());
|
||||||
@@ -1401,11 +1418,11 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private static void SceneUnloadComplete(SceneUnloadEventHandler sceneUnloadEventHandler)
|
private static void SceneUnloadComplete(SceneUnloadEventHandler sceneUnloadEventHandler)
|
||||||
{
|
{
|
||||||
if (sceneUnloadEventHandler == null || sceneUnloadEventHandler.m_NetworkSceneManager == null || sceneUnloadEventHandler.m_NetworkSceneManager.m_NetworkManager == null)
|
if (sceneUnloadEventHandler == null || sceneUnloadEventHandler.m_NetworkSceneManager == null || sceneUnloadEventHandler.m_NetworkSceneManager.NetworkManager == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var networkManager = sceneUnloadEventHandler.m_NetworkSceneManager.m_NetworkManager;
|
var networkManager = sceneUnloadEventHandler.m_NetworkSceneManager.NetworkManager;
|
||||||
if (s_Instances.ContainsKey(networkManager))
|
if (s_Instances.ContainsKey(networkManager))
|
||||||
{
|
{
|
||||||
s_Instances[networkManager].Remove(sceneUnloadEventHandler);
|
s_Instances[networkManager].Remove(sceneUnloadEventHandler);
|
||||||
@@ -1449,7 +1466,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (m_Scene.handle == scene.handle && !m_ShuttingDown)
|
if (m_Scene.handle == scene.handle && !m_ShuttingDown)
|
||||||
{
|
{
|
||||||
if (m_NetworkSceneManager != null && m_NetworkSceneManager.m_NetworkManager != null)
|
if (m_NetworkSceneManager != null && m_NetworkSceneManager.NetworkManager != null)
|
||||||
{
|
{
|
||||||
m_NetworkSceneManager.OnSceneEvent?.Invoke(new SceneEvent()
|
m_NetworkSceneManager.OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
{
|
{
|
||||||
@@ -1484,7 +1501,7 @@ namespace Unity.Netcode
|
|||||||
ClientId = clientId
|
ClientId = clientId
|
||||||
});
|
});
|
||||||
|
|
||||||
m_NetworkSceneManager.OnUnload?.Invoke(networkSceneManager.m_NetworkManager.LocalClientId, m_Scene.name, null);
|
m_NetworkSceneManager.OnUnload?.Invoke(networkSceneManager.NetworkManager.LocalClientId, m_Scene.name, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1527,7 +1544,7 @@ namespace Unity.Netcode
|
|||||||
SceneUnloadEventHandler.RegisterScene(this, SceneManager.GetActiveScene(), LoadSceneMode.Single);
|
SceneUnloadEventHandler.RegisterScene(this, SceneManager.GetActiveScene(), LoadSceneMode.Single);
|
||||||
|
|
||||||
}
|
}
|
||||||
var sceneEventProgress = new SceneEventProgress(m_NetworkManager)
|
var sceneEventProgress = new SceneEventProgress(NetworkManager)
|
||||||
{
|
{
|
||||||
SceneEventId = sceneEventId,
|
SceneEventId = sceneEventId,
|
||||||
OnSceneEventCompleted = OnSceneLoaded
|
OnSceneEventCompleted = OnSceneLoaded
|
||||||
@@ -1540,10 +1557,10 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = sceneName,
|
SceneName = sceneName,
|
||||||
ClientId = m_NetworkManager.LocalClientId
|
ClientId = NetworkManager.LocalClientId
|
||||||
});
|
});
|
||||||
|
|
||||||
OnLoad?.Invoke(m_NetworkManager.LocalClientId, sceneName, sceneEventData.LoadSceneMode, sceneLoad);
|
OnLoad?.Invoke(NetworkManager.LocalClientId, sceneName, sceneEventData.LoadSceneMode, sceneLoad);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1553,7 +1570,7 @@ namespace Unity.Netcode
|
|||||||
private void OnSceneLoaded(uint sceneEventId)
|
private void OnSceneLoaded(uint sceneEventId)
|
||||||
{
|
{
|
||||||
// If we are shutdown or about to shutdown, then ignore this event
|
// If we are shutdown or about to shutdown, then ignore this event
|
||||||
if (!m_NetworkManager.IsListening || m_NetworkManager.ShutdownInProgress)
|
if (!NetworkManager.IsListening || NetworkManager.ShutdownInProgress)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1586,7 +1603,7 @@ namespace Unity.Netcode
|
|||||||
// not destroy temporary scene are moved into the active scene
|
// not destroy temporary scene are moved into the active scene
|
||||||
IsSpawnedObjectsPendingInDontDestroyOnLoad = false;
|
IsSpawnedObjectsPendingInDontDestroyOnLoad = false;
|
||||||
|
|
||||||
if (m_NetworkManager.IsServer)
|
if (NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
OnServerLoadedScene(sceneEventId, nextScene);
|
OnServerLoadedScene(sceneEventId, nextScene);
|
||||||
}
|
}
|
||||||
@@ -1618,8 +1635,8 @@ namespace Unity.Netcode
|
|||||||
if (!keyValuePairBySceneHandle.Value.IsPlayerObject)
|
if (!keyValuePairBySceneHandle.Value.IsPlayerObject)
|
||||||
{
|
{
|
||||||
// All in-scene placed NetworkObjects default to being owned by the server
|
// All in-scene placed NetworkObjects default to being owned by the server
|
||||||
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value,
|
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value,
|
||||||
m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, NetworkManager.ServerClientId, true);
|
NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, NetworkManager.ServerClientId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1631,9 +1648,9 @@ namespace Unity.Netcode
|
|||||||
sceneEventData.SceneHandle = scene.handle;
|
sceneEventData.SceneHandle = scene.handle;
|
||||||
|
|
||||||
// Send all clients the scene load event
|
// Send all clients the scene load event
|
||||||
for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++)
|
for (int j = 0; j < NetworkManager.ConnectedClientsList.Count; j++)
|
||||||
{
|
{
|
||||||
var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId;
|
var clientId = NetworkManager.ConnectedClientsList[j].ClientId;
|
||||||
if (clientId != NetworkManager.ServerClientId)
|
if (clientId != NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
sceneEventData.TargetClientId = clientId;
|
sceneEventData.TargetClientId = clientId;
|
||||||
@@ -1641,8 +1658,8 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = sceneEventData
|
EventData = sceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, clientId);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, k_DeliveryType, clientId);
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)sceneEventData.SceneEventType, scene.name, size);
|
NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)sceneEventData.SceneEventType, scene.name, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1660,7 +1677,7 @@ namespace Unity.Netcode
|
|||||||
OnLoadComplete?.Invoke(NetworkManager.ServerClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
OnLoadComplete?.Invoke(NetworkManager.ServerClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
||||||
|
|
||||||
//Second, only if we are a host do we want register having loaded for the associated SceneEventProgress
|
//Second, only if we are a host do we want register having loaded for the associated SceneEventProgress
|
||||||
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && NetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].ClientFinishedSceneEvent(NetworkManager.ServerClientId);
|
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].ClientFinishedSceneEvent(NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
@@ -1686,11 +1703,11 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = SceneEventType.LoadComplete,
|
SceneEventType = SceneEventType.LoadComplete,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||||
ClientId = m_NetworkManager.LocalClientId,
|
ClientId = NetworkManager.LocalClientId,
|
||||||
Scene = scene,
|
Scene = scene,
|
||||||
});
|
});
|
||||||
|
|
||||||
OnLoadComplete?.Invoke(m_NetworkManager.LocalClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
OnLoadComplete?.Invoke(NetworkManager.LocalClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
||||||
|
|
||||||
EndSceneEvent(sceneEventId);
|
EndSceneEvent(sceneEventId);
|
||||||
}
|
}
|
||||||
@@ -1713,7 +1730,7 @@ namespace Unity.Netcode
|
|||||||
internal void SynchronizeNetworkObjects(ulong clientId)
|
internal void SynchronizeNetworkObjects(ulong clientId)
|
||||||
{
|
{
|
||||||
// Update the clients
|
// Update the clients
|
||||||
m_NetworkManager.SpawnManager.UpdateObservedNetworkObjects(clientId);
|
NetworkManager.SpawnManager.UpdateObservedNetworkObjects(clientId);
|
||||||
|
|
||||||
var sceneEventData = BeginSceneEvent();
|
var sceneEventData = BeginSceneEvent();
|
||||||
sceneEventData.ClientSynchronizationMode = ClientSynchronizationMode;
|
sceneEventData.ClientSynchronizationMode = ClientSynchronizationMode;
|
||||||
@@ -1744,24 +1761,22 @@ namespace Unity.Netcode
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sceneHash = SceneHashFromNameOrPath(scene.path);
|
|
||||||
|
|
||||||
// This would depend upon whether we are additive or not
|
// This would depend upon whether we are additive or not
|
||||||
// If we are the base scene, then we set the root scene index;
|
// If we are the base scene, then we set the root scene index;
|
||||||
if (activeScene == scene)
|
if (activeScene == scene)
|
||||||
{
|
{
|
||||||
if (!ValidateSceneBeforeLoading(sceneHash, sceneEventData.LoadSceneMode))
|
if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, sceneEventData.LoadSceneMode))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sceneEventData.SceneHash = sceneHash;
|
sceneEventData.SceneHash = SceneHashFromNameOrPath(scene.path);
|
||||||
sceneEventData.SceneHandle = scene.handle;
|
sceneEventData.SceneHandle = scene.handle;
|
||||||
}
|
}
|
||||||
else if (!ValidateSceneBeforeLoading(sceneHash, LoadSceneMode.Additive))
|
else if (!ValidateSceneBeforeLoading(scene.buildIndex, scene.name, LoadSceneMode.Additive))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sceneEventData.AddSceneToSynchronize(sceneHash, scene.handle);
|
sceneEventData.AddSceneToSynchronize(SceneHashFromNameOrPath(scene.path), scene.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
sceneEventData.AddSpawnedNetworkObjects();
|
sceneEventData.AddSpawnedNetworkObjects();
|
||||||
@@ -1771,8 +1786,8 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = sceneEventData
|
EventData = sceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, clientId);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, k_DeliveryType, clientId);
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)sceneEventData.SceneEventType, "", size);
|
NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)sceneEventData.SceneEventType, "", size);
|
||||||
|
|
||||||
// Notify the local server that the client has been sent the synchronize event
|
// Notify the local server that the client has been sent the synchronize event
|
||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
@@ -1811,17 +1826,17 @@ namespace Unity.Netcode
|
|||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
{
|
{
|
||||||
SceneEventType = SceneEventType.Synchronize,
|
SceneEventType = SceneEventType.Synchronize,
|
||||||
ClientId = m_NetworkManager.LocalClientId,
|
ClientId = NetworkManager.LocalClientId,
|
||||||
});
|
});
|
||||||
|
|
||||||
OnSynchronize?.Invoke(m_NetworkManager.LocalClientId);
|
OnSynchronize?.Invoke(NetworkManager.LocalClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always check to see if the scene needs to be validated
|
// Always check to see if the scene needs to be validated
|
||||||
if (!ValidateSceneBeforeLoading(sceneHash, loadSceneMode))
|
if (!ValidateSceneBeforeLoading(sceneHash, loadSceneMode))
|
||||||
{
|
{
|
||||||
HandleClientSceneEvent(sceneEventId);
|
HandleClientSceneEvent(sceneEventId);
|
||||||
if (m_NetworkManager.LogLevel == LogLevel.Developer)
|
if (NetworkManager.LogLevel == LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogInfo($"Client declined to load the scene {sceneName}, continuing with synchronization.");
|
NetworkLog.LogInfo($"Client declined to load the scene {sceneName}, continuing with synchronization.");
|
||||||
}
|
}
|
||||||
@@ -1835,12 +1850,12 @@ namespace Unity.Netcode
|
|||||||
// it should pass through to post load processing (ClientLoadedSynchronization).
|
// it should pass through to post load processing (ClientLoadedSynchronization).
|
||||||
// For ClientSynchronizationMode LoadSceneMode.Additive, if the scene is already loaded or the active scene is the scene to be loaded (does not require it to
|
// For ClientSynchronizationMode LoadSceneMode.Additive, if the scene is already loaded or the active scene is the scene to be loaded (does not require it to
|
||||||
// be the initial primary scene) then go ahead and pass through to post load processing (ClientLoadedSynchronization).
|
// be the initial primary scene) then go ahead and pass through to post load processing (ClientLoadedSynchronization).
|
||||||
var shouldPassThrough = SceneManagerHandler.ClientShouldPassThrough(sceneName, sceneHash == sceneEventData.SceneHash, ClientSynchronizationMode, m_NetworkManager);
|
var shouldPassThrough = SceneManagerHandler.ClientShouldPassThrough(sceneName, sceneHash == sceneEventData.SceneHash, ClientSynchronizationMode, NetworkManager);
|
||||||
|
|
||||||
if (!shouldPassThrough)
|
if (!shouldPassThrough)
|
||||||
{
|
{
|
||||||
// If not, then load the scene
|
// If not, then load the scene
|
||||||
var sceneEventProgress = new SceneEventProgress(m_NetworkManager)
|
var sceneEventProgress = new SceneEventProgress(NetworkManager)
|
||||||
{
|
{
|
||||||
SceneEventId = sceneEventId,
|
SceneEventId = sceneEventId,
|
||||||
OnSceneEventCompleted = ClientLoadedSynchronization
|
OnSceneEventCompleted = ClientLoadedSynchronization
|
||||||
@@ -1854,10 +1869,10 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = SceneEventType.Load,
|
SceneEventType = SceneEventType.Load,
|
||||||
LoadSceneMode = loadSceneMode,
|
LoadSceneMode = loadSceneMode,
|
||||||
SceneName = sceneName,
|
SceneName = sceneName,
|
||||||
ClientId = m_NetworkManager.LocalClientId,
|
ClientId = NetworkManager.LocalClientId,
|
||||||
});
|
});
|
||||||
|
|
||||||
OnLoad?.Invoke(m_NetworkManager.LocalClientId, sceneName, loadSceneMode, sceneLoad);
|
OnLoad?.Invoke(NetworkManager.LocalClientId, sceneName, loadSceneMode, sceneLoad);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1875,7 +1890,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
var sceneEventData = SceneEventDataStore[sceneEventId];
|
var sceneEventData = SceneEventDataStore[sceneEventId];
|
||||||
var sceneName = SceneNameFromHash(sceneEventData.ClientSceneHash);
|
var sceneName = SceneNameFromHash(sceneEventData.ClientSceneHash);
|
||||||
var nextScene = SceneManagerHandler.GetSceneFromLoadedScenes(sceneName, m_NetworkManager);
|
var nextScene = SceneManagerHandler.GetSceneFromLoadedScenes(sceneName, NetworkManager);
|
||||||
if (!nextScene.IsValid())
|
if (!nextScene.IsValid())
|
||||||
{
|
{
|
||||||
nextScene = GetAndAddNewlyLoadedSceneByName(sceneName);
|
nextScene = GetAndAddNewlyLoadedSceneByName(sceneName);
|
||||||
@@ -1915,9 +1930,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = responseSceneEventData
|
EventData = responseSceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, NetworkManager.ServerClientId);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, k_DeliveryType, NetworkManager.ServerClientId);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(NetworkManager.ServerClientId, (uint)responseSceneEventData.SceneEventType, sceneName, size);
|
NetworkManager.NetworkMetrics.TrackSceneEventSent(NetworkManager.ServerClientId, (uint)responseSceneEventData.SceneEventType, sceneName, size);
|
||||||
|
|
||||||
EndSceneEvent(responseSceneEventData.SceneEventId);
|
EndSceneEvent(responseSceneEventData.SceneEventId);
|
||||||
|
|
||||||
@@ -1928,10 +1943,10 @@ namespace Unity.Netcode
|
|||||||
LoadSceneMode = loadSceneMode,
|
LoadSceneMode = loadSceneMode,
|
||||||
SceneName = sceneName,
|
SceneName = sceneName,
|
||||||
Scene = nextScene,
|
Scene = nextScene,
|
||||||
ClientId = m_NetworkManager.LocalClientId,
|
ClientId = NetworkManager.LocalClientId,
|
||||||
});
|
});
|
||||||
|
|
||||||
OnLoadComplete?.Invoke(m_NetworkManager.LocalClientId, sceneName, loadSceneMode);
|
OnLoadComplete?.Invoke(NetworkManager.LocalClientId, sceneName, loadSceneMode);
|
||||||
|
|
||||||
// Check to see if we still have scenes to load and synchronize with
|
// Check to see if we still have scenes to load and synchronize with
|
||||||
HandleClientSceneEvent(sceneEventId);
|
HandleClientSceneEvent(sceneEventId);
|
||||||
@@ -1944,7 +1959,7 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void SynchronizeNetworkObjectScene()
|
private void SynchronizeNetworkObjectScene()
|
||||||
{
|
{
|
||||||
foreach (var networkObject in m_NetworkManager.SpawnManager.SpawnedObjectsList)
|
foreach (var networkObject in NetworkManager.SpawnManager.SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
// This is only done for dynamically spawned NetworkObjects
|
// This is only done for dynamically spawned NetworkObjects
|
||||||
// Theoretically, a server could have NetworkObjects in a server-side only scene, if the client doesn't have that scene loaded
|
// Theoretically, a server could have NetworkObjects in a server-side only scene, if the client doesn't have that scene loaded
|
||||||
@@ -1969,9 +1984,9 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
SceneManager.MoveGameObjectToScene(networkObject.gameObject, scene);
|
SceneManager.MoveGameObjectToScene(networkObject.gameObject, scene);
|
||||||
}
|
}
|
||||||
else if (m_NetworkManager.LogLevel <= LogLevel.Normal)
|
else if (NetworkManager.LogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarningServer($"[Client-{m_NetworkManager.LocalClientId}][{networkObject.gameObject.name}] Server - " +
|
NetworkLog.LogWarningServer($"[Client-{NetworkManager.LocalClientId}][{networkObject.gameObject.name}] Server - " +
|
||||||
$"client scene mismatch detected! Client-side has no scene loaded with handle ({networkObject.SceneOriginHandle})!");
|
$"client scene mismatch detected! Client-side has no scene loaded with handle ({networkObject.SceneOriginHandle})!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2026,8 +2041,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
// Include anything in the DDOL scene
|
// Include anything in the DDOL scene
|
||||||
PopulateScenePlacedObjects(DontDestroyOnLoadScene, false);
|
PopulateScenePlacedObjects(DontDestroyOnLoadScene, false);
|
||||||
// Synchronize the NetworkObjects for this scene
|
|
||||||
sceneEventData.SynchronizeSceneNetworkObjects(m_NetworkManager);
|
|
||||||
|
|
||||||
// If needed, set the currently active scene
|
// If needed, set the currently active scene
|
||||||
if (HashToBuildIndex.ContainsKey(sceneEventData.ActiveSceneHash))
|
if (HashToBuildIndex.ContainsKey(sceneEventData.ActiveSceneHash))
|
||||||
@@ -2039,23 +2052,26 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If needed, migrate dynamically spawned NetworkObjects to the same scene as on the server side
|
// Spawn and Synchronize all NetworkObjects
|
||||||
|
sceneEventData.SynchronizeSceneNetworkObjects(NetworkManager);
|
||||||
|
|
||||||
|
// If needed, migrate dynamically spawned NetworkObjects to the same scene as they are on the server
|
||||||
SynchronizeNetworkObjectScene();
|
SynchronizeNetworkObjectScene();
|
||||||
|
|
||||||
sceneEventData.SceneEventType = SceneEventType.SynchronizeComplete;
|
sceneEventData.SceneEventType = SceneEventType.SynchronizeComplete;
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||||
|
|
||||||
// All scenes are synchronized, let the server know we are done synchronizing
|
// All scenes are synchronized, let the server know we are done synchronizing
|
||||||
m_NetworkManager.IsConnectedClient = true;
|
NetworkManager.IsConnectedClient = true;
|
||||||
|
|
||||||
// Client is now synchronized and fully "connected". This also means the client can send "RPCs" at this time
|
// Client is now synchronized and fully "connected". This also means the client can send "RPCs" at this time
|
||||||
m_NetworkManager.InvokeOnClientConnectedCallback(m_NetworkManager.LocalClientId);
|
NetworkManager.ConnectionManager.InvokeOnClientConnectedCallback(NetworkManager.LocalClientId);
|
||||||
|
|
||||||
// Notify the client that they have finished synchronizing
|
// Notify the client that they have finished synchronizing
|
||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
{
|
{
|
||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
ClientId = m_NetworkManager.LocalClientId, // Client sent this to the server
|
ClientId = NetworkManager.LocalClientId, // Client sent this to the server
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process any SceneEventType.ObjectSceneChanged messages that
|
// Process any SceneEventType.ObjectSceneChanged messages that
|
||||||
@@ -2068,10 +2084,10 @@ namespace Unity.Netcode
|
|||||||
// scene not synchronized by the server will remain loaded)
|
// scene not synchronized by the server will remain loaded)
|
||||||
if (PostSynchronizationSceneUnloading && ClientSynchronizationMode == LoadSceneMode.Additive)
|
if (PostSynchronizationSceneUnloading && ClientSynchronizationMode == LoadSceneMode.Additive)
|
||||||
{
|
{
|
||||||
SceneManagerHandler.UnloadUnassignedScenes(m_NetworkManager);
|
SceneManagerHandler.UnloadUnassignedScenes(NetworkManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnSynchronizeComplete?.Invoke(m_NetworkManager.LocalClientId);
|
OnSynchronizeComplete?.Invoke(NetworkManager.LocalClientId);
|
||||||
|
|
||||||
EndSceneEvent(sceneEventId);
|
EndSceneEvent(sceneEventId);
|
||||||
}
|
}
|
||||||
@@ -2183,6 +2199,10 @@ namespace Unity.Netcode
|
|||||||
ClientId = clientId
|
ClientId = clientId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// At this point the client is considered fully "connected"
|
||||||
|
NetworkManager.ConnectedClients[clientId].IsConnected = true;
|
||||||
|
|
||||||
|
// All scenes are synchronized, let the server know we are done synchronizing
|
||||||
OnSynchronizeComplete?.Invoke(clientId);
|
OnSynchronizeComplete?.Invoke(clientId);
|
||||||
|
|
||||||
// At this time the client is fully synchronized with all loaded scenes and
|
// At this time the client is fully synchronized with all loaded scenes and
|
||||||
@@ -2191,11 +2211,11 @@ namespace Unity.Netcode
|
|||||||
// TODO 2023: We should have a better name for this or have multiple states the
|
// TODO 2023: We should have a better name for this or have multiple states the
|
||||||
// client progresses through (the name and associated legacy behavior/expected state
|
// client progresses through (the name and associated legacy behavior/expected state
|
||||||
// of the client was persisted since MLAPI)
|
// of the client was persisted since MLAPI)
|
||||||
m_NetworkManager.InvokeOnClientConnectedCallback(clientId);
|
NetworkManager.ConnectionManager.InvokeOnClientConnectedCallback(clientId);
|
||||||
|
|
||||||
// Check to see if the client needs to resynchronize and before sending the message make sure the client is still connected to avoid
|
// Check to see if the client needs to resynchronize and before sending the message make sure the client is still connected to avoid
|
||||||
// a potential crash within the MessageSystem (i.e. sending to a client that no longer exists)
|
// a potential crash within the MessageSystem (i.e. sending to a client that no longer exists)
|
||||||
if (sceneEventData.ClientNeedsReSynchronization() && !DisableReSynchronization && m_NetworkManager.ConnectedClients.ContainsKey(clientId))
|
if (sceneEventData.ClientNeedsReSynchronization() && !DisableReSynchronization && NetworkManager.ConnectedClients.ContainsKey(clientId))
|
||||||
{
|
{
|
||||||
sceneEventData.SceneEventType = SceneEventType.ReSynchronize;
|
sceneEventData.SceneEventType = SceneEventType.ReSynchronize;
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { clientId });
|
SendSceneEventData(sceneEventId, new ulong[] { clientId });
|
||||||
@@ -2225,13 +2245,13 @@ namespace Unity.Netcode
|
|||||||
/// <param name="reader">data associated with the scene event</param>
|
/// <param name="reader">data associated with the scene event</param>
|
||||||
internal void HandleSceneEvent(ulong clientId, FastBufferReader reader)
|
internal void HandleSceneEvent(ulong clientId, FastBufferReader reader)
|
||||||
{
|
{
|
||||||
if (m_NetworkManager != null)
|
if (NetworkManager != null)
|
||||||
{
|
{
|
||||||
var sceneEventData = BeginSceneEvent();
|
var sceneEventData = BeginSceneEvent();
|
||||||
|
|
||||||
sceneEventData.Deserialize(reader);
|
sceneEventData.Deserialize(reader);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventReceived(
|
NetworkManager.NetworkMetrics.TrackSceneEventReceived(
|
||||||
clientId, (uint)sceneEventData.SceneEventType, SceneNameFromHash(sceneEventData.SceneHash), reader.Length);
|
clientId, (uint)sceneEventData.SceneEventType, SceneNameFromHash(sceneEventData.SceneHash), reader.Length);
|
||||||
|
|
||||||
if (sceneEventData.IsSceneEventClientSide())
|
if (sceneEventData.IsSceneEventClientSide())
|
||||||
@@ -2250,7 +2270,7 @@ namespace Unity.Netcode
|
|||||||
// used if the server is synchronizing the same scenes (i.e. if a matching scene is already loaded on the
|
// used if the server is synchronizing the same scenes (i.e. if a matching scene is already loaded on the
|
||||||
// client side, then that scene will be used as opposed to loading another scene). This allows for clients
|
// client side, then that scene will be used as opposed to loading another scene). This allows for clients
|
||||||
// to reconnect to a network session without having to unload all of the scenes and reload all of the scenes.
|
// to reconnect to a network session without having to unload all of the scenes and reload all of the scenes.
|
||||||
SceneManagerHandler.PopulateLoadedScenes(ref ScenesLoaded, m_NetworkManager);
|
SceneManagerHandler.PopulateLoadedScenes(ref ScenesLoaded, NetworkManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HandleClientSceneEvent(sceneEventData.SceneEventId);
|
HandleClientSceneEvent(sceneEventData.SceneEventId);
|
||||||
@@ -2262,7 +2282,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogError($"{nameof(HandleSceneEvent)} was invoked but {nameof(NetworkManager)} reference was null!");
|
Debug.LogError($"{nameof(HandleSceneEvent)} was invoked but {nameof(Netcode.NetworkManager)} reference was null!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2274,7 +2294,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
// Create a local copy of the spawned objects list since the spawn manager will adjust the list as objects
|
// Create a local copy of the spawned objects list since the spawn manager will adjust the list as objects
|
||||||
// are despawned.
|
// are despawned.
|
||||||
var localSpawnedObjectsHashSet = new HashSet<NetworkObject>(m_NetworkManager.SpawnManager.SpawnedObjectsList);
|
var localSpawnedObjectsHashSet = new HashSet<NetworkObject>(NetworkManager.SpawnManager.SpawnedObjectsList);
|
||||||
foreach (var networkObject in localSpawnedObjectsHashSet)
|
foreach (var networkObject in localSpawnedObjectsHashSet)
|
||||||
{
|
{
|
||||||
if (networkObject == null || (networkObject != null && networkObject.gameObject.scene == DontDestroyOnLoadScene))
|
if (networkObject == null || (networkObject != null && networkObject.gameObject.scene == DontDestroyOnLoadScene))
|
||||||
@@ -2291,7 +2311,7 @@ namespace Unity.Netcode
|
|||||||
UnityEngine.Object.DontDestroyOnLoad(networkObject.gameObject);
|
UnityEngine.Object.DontDestroyOnLoad(networkObject.gameObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_NetworkManager.IsServer)
|
else if (NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
networkObject.Despawn();
|
networkObject.Despawn();
|
||||||
}
|
}
|
||||||
@@ -2330,7 +2350,7 @@ namespace Unity.Netcode
|
|||||||
var globalObjectIdHash = networkObjectInstance.GlobalObjectIdHash;
|
var globalObjectIdHash = networkObjectInstance.GlobalObjectIdHash;
|
||||||
var sceneHandle = networkObjectInstance.gameObject.scene.handle;
|
var sceneHandle = networkObjectInstance.gameObject.scene.handle;
|
||||||
// We check to make sure the NetworkManager instance is the same one to be "NetcodeIntegrationTestHelpers" compatible and filter the list on a per scene basis (for additive scenes)
|
// We check to make sure the NetworkManager instance is the same one to be "NetcodeIntegrationTestHelpers" compatible and filter the list on a per scene basis (for additive scenes)
|
||||||
if (networkObjectInstance.IsSceneObject != false && (networkObjectInstance.NetworkManager == m_NetworkManager ||
|
if (networkObjectInstance.IsSceneObject != false && (networkObjectInstance.NetworkManager == NetworkManager ||
|
||||||
networkObjectInstance.NetworkManagerOwner == null) && sceneHandle == sceneToFilterBy.handle)
|
networkObjectInstance.NetworkManagerOwner == null) && sceneHandle == sceneToFilterBy.handle)
|
||||||
{
|
{
|
||||||
if (!ScenePlacedObjects.ContainsKey(globalObjectIdHash))
|
if (!ScenePlacedObjects.ContainsKey(globalObjectIdHash))
|
||||||
@@ -2358,7 +2378,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="scene">scene to move the NetworkObjects to</param>
|
/// <param name="scene">scene to move the NetworkObjects to</param>
|
||||||
internal void MoveObjectsFromDontDestroyOnLoadToScene(Scene scene)
|
internal void MoveObjectsFromDontDestroyOnLoadToScene(Scene scene)
|
||||||
{
|
{
|
||||||
foreach (var networkObject in m_NetworkManager.SpawnManager.SpawnedObjectsList)
|
foreach (var networkObject in NetworkManager.SpawnManager.SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
if (networkObject == null)
|
if (networkObject == null)
|
||||||
{
|
{
|
||||||
@@ -2389,9 +2409,9 @@ namespace Unity.Netcode
|
|||||||
internal void NotifyNetworkObjectSceneChanged(NetworkObject networkObject)
|
internal void NotifyNetworkObjectSceneChanged(NetworkObject networkObject)
|
||||||
{
|
{
|
||||||
// Really, this should never happen but in case it does
|
// Really, this should never happen but in case it does
|
||||||
if (!m_NetworkManager.IsServer)
|
if (!NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
if (m_NetworkManager.LogLevel == LogLevel.Developer)
|
if (NetworkManager.LogLevel == LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogErrorServer("[Please Report This Error][NotifyNetworkObjectSceneChanged] A client is trying to notify of an object's scene change!");
|
NetworkLog.LogErrorServer("[Please Report This Error][NotifyNetworkObjectSceneChanged] A client is trying to notify of an object's scene change!");
|
||||||
}
|
}
|
||||||
@@ -2402,7 +2422,7 @@ namespace Unity.Netcode
|
|||||||
if (networkObject.IsSceneObject != false)
|
if (networkObject.IsSceneObject != false)
|
||||||
{
|
{
|
||||||
// Really, this should ever happen but in case it does
|
// Really, this should ever happen but in case it does
|
||||||
if (m_NetworkManager.LogLevel == LogLevel.Developer)
|
if (NetworkManager.LogLevel == LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogErrorServer("[Please Report This Error][NotifyNetworkObjectSceneChanged] Trying to notify in-scene placed object scene change!");
|
NetworkLog.LogErrorServer("[Please Report This Error][NotifyNetworkObjectSceneChanged] Trying to notify in-scene placed object scene change!");
|
||||||
}
|
}
|
||||||
@@ -2468,20 +2488,57 @@ namespace Unity.Netcode
|
|||||||
ObjectsMigratedIntoNewScene.Clear();
|
ObjectsMigratedIntoNewScene.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<int> m_ScenesToRemoveFromObjectMigration = new List<int>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should be invoked during PostLateUpdate just prior to the
|
/// Should be invoked during PostLateUpdate just prior to the NetworkMessageManager processes its outbound message queue.
|
||||||
/// MessagingSystem processes its outbound message queue.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal void CheckForAndSendNetworkObjectSceneChanged()
|
internal void CheckForAndSendNetworkObjectSceneChanged()
|
||||||
{
|
{
|
||||||
// Early exit if not the server or there is nothing pending
|
// Early exit if not the server or there is nothing pending
|
||||||
if (!m_NetworkManager.IsServer || ObjectsMigratedIntoNewScene.Count == 0)
|
if (!NetworkManager.IsServer || ObjectsMigratedIntoNewScene.Count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Double check that the NetworkObjects to migrate still exist
|
||||||
|
m_ScenesToRemoveFromObjectMigration.Clear();
|
||||||
|
foreach (var sceneEntry in ObjectsMigratedIntoNewScene)
|
||||||
|
{
|
||||||
|
for (int i = sceneEntry.Value.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
// Remove NetworkObjects that are no longer spawned
|
||||||
|
if (!sceneEntry.Value[i].IsSpawned)
|
||||||
|
{
|
||||||
|
sceneEntry.Value.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the scene entry no longer has any NetworkObjects to migrate
|
||||||
|
// then add it to the list of scenes to be removed from the table
|
||||||
|
// of scenes containing NetworkObjects to migrate.
|
||||||
|
if (sceneEntry.Value.Count == 0)
|
||||||
|
{
|
||||||
|
m_ScenesToRemoveFromObjectMigration.Add(sceneEntry.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove sceneHandle entries that no longer have any NetworkObjects remaining
|
||||||
|
foreach (var sceneHandle in m_ScenesToRemoveFromObjectMigration)
|
||||||
|
{
|
||||||
|
ObjectsMigratedIntoNewScene.Remove(sceneHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is nothing to send a migration notification for then exit
|
||||||
|
if (ObjectsMigratedIntoNewScene.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some NetworkObjects still exist, send the message
|
||||||
var sceneEvent = BeginSceneEvent();
|
var sceneEvent = BeginSceneEvent();
|
||||||
sceneEvent.SceneEventType = SceneEventType.ObjectSceneChanged;
|
sceneEvent.SceneEventType = SceneEventType.ObjectSceneChanged;
|
||||||
SendSceneEventData(sceneEvent.SceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
|
SendSceneEventData(sceneEvent.SceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
|
||||||
EndSceneEvent(sceneEvent.SceneEventId);
|
EndSceneEvent(sceneEvent.SceneEventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,25 @@ namespace Unity.Netcode
|
|||||||
/// <typeparam name="T">The type being serialized</typeparam>
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read or write a NativeArray of struct values implementing ISerializeByMemcpy
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray when reading</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
public void SerializeValue<T>(ref NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Implementation.SerializeValue(ref value, allocator);
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Read or write a NativeList of struct values implementing ISerializeByMemcpy
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
public void SerializeValue<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Implementation.SerializeValue(ref value);
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read or write a struct or class value implementing INetworkSerializable
|
/// Read or write a struct or class value implementing INetworkSerializable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -266,6 +285,7 @@ namespace Unity.Netcode
|
|||||||
// Those two are necessary to serialize FixedStrings efficiently
|
// Those two are necessary to serialize FixedStrings efficiently
|
||||||
// - otherwise we'd just be memcpy'ing the whole thing even if
|
// - otherwise we'd just be memcpy'ing the whole thing even if
|
||||||
// most of it isn't used.
|
// most of it isn't used.
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read or write a FixedString value
|
/// Read or write a FixedString value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -275,6 +295,27 @@ namespace Unity.Netcode
|
|||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Implementation.SerializeValue(ref value);
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Implementation.SerializeValue(ref value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read or write a NativeArray of FixedString values
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The network serializable type</typeparam>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray when reading</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution of FixedStrings</param>
|
||||||
|
public void SerializeValue<T>(ref NativeArray<T> value, Allocator allocator)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Implementation.SerializeValue(ref value, allocator);
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Read or write a NativeList of FixedString values
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The network serializable type</typeparam>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution of FixedStrings</param>
|
||||||
|
public void SerializeValue<T>(ref NativeList<T> value)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Implementation.SerializeValue(ref value);
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read or write a NetworkSerializable value.
|
/// Read or write a NetworkSerializable value.
|
||||||
/// SerializeValue() is the preferred method to do this - this is provided for backward compatibility only.
|
/// SerializeValue() is the preferred method to do this - this is provided for backward compatibility only.
|
||||||
@@ -381,6 +422,31 @@ namespace Unity.Netcode
|
|||||||
/// <param name="unused">An unused parameter used for enabling overload resolution of structs</param>
|
/// <param name="unused">An unused parameter used for enabling overload resolution of structs</param>
|
||||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value);
|
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialize a NativeArray of structs, "pre-checked", which skips buffer checks.
|
||||||
|
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||||
|
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||||
|
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The network serializable types in an array</typeparam>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray when reading</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution of structs</param>
|
||||||
|
public void SerializeValuePreChecked<T>(ref NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Implementation.SerializeValuePreChecked(ref value, allocator);
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Serialize a NativeList of structs, "pre-checked", which skips buffer checks.
|
||||||
|
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||||
|
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||||
|
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The network serializable types in an array</typeparam>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution of structs</param>
|
||||||
|
public void SerializeValuePreChecked<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Implementation.SerializeValuePreChecked(ref value);
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serialize a Vector2, "pre-checked", which skips buffer checks.
|
/// Serialize a Vector2, "pre-checked", which skips buffer checks.
|
||||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||||
@@ -463,7 +529,7 @@ namespace Unity.Netcode
|
|||||||
public void SerializeValuePreChecked(ref Vector4 value) => m_Implementation.SerializeValuePreChecked(ref value);
|
public void SerializeValuePreChecked(ref Vector4 value) => m_Implementation.SerializeValuePreChecked(ref value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serialize a Vector4Array, "pre-checked", which skips buffer checks.
|
/// Serialize a Vector4 array, "pre-checked", which skips buffer checks.
|
||||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||||
|
|||||||
@@ -30,34 +30,56 @@ namespace Unity.Netcode
|
|||||||
public void SerializeValue(ref byte value) => m_Reader.ReadByteSafe(out value);
|
public void SerializeValue(ref byte value) => m_Reader.ReadByteSafe(out value);
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValueSafe(out value);
|
||||||
|
public void SerializeValue<T>(ref NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Reader.ReadValueSafe(out value, allocator);
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
public void SerializeValue<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Reader.ReadValueSafeInPlace(ref value);
|
||||||
|
#endif
|
||||||
|
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Reader.ReadNetworkSerializableInPlace(ref value);
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Reader.ReadNetworkSerializableInPlace(ref value);
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Reader.ReadValue(out value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Reader.ReadValueSafe(out value);
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
|
public void SerializeValue<T>(ref NativeArray<T> value, Allocator allocator) where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Reader.ReadValueSafe(out value, allocator);
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
public void SerializeValue<T>(ref NativeList<T> value) where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Reader.ReadValueSafeInPlace(ref value);
|
||||||
|
#endif
|
||||||
|
|
||||||
public void SerializeValue(ref Vector2 value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector2 value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Vector2[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector2[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Vector3 value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector3 value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Vector3[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector3[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Vector2Int value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector2Int value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Vector2Int[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector2Int[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Vector3Int value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector3Int value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Vector3Int[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector3Int[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Vector4 value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector4 value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Vector4[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Vector4[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Quaternion value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Quaternion value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Quaternion[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Quaternion[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Color value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Color value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Color[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Color[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Color32 value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Color32 value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Color32[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Color32[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Ray value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Ray value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Ray[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Ray[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
public void SerializeValue(ref Ray2D value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Ray2D value) => m_Reader.ReadValueSafe(out value);
|
||||||
public void SerializeValue(ref Ray2D[] value) => m_Reader.ReadValueSafe(out value);
|
public void SerializeValue(ref Ray2D[] value) => m_Reader.ReadValueSafe(out value);
|
||||||
|
|
||||||
@@ -72,30 +94,48 @@ namespace Unity.Netcode
|
|||||||
public void SerializeValuePreChecked(ref byte value) => m_Reader.ReadByte(out value);
|
public void SerializeValuePreChecked(ref byte value) => m_Reader.ReadByte(out value);
|
||||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValue(out value);
|
||||||
|
public void SerializeValuePreChecked<T>(ref NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Reader.ReadValue(out value, allocator);
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
public void SerializeValuePreChecked<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Reader.ReadValueInPlace(ref value);
|
||||||
|
#endif
|
||||||
|
|
||||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Reader.ReadValue(out value);
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector2 value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector2 value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Vector2[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector2[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector3 value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector3 value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Vector3[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector3[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector2Int value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector2Int value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Vector2Int[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector2Int[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector3Int value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector3Int value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Vector3Int[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector3Int[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector4 value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector4 value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Vector4[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Vector4[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Quaternion value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Quaternion value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Color value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Color value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Color[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Color[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Color32 value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Color32 value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Color32[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Color32[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Ray value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Ray value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Ray[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Ray[] value) => m_Reader.ReadValue(out value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Ray2D value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Ray2D value) => m_Reader.ReadValue(out value);
|
||||||
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Reader.ReadValue(out value);
|
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Reader.ReadValue(out value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,33 +30,55 @@ namespace Unity.Netcode
|
|||||||
public void SerializeValue(ref byte value) => m_Writer.WriteByteSafe(value);
|
public void SerializeValue(ref byte value) => m_Writer.WriteByteSafe(value);
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Writer.WriteValueSafe(value);
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Writer.WriteValueSafe(value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValueSafe(value);
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValueSafe(value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValueSafe(value);
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValueSafe(value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValueSafe(value);
|
||||||
|
public void SerializeValue<T>(ref NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Writer.WriteValueSafe(value);
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
public void SerializeValue<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Writer.WriteValueSafe(value);
|
||||||
|
#endif
|
||||||
|
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Writer.WriteValue(value);
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Writer.WriteValue(value);
|
||||||
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Writer.WriteValue(value);
|
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
public void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Writer.WriteValueSafe(value);
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Writer.WriteValueSafe(value);
|
||||||
|
public void SerializeValue<T>(ref NativeArray<T> value, Allocator allocator) where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
public void SerializeValue<T>(ref NativeList<T> value) where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Writer.WriteValueSafe(value);
|
||||||
|
#endif
|
||||||
|
|
||||||
public void SerializeValue(ref Vector2 value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector2 value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Vector2[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector2[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Vector3 value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector3 value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Vector3[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector3[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Vector2Int value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector2Int value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Vector2Int[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector2Int[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Vector3Int value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector3Int value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Vector3Int[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector3Int[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Vector4 value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector4 value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Vector4[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Vector4[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Quaternion value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Quaternion value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Quaternion[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Quaternion[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Color value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Color value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Color[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Color[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Color32 value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Color32 value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Color32[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Color32[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Ray value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Ray value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Ray[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Ray[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
public void SerializeValue(ref Ray2D value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Ray2D value) => m_Writer.WriteValueSafe(value);
|
||||||
public void SerializeValue(ref Ray2D[] value) => m_Writer.WriteValueSafe(value);
|
public void SerializeValue(ref Ray2D[] value) => m_Writer.WriteValueSafe(value);
|
||||||
|
|
||||||
@@ -77,29 +99,44 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValue(value);
|
||||||
|
public void SerializeValuePreChecked<T>(ref NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Writer.WriteValue(value);
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
public void SerializeValuePreChecked<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged => m_Writer.WriteValue(value);
|
||||||
|
#endif
|
||||||
|
|
||||||
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Writer.WriteValue(value);
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector2 value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector2 value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Vector2[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector2[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector3 value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector3 value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Vector3[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector3[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector2Int value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector2Int value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Vector2Int[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector2Int[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector3Int value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector3Int value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Vector3Int[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector3Int[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Vector4 value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector4 value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Vector4[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Vector4[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Quaternion value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Quaternion value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Color value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Color value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Color[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Color[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Color32 value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Color32 value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Color32[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Color32[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Ray value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Ray value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Ray[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Ray[] value) => m_Writer.WriteValue(value);
|
||||||
|
|
||||||
public void SerializeValuePreChecked(ref Ray2D value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Ray2D value) => m_Writer.WriteValue(value);
|
||||||
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Writer.WriteValue(value);
|
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Writer.WriteValue(value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private static unsafe ReaderHandle* CreateHandle(byte* buffer, int length, int offset, Allocator copyAllocator, Allocator internalAllocator)
|
private static unsafe ReaderHandle* CreateHandle(byte* buffer, int length, int offset, Allocator copyAllocator, Allocator internalAllocator)
|
||||||
{
|
{
|
||||||
ReaderHandle* readerHandle = null;
|
ReaderHandle* readerHandle;
|
||||||
if (copyAllocator == Allocator.None)
|
if (copyAllocator == Allocator.None)
|
||||||
{
|
{
|
||||||
readerHandle = (ReaderHandle*)UnsafeUtility.Malloc(sizeof(ReaderHandle), UnsafeUtility.AlignOf<byte>(), internalAllocator);
|
readerHandle = (ReaderHandle*)UnsafeUtility.Malloc(sizeof(ReaderHandle), UnsafeUtility.AlignOf<byte>(), internalAllocator);
|
||||||
@@ -461,6 +461,42 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a NativeArray of INetworkSerializables
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">INetworkSerializable instance</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray</param>
|
||||||
|
/// <typeparam name="T">the array to read the values of type `T` into</typeparam>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public void ReadNetworkSerializable<T>(out NativeArray<T> value, Allocator allocator) where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
ReadValueSafe(out int size);
|
||||||
|
value = new NativeArray<T>(size, allocator);
|
||||||
|
for (var i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
ReadNetworkSerializable(out T item);
|
||||||
|
value[i] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Read a NativeList of INetworkSerializables
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">INetworkSerializable instance</param>
|
||||||
|
/// <typeparam name="T">the array to read the values of type `T` into</typeparam>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public void ReadNetworkSerializableInPlace<T>(ref NativeList<T> value) where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
ReadValueSafe(out int size);
|
||||||
|
value.Resize(size, NativeArrayOptions.UninitializedMemory);
|
||||||
|
for (var i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
ReadNetworkSerializable(out value.ElementAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read an INetworkSerializable in-place, without constructing a new one
|
/// Read an INetworkSerializable in-place, without constructing a new one
|
||||||
/// Note that this will NOT check for null before calling NetworkSerialize
|
/// Note that this will NOT check for null before calling NetworkSerialize
|
||||||
@@ -757,6 +793,44 @@ namespace Unity.Netcode
|
|||||||
ReadBytesSafe(bytes, sizeInBytes);
|
ReadBytesSafe(bytes, sizeInBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal unsafe void ReadUnmanaged<T>(out NativeArray<T> value, Allocator allocator) where T : unmanaged
|
||||||
|
{
|
||||||
|
ReadUnmanaged(out int sizeInTs);
|
||||||
|
int sizeInBytes = sizeInTs * sizeof(T);
|
||||||
|
value = new NativeArray<T>(sizeInTs, allocator);
|
||||||
|
byte* bytes = (byte*)value.GetUnsafePtr();
|
||||||
|
ReadBytes(bytes, sizeInBytes);
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal unsafe void ReadUnmanagedSafe<T>(out NativeArray<T> value, Allocator allocator) where T : unmanaged
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafe(out int sizeInTs);
|
||||||
|
int sizeInBytes = sizeInTs * sizeof(T);
|
||||||
|
value = new NativeArray<T>(sizeInTs, allocator);
|
||||||
|
byte* bytes = (byte*)value.GetUnsafePtr();
|
||||||
|
ReadBytesSafe(bytes, sizeInBytes);
|
||||||
|
}
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal unsafe void ReadUnmanagedInPlace<T>(ref NativeList<T> value) where T : unmanaged
|
||||||
|
{
|
||||||
|
ReadUnmanaged(out int sizeInTs);
|
||||||
|
int sizeInBytes = sizeInTs * sizeof(T);
|
||||||
|
value.Resize(sizeInTs, NativeArrayOptions.UninitializedMemory);
|
||||||
|
byte* bytes = (byte*)value.GetUnsafePtr();
|
||||||
|
ReadBytes(bytes, sizeInBytes);
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal unsafe void ReadUnmanagedSafeInPlace<T>(ref NativeList<T> value) where T : unmanaged
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafe(out int sizeInTs);
|
||||||
|
int sizeInBytes = sizeInTs * sizeof(T);
|
||||||
|
value.Resize(sizeInTs, NativeArrayOptions.UninitializedMemory);
|
||||||
|
byte* bytes = (byte*)value.GetUnsafePtr();
|
||||||
|
ReadBytesSafe(bytes, sizeInBytes);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a NetworkSerializable value
|
/// Read a NetworkSerializable value
|
||||||
@@ -800,6 +874,19 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
|
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a NetworkSerializable NativeArray
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple reads at once by calling TryBeginRead.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValueSafe<T>(out NativeArray<T> value, Allocator allocator, FastBufferWriter.ForNetworkSerializable unused = default) where T : unmanaged, INetworkSerializable => ReadNetworkSerializable(out value, allocator);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a struct
|
/// Read a struct
|
||||||
@@ -819,6 +906,72 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void ReadValue<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value);
|
public void ReadValue<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a struct NativeArray
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValue<T>(out NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer.ReadWithAllocator(this, out value, allocator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReadUnmanaged(out value, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a struct NativeArray using a Temp allocator. Equivalent to ReadValue(out value, Allocator.Temp)
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValueTemp<T>(out NativeArray<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer.ReadWithAllocator(this, out value, Allocator.Temp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReadUnmanaged(out value, Allocator.Temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Read a struct NativeList
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValueInPlace<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.Serializer.Read(this, ref value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReadUnmanagedInPlace(ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a struct
|
/// Read a struct
|
||||||
///
|
///
|
||||||
@@ -843,6 +996,81 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value);
|
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a struct NativeArray
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple reads at once by calling TryBeginRead.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValueSafe<T>(out NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer.ReadWithAllocator(this, out value, allocator);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafe(out value, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a struct NativeArray using a Temp allocator. Equivalent to ReadValueSafe(out value, Allocator.Temp)
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple reads at once by calling TryBeginRead.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValueSafeTemp<T>(out NativeArray<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer.ReadWithAllocator(this, out value, Allocator.Temp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafe(out value, Allocator.Temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Read a struct NativeList
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple reads at once by calling TryBeginRead.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValueSafeInPlace<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.Serializer.Read(this, ref value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafeInPlace(ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a primitive value (int, bool, etc)
|
/// Read a primitive value (int, bool, etc)
|
||||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||||
@@ -880,7 +1108,7 @@ namespace Unity.Netcode
|
|||||||
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanagedSafe(out value);
|
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanagedSafe(out value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a primitive value (int, bool, etc)
|
/// Read a primitive value (int, bool, etc) array
|
||||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||||
/// on values that are not primitives.
|
/// on values that are not primitives.
|
||||||
///
|
///
|
||||||
@@ -936,6 +1164,7 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value);
|
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read a Vector2
|
/// Read a Vector2
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1346,5 +1575,94 @@ namespace Unity.Netcode
|
|||||||
value.Length = length;
|
value.Length = length;
|
||||||
ReadBytesSafe(value.GetUnsafePtr(), length);
|
ReadBytesSafe(value.GetUnsafePtr(), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a FixedString NativeArray.
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple reads at once by calling TryBeginRead.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to read</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe void ReadValueSafe<T>(out NativeArray<T> value, Allocator allocator)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafe(out int length);
|
||||||
|
value = new NativeArray<T>(length, allocator);
|
||||||
|
var ptr = (T*)value.GetUnsafePtr();
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
ReadValueSafeInPlace(ref ptr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a FixedString NativeArray using a Temp allocator. Equivalent to ReadValueSafe(out value, Allocator.Temp)
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple reads at once by calling TryBeginRead.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to read</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe void ReadValueSafeTemp<T>(out NativeArray<T> value)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafe(out int length);
|
||||||
|
value = new NativeArray<T>(length, Allocator.Temp);
|
||||||
|
var ptr = (T*)value.GetUnsafePtr();
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
ReadValueSafeInPlace(ref ptr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read a FixedString NativeArray using a Temp allocator. Equivalent to ReadValueSafe(out value, Allocator.Temp)
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple reads at once by calling TryBeginRead.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to read</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForFixedStrings unused = default)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafe(out int length);
|
||||||
|
value = new T[length];
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
ReadValueSafeInPlace(ref value[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Read a FixedString NativeList.
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple reads at once by calling TryBeginRead.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to read</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void ReadValueSafeInPlace<T>(ref NativeList<T> value)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
ReadUnmanagedSafe(out int length);
|
||||||
|
value.Resize(length, NativeArrayOptions.UninitializedMemory);
|
||||||
|
for (var i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
ReadValueSafeInPlace(ref value.ElementAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -452,6 +452,42 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a NativeArray of INetworkSerializables
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">The value to write</param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <param name="offset"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public void WriteNetworkSerializable<T>(NativeArray<T> array, int count = -1, int offset = 0) where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
int sizeInTs = count != -1 ? count : array.Length - offset;
|
||||||
|
WriteValueSafe(sizeInTs);
|
||||||
|
foreach (var item in array)
|
||||||
|
{
|
||||||
|
WriteNetworkSerializable(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Write a NativeList of INetworkSerializables
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">The value to write</param>
|
||||||
|
/// <param name="count"></param>
|
||||||
|
/// <param name="offset"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public void WriteNetworkSerializable<T>(NativeList<T> array, int count = -1, int offset = 0) where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
int sizeInTs = count != -1 ? count : array.Length - offset;
|
||||||
|
WriteValueSafe(sizeInTs);
|
||||||
|
foreach (var item in array)
|
||||||
|
{
|
||||||
|
WriteNetworkSerializable(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a string
|
/// Writes a string
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -536,6 +572,40 @@ namespace Unity.Netcode
|
|||||||
return sizeof(int) + sizeInBytes;
|
return sizeof(int) + sizeInBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the required size to write a NativeArray
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">The array to write</param>
|
||||||
|
/// <param name="count">The amount of elements to write</param>
|
||||||
|
/// <param name="offset">Where in the array to start</param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static unsafe int GetWriteSize<T>(NativeArray<T> array, int count = -1, int offset = 0) where T : unmanaged
|
||||||
|
{
|
||||||
|
int sizeInTs = count != -1 ? count : array.Length - offset;
|
||||||
|
int sizeInBytes = sizeInTs * sizeof(T);
|
||||||
|
return sizeof(int) + sizeInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Get the required size to write a NativeList
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="array">The array to write</param>
|
||||||
|
/// <param name="count">The amount of elements to write</param>
|
||||||
|
/// <param name="offset">Where in the array to start</param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static unsafe int GetWriteSize<T>(NativeList<T> array, int count = -1, int offset = 0) where T : unmanaged
|
||||||
|
{
|
||||||
|
int sizeInTs = count != -1 ? count : array.Length - offset;
|
||||||
|
int sizeInBytes = sizeInTs * sizeof(T);
|
||||||
|
return sizeof(int) + sizeInBytes;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a partial value. The specified number of bytes is written from the value and the rest is ignored.
|
/// Write a partial value. The specified number of bytes is written from the value and the rest is ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -680,6 +750,32 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write multiple bytes to the stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Value to write</param>
|
||||||
|
/// <param name="size">Number of bytes to write</param>
|
||||||
|
/// <param name="offset">Offset into the buffer to begin writing</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe void WriteBytes(NativeArray<byte> value, int size = -1, int offset = 0)
|
||||||
|
{
|
||||||
|
byte* ptr = (byte*)value.GetUnsafePtr();
|
||||||
|
WriteBytes(ptr, size == -1 ? value.Length : size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write multiple bytes to the stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Value to write</param>
|
||||||
|
/// <param name="size">Number of bytes to write</param>
|
||||||
|
/// <param name="offset">Offset into the buffer to begin writing</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe void WriteBytes(NativeList<byte> value, int size = -1, int offset = 0)
|
||||||
|
{
|
||||||
|
byte* ptr = (byte*)value.GetUnsafePtr();
|
||||||
|
WriteBytes(ptr, size == -1 ? value.Length : size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write multiple bytes to the stream
|
/// Write multiple bytes to the stream
|
||||||
///
|
///
|
||||||
@@ -698,6 +794,32 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write multiple bytes to the stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Value to write</param>
|
||||||
|
/// <param name="size">Number of bytes to write</param>
|
||||||
|
/// <param name="offset">Offset into the buffer to begin writing</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe void WriteBytesSafe(NativeArray<byte> value, int size = -1, int offset = 0)
|
||||||
|
{
|
||||||
|
byte* ptr = (byte*)value.GetUnsafePtr();
|
||||||
|
WriteBytesSafe(ptr, size == -1 ? value.Length : size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write multiple bytes to the stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Value to write</param>
|
||||||
|
/// <param name="size">Number of bytes to write</param>
|
||||||
|
/// <param name="offset">Offset into the buffer to begin writing</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe void WriteBytesSafe(NativeList<byte> value, int size = -1, int offset = 0)
|
||||||
|
{
|
||||||
|
byte* ptr = (byte*)value.GetUnsafePtr();
|
||||||
|
WriteBytesSafe(ptr, size == -1 ? value.Length : size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy the contents of this writer into another writer.
|
/// Copy the contents of this writer into another writer.
|
||||||
/// The contents will be copied from the beginning of this writer to its current position.
|
/// The contents will be copied from the beginning of this writer to its current position.
|
||||||
@@ -749,6 +871,44 @@ namespace Unity.Netcode
|
|||||||
return value.Length + sizeof(int);
|
return value.Length + sizeof(int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the write size for an array of FixedStrings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int GetWriteSize<T>(in NativeArray<T> value)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
var size = sizeof(int);
|
||||||
|
foreach (var item in value)
|
||||||
|
{
|
||||||
|
size += sizeof(int) + item.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Get the write size for an array of FixedStrings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int GetWriteSize<T>(in NativeList<T> value)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
var size = sizeof(int);
|
||||||
|
foreach (var item in value)
|
||||||
|
{
|
||||||
|
size += sizeof(int) + item.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the size required to write an unmanaged value of type T
|
/// Get the size required to write an unmanaged value of type T
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -799,6 +959,50 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal unsafe void WriteUnmanaged<T>(NativeArray<T> value) where T : unmanaged
|
||||||
|
{
|
||||||
|
WriteUnmanaged(value.Length);
|
||||||
|
var ptr = (T*)value.GetUnsafePtr();
|
||||||
|
{
|
||||||
|
byte* bytes = (byte*)ptr;
|
||||||
|
WriteBytes(bytes, sizeof(T) * value.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal unsafe void WriteUnmanagedSafe<T>(NativeArray<T> value) where T : unmanaged
|
||||||
|
{
|
||||||
|
WriteUnmanagedSafe(value.Length);
|
||||||
|
var ptr = (T*)value.GetUnsafePtr();
|
||||||
|
{
|
||||||
|
byte* bytes = (byte*)ptr;
|
||||||
|
WriteBytesSafe(bytes, sizeof(T) * value.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal unsafe void WriteUnmanaged<T>(NativeList<T> value) where T : unmanaged
|
||||||
|
{
|
||||||
|
WriteUnmanaged(value.Length);
|
||||||
|
var ptr = (T*)value.GetUnsafePtr();
|
||||||
|
{
|
||||||
|
byte* bytes = (byte*)ptr;
|
||||||
|
WriteBytes(bytes, sizeof(T) * value.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal unsafe void WriteUnmanagedSafe<T>(NativeList<T> value) where T : unmanaged
|
||||||
|
{
|
||||||
|
WriteUnmanagedSafe(value.Length);
|
||||||
|
var ptr = (T*)value.GetUnsafePtr();
|
||||||
|
{
|
||||||
|
byte* bytes = (byte*)ptr;
|
||||||
|
WriteBytesSafe(bytes, sizeof(T) * value.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
|
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
|
||||||
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
|
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
|
||||||
@@ -869,6 +1073,20 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
|
||||||
|
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
|
||||||
|
/// methods exist with the same signature, it causes a compile error because they would end up
|
||||||
|
/// being emitted as the same method, even if the constraints are different.
|
||||||
|
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
|
||||||
|
/// which then allows the compiler to do overload resolution based on the generic constraints
|
||||||
|
/// without the user having to pass the struct in themselves.
|
||||||
|
/// </summary>
|
||||||
|
public struct ForGeneric
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a NetworkSerializable value
|
/// Write a NetworkSerializable value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -929,6 +1147,50 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteValue<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value);
|
public void WriteValue<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a struct NativeArray
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The values to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValue<T>(NativeArray<T> value, ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer.Write(this, ref value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteUnmanaged(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Write a struct NativeList
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The values to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValue<T>(NativeList<T> value, ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.Serializer.Write(this, ref value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteUnmanaged(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a struct
|
/// Write a struct
|
||||||
///
|
///
|
||||||
@@ -953,6 +1215,56 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteValueSafe<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value);
|
public void WriteValueSafe<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a struct NativeArray
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple writes at once by calling TryBeginWrite.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The values to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValueSafe<T>(NativeArray<T> value, ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeArray<T>>.Serializer.Write(this, ref value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteUnmanagedSafe(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Write a struct NativeList
|
||||||
|
///
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple writes at once by calling TryBeginWrite.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The values to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValueSafe<T>(NativeList<T> value, ForGeneric unused = default) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||||
|
{
|
||||||
|
// This calls WriteNetworkSerializable in a way that doesn't require
|
||||||
|
// any boxing.
|
||||||
|
NetworkVariableSerialization<NativeList<T>>.Serializer.Write(this, ref value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteUnmanagedSafe(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a primitive value (int, bool, etc)
|
/// Write a primitive value (int, bool, etc)
|
||||||
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
|
||||||
@@ -1185,7 +1497,6 @@ namespace Unity.Netcode
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteValue(Ray2D[] value) => WriteUnmanaged(value);
|
public void WriteValue(Ray2D[] value) => WriteUnmanaged(value);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a Vector2
|
/// Write a Vector2
|
||||||
///
|
///
|
||||||
@@ -1415,6 +1726,65 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write an array of FixedString values. Writes only the part of each string that's actually used.
|
||||||
|
/// When calling TryBeginWrite, ensure you calculate the write size correctly (preferably by calling
|
||||||
|
/// FastBufferWriter.GetWriteSize())
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValue<T>(T[] value, ForFixedStrings unused = default)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
WriteUnmanaged(value.Length);
|
||||||
|
foreach (var str in value)
|
||||||
|
{
|
||||||
|
WriteValue(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a NativeArray of FixedString values. Writes only the part of each string that's actually used.
|
||||||
|
/// When calling TryBeginWrite, ensure you calculate the write size correctly (preferably by calling
|
||||||
|
/// FastBufferWriter.GetWriteSize())
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValue<T>(in NativeArray<T> value, ForFixedStrings unused = default)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
WriteUnmanaged(value.Length);
|
||||||
|
foreach (var str in value)
|
||||||
|
{
|
||||||
|
WriteValue(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Write a NativeList of FixedString values. Writes only the part of each string that's actually used.
|
||||||
|
/// When calling TryBeginWrite, ensure you calculate the write size correctly (preferably by calling
|
||||||
|
/// FastBufferWriter.GetWriteSize())
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValue<T>(in NativeList<T> value, ForFixedStrings unused = default)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
WriteUnmanaged(value.Length);
|
||||||
|
foreach (var str in value)
|
||||||
|
{
|
||||||
|
WriteValue(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Write a FixedString value. Writes only the part of the string that's actually used.
|
/// Write a FixedString value. Writes only the part of the string that's actually used.
|
||||||
@@ -1435,5 +1805,76 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
WriteValue(value);
|
WriteValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a NativeArray of FixedString values. Writes only the part of each string that's actually used.
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple writes at once by calling TryBeginWrite.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValueSafe<T>(T[] value, ForFixedStrings unused = default)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
if (!TryBeginWriteInternal(GetWriteSize(value)))
|
||||||
|
{
|
||||||
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
|
}
|
||||||
|
WriteUnmanaged(value.Length);
|
||||||
|
foreach (var str in value)
|
||||||
|
{
|
||||||
|
WriteValue(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a NativeArray of FixedString values. Writes only the part of each string that's actually used.
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple writes at once by calling TryBeginWrite.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValueSafe<T>(in NativeArray<T> value)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
if (!TryBeginWriteInternal(GetWriteSize(value)))
|
||||||
|
{
|
||||||
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
|
}
|
||||||
|
WriteUnmanaged(value.Length);
|
||||||
|
foreach (var str in value)
|
||||||
|
{
|
||||||
|
WriteValue(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Write a NativeList of FixedString values. Writes only the part of each string that's actually used.
|
||||||
|
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
|
||||||
|
/// for multiple writes at once by calling TryBeginWrite.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">the value to write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteValueSafe<T>(in NativeList<T> value)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||||
|
{
|
||||||
|
if (!TryBeginWriteInternal(GetWriteSize(value)))
|
||||||
|
{
|
||||||
|
throw new OverflowException("Writing past the end of the buffer");
|
||||||
|
}
|
||||||
|
WriteUnmanaged(value.Length);
|
||||||
|
foreach (var str in value)
|
||||||
|
{
|
||||||
|
WriteValue(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,25 @@ namespace Unity.Netcode
|
|||||||
/// <typeparam name="T">The type being serialized</typeparam>
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
|
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read or write a NativeArray of struct values implementing ISerializeByMemcpy
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray when reading</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
void SerializeValue<T>(ref NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged;
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Read or write a NativeList of struct values implementing ISerializeByMemcpy
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
void SerializeValue<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged;
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read or write a struct or class value implementing INetworkSerializable
|
/// Read or write a struct or class value implementing INetworkSerializable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -121,6 +140,27 @@ namespace Unity.Netcode
|
|||||||
void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
|
||||||
where T : unmanaged, INativeList<byte>, IUTF8Bytes;
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read or write NativeArray of FixedString values
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to read/write</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray when reading</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
void SerializeValue<T>(ref NativeArray<T> value, Allocator allocator)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes;
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Read or write a NativeList of FixedString values
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to read/write</param>
|
||||||
|
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
void SerializeValue<T>(ref NativeList<T> value)
|
||||||
|
where T : unmanaged, INativeList<byte>, IUTF8Bytes;
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read or write a Vector2 value
|
/// Read or write a Vector2 value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -344,6 +384,31 @@ namespace Unity.Netcode
|
|||||||
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||||
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
|
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialize a NativeArray of structs, "pre-checked", which skips buffer checks.
|
||||||
|
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||||
|
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||||
|
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="allocator">The allocator to use to construct the resulting NativeArray when reading</param>
|
||||||
|
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||||
|
void SerializeValuePreChecked<T>(ref NativeArray<T> value, Allocator allocator, FastBufferWriter.ForGeneric unused = default) where T : unmanaged;
|
||||||
|
|
||||||
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
||||||
|
/// <summary>
|
||||||
|
/// Serialize a NativeList of structs, "pre-checked", which skips buffer checks.
|
||||||
|
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||||
|
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||||
|
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type being serialized</typeparam>
|
||||||
|
/// <param name="value">The values to read/write</param>
|
||||||
|
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
|
||||||
|
void SerializeValuePreChecked<T>(ref NativeList<T> value, FastBufferWriter.ForGeneric unused = default) where T : unmanaged;
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serialize a FixedString, "pre-checked", which skips buffer checks.
|
/// Serialize a FixedString, "pre-checked", which skips buffer checks.
|
||||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||||
@@ -438,7 +503,7 @@ namespace Unity.Netcode
|
|||||||
void SerializeValuePreChecked(ref Vector4 value);
|
void SerializeValuePreChecked(ref Vector4 value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serialize a Vector4Array, "pre-checked", which skips buffer checks.
|
/// Serialize a Vector4 array, "pre-checked", which skips buffer checks.
|
||||||
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
|
||||||
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
|
||||||
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@@ -49,6 +50,8 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NetworkPrefabHandler
|
public class NetworkPrefabHandler
|
||||||
{
|
{
|
||||||
|
private NetworkManager m_NetworkManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Links a network prefab asset to a class with the INetworkPrefabInstanceHandler interface
|
/// Links a network prefab asset to a class with the INetworkPrefabInstanceHandler interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -60,6 +63,8 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Dictionary<uint, uint> m_PrefabInstanceToPrefabAsset = new Dictionary<uint, uint>();
|
private readonly Dictionary<uint, uint> m_PrefabInstanceToPrefabAsset = new Dictionary<uint, uint>();
|
||||||
|
|
||||||
|
internal static string PrefabDebugHelper(NetworkPrefab networkPrefab) => $"{nameof(NetworkPrefab)} \"{networkPrefab.Prefab.name}\"";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use a <see cref="GameObject"/> to register a class that implements the <see cref="INetworkPrefabInstanceHandler"/> interface with the <see cref="NetworkPrefabHandler"/>
|
/// Use a <see cref="GameObject"/> to register a class that implements the <see cref="INetworkPrefabInstanceHandler"/> interface with the <see cref="NetworkPrefabHandler"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -132,23 +137,23 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new System.Exception($"{targetNetworkObject.name} does not have a {nameof(NetworkObject)} component!");
|
throw new Exception($"{targetNetworkObject.name} does not have a {nameof(NetworkObject)} component!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new System.Exception($"{sourceNetworkPrefab.name} does not have a {nameof(NetworkObject)} component!");
|
throw new Exception($"{sourceNetworkPrefab.name} does not have a {nameof(NetworkObject)} component!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new System.Exception($"You should only call {nameof(RegisterHostGlobalObjectIdHashValues)} as a Host!");
|
throw new Exception($"You should only call {nameof(RegisterHostGlobalObjectIdHashValues)} as a Host!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new System.Exception($"You can only call {nameof(RegisterHostGlobalObjectIdHashValues)} once NetworkManager is listening!");
|
throw new Exception($"You can only call {nameof(RegisterHostGlobalObjectIdHashValues)} once NetworkManager is listening!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +195,7 @@ namespace Unity.Netcode
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_PrefabInstanceToPrefabAsset.Remove(networkPrefabHashKey);
|
m_PrefabInstanceToPrefabAsset.Remove(networkPrefabHashKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,30 +207,21 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="networkPrefab"></param>
|
/// <param name="networkPrefab"></param>
|
||||||
/// <returns>true or false</returns>
|
/// <returns>true or false</returns>
|
||||||
internal bool ContainsHandler(GameObject networkPrefab)
|
internal bool ContainsHandler(GameObject networkPrefab) => ContainsHandler(networkPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash);
|
||||||
{
|
|
||||||
return ContainsHandler(networkPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check to see if a <see cref="NetworkObject"/> is registered to an <see cref="INetworkPrefabInstanceHandler"/> implementation
|
/// Check to see if a <see cref="NetworkObject"/> is registered to an <see cref="INetworkPrefabInstanceHandler"/> implementation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="networkObject"></param>
|
/// <param name="networkObject"></param>
|
||||||
/// <returns>true or false</returns>
|
/// <returns>true or false</returns>
|
||||||
internal bool ContainsHandler(NetworkObject networkObject)
|
internal bool ContainsHandler(NetworkObject networkObject) => ContainsHandler(networkObject.GlobalObjectIdHash);
|
||||||
{
|
|
||||||
return ContainsHandler(networkObject.GlobalObjectIdHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check to see if a <see cref="NetworkObject.GlobalObjectIdHash"/> is registered to an <see cref="INetworkPrefabInstanceHandler"/> implementation
|
/// Check to see if a <see cref="NetworkObject.GlobalObjectIdHash"/> is registered to an <see cref="INetworkPrefabInstanceHandler"/> implementation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="networkPrefabHash"></param>
|
/// <param name="networkPrefabHash"></param>
|
||||||
/// <returns>true or false</returns>
|
/// <returns>true or false</returns>
|
||||||
internal bool ContainsHandler(uint networkPrefabHash)
|
internal bool ContainsHandler(uint networkPrefabHash) => m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabHash) || m_PrefabInstanceToPrefabAsset.ContainsKey(networkPrefabHash);
|
||||||
{
|
|
||||||
return m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabHash) || m_PrefabInstanceToPrefabAsset.ContainsKey(networkPrefabHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the source NetworkPrefab's <see cref="NetworkObject.GlobalObjectIdHash"/>
|
/// Returns the source NetworkPrefab's <see cref="NetworkObject.GlobalObjectIdHash"/>
|
||||||
@@ -237,9 +234,10 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return networkPrefabHash;
|
return networkPrefabHash;
|
||||||
}
|
}
|
||||||
else if (m_PrefabInstanceToPrefabAsset.ContainsKey(networkPrefabHash))
|
|
||||||
|
if (m_PrefabInstanceToPrefabAsset.TryGetValue(networkPrefabHash, out var hash))
|
||||||
{
|
{
|
||||||
return m_PrefabInstanceToPrefabAsset[networkPrefabHash];
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -256,9 +254,9 @@ namespace Unity.Netcode
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
internal NetworkObject HandleNetworkPrefabSpawn(uint networkPrefabAssetHash, ulong ownerClientId, Vector3 position, Quaternion rotation)
|
internal NetworkObject HandleNetworkPrefabSpawn(uint networkPrefabAssetHash, ulong ownerClientId, Vector3 position, Quaternion rotation)
|
||||||
{
|
{
|
||||||
if (m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabAssetHash))
|
if (m_PrefabAssetToPrefabHandler.TryGetValue(networkPrefabAssetHash, out var prefabInstanceHandler))
|
||||||
{
|
{
|
||||||
var networkObjectInstance = m_PrefabAssetToPrefabHandler[networkPrefabAssetHash].Instantiate(ownerClientId, position, rotation);
|
var networkObjectInstance = prefabInstanceHandler.Instantiate(ownerClientId, position, rotation);
|
||||||
|
|
||||||
//Now we must make sure this alternate PrefabAsset spawned in place of the prefab asset with the networkPrefabAssetHash (GlobalObjectIdHash)
|
//Now we must make sure this alternate PrefabAsset spawned in place of the prefab asset with the networkPrefabAssetHash (GlobalObjectIdHash)
|
||||||
//is registered and linked to the networkPrefabAssetHash so during the HandleNetworkPrefabDestroy process we can identify the alternate prefab asset.
|
//is registered and linked to the networkPrefabAssetHash so during the HandleNetworkPrefabDestroy process we can identify the alternate prefab asset.
|
||||||
@@ -282,19 +280,146 @@ namespace Unity.Netcode
|
|||||||
var networkObjectInstanceHash = networkObjectInstance.GlobalObjectIdHash;
|
var networkObjectInstanceHash = networkObjectInstance.GlobalObjectIdHash;
|
||||||
|
|
||||||
// Do we have custom overrides registered?
|
// Do we have custom overrides registered?
|
||||||
if (m_PrefabInstanceToPrefabAsset.ContainsKey(networkObjectInstanceHash))
|
if (m_PrefabInstanceToPrefabAsset.TryGetValue(networkObjectInstanceHash, out var networkPrefabAssetHash))
|
||||||
{
|
{
|
||||||
var networkPrefabAssetHash = m_PrefabInstanceToPrefabAsset[networkObjectInstanceHash];
|
if (m_PrefabAssetToPrefabHandler.TryGetValue(networkPrefabAssetHash, out var prefabInstanceHandler))
|
||||||
if (m_PrefabAssetToPrefabHandler.ContainsKey(networkPrefabAssetHash))
|
|
||||||
{
|
{
|
||||||
m_PrefabAssetToPrefabHandler[networkPrefabAssetHash].Destroy(networkObjectInstance);
|
prefabInstanceHandler.Destroy(networkObjectInstance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Otherwise the NetworkObject is the source NetworkPrefab
|
else // Otherwise the NetworkObject is the source NetworkPrefab
|
||||||
if (m_PrefabAssetToPrefabHandler.ContainsKey(networkObjectInstanceHash))
|
if (m_PrefabAssetToPrefabHandler.TryGetValue(networkObjectInstanceHash, out var prefabInstanceHandler))
|
||||||
{
|
{
|
||||||
m_PrefabAssetToPrefabHandler[networkObjectInstanceHash].Destroy(networkObjectInstance);
|
prefabInstanceHandler.Destroy(networkObjectInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the <see cref="GameObject"/> to use as the override as could be defined within the NetworkPrefab list
|
||||||
|
/// Note: This should be used to create <see cref="GameObject"/> pools (with <see cref="NetworkObject"/> components)
|
||||||
|
/// under the scenario where you are using the Host model as it spawns everything locally. As such, the override
|
||||||
|
/// will not be applied when spawning locally on a Host.
|
||||||
|
/// Related Classes and Interfaces:
|
||||||
|
/// <see cref="INetworkPrefabInstanceHandler"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameObject">the <see cref="GameObject"/> to be checked for a <see cref="NetworkManager"/> defined NetworkPrefab override</param>
|
||||||
|
/// <returns>a <see cref="GameObject"/> that is either the override or if no overrides exist it returns the same as the one passed in as a parameter</returns>
|
||||||
|
public GameObject GetNetworkPrefabOverride(GameObject gameObject)
|
||||||
|
{
|
||||||
|
if (gameObject.TryGetComponent<NetworkObject>(out var networkObject))
|
||||||
|
{
|
||||||
|
if (m_NetworkManager.NetworkConfig.Prefabs.NetworkPrefabOverrideLinks.ContainsKey(networkObject.GlobalObjectIdHash))
|
||||||
|
{
|
||||||
|
switch (m_NetworkManager.NetworkConfig.Prefabs.NetworkPrefabOverrideLinks[networkObject.GlobalObjectIdHash].Override)
|
||||||
|
{
|
||||||
|
case NetworkPrefabOverride.Hash:
|
||||||
|
case NetworkPrefabOverride.Prefab:
|
||||||
|
{
|
||||||
|
return m_NetworkManager.NetworkConfig.Prefabs.NetworkPrefabOverrideLinks[networkObject.GlobalObjectIdHash].OverridingTargetPrefab;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new prefab to the network prefab list.
|
||||||
|
/// This can be any GameObject with a NetworkObject component, from any source (addressables, asset
|
||||||
|
/// bundles, Resource.Load, dynamically created, etc)
|
||||||
|
///
|
||||||
|
/// There are three limitations to this method:
|
||||||
|
/// - If you have NetworkConfig.ForceSamePrefabs enabled, you can only do this before starting
|
||||||
|
/// networking, and the server and all connected clients must all have the same exact set of prefabs
|
||||||
|
/// added via this method before connecting
|
||||||
|
/// - Adding a prefab on the server does not automatically add it on the client - it's up to you
|
||||||
|
/// to make sure the client and server are synchronized via whatever method makes sense for your game
|
||||||
|
/// (RPCs, configurations, deterministic loading, etc)
|
||||||
|
/// - If the server sends a Spawn message to a client that has not yet added a prefab for, the spawn message
|
||||||
|
/// and any other relevant messages will be held for a configurable time (default 1 second, configured via
|
||||||
|
/// NetworkConfig.SpawnTimeout) before an error is logged. This is intended to enable the SDK to gracefully
|
||||||
|
/// handle unexpected conditions (slow disks, slow network, etc) that slow down asset loading. This timeout
|
||||||
|
/// should not be relied on and code shouldn't be written around it - your code should be written so that
|
||||||
|
/// the asset is expected to be loaded before it's needed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefab"></param>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public void AddNetworkPrefab(GameObject prefab)
|
||||||
|
{
|
||||||
|
if (m_NetworkManager.IsListening && m_NetworkManager.NetworkConfig.ForceSamePrefabs)
|
||||||
|
{
|
||||||
|
throw new Exception($"All prefabs must be registered before starting {nameof(NetworkManager)} when {nameof(NetworkConfig.ForceSamePrefabs)} is enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var networkObject = prefab.GetComponent<NetworkObject>();
|
||||||
|
if (!networkObject)
|
||||||
|
{
|
||||||
|
throw new Exception($"All {nameof(NetworkPrefab)}s must contain a {nameof(NetworkObject)} component.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var networkPrefab = new NetworkPrefab { Prefab = prefab };
|
||||||
|
bool added = m_NetworkManager.NetworkConfig.Prefabs.Add(networkPrefab);
|
||||||
|
if (m_NetworkManager.IsListening && added)
|
||||||
|
{
|
||||||
|
m_NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnAddPrefab, networkObject.GlobalObjectIdHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove a prefab from the prefab list.
|
||||||
|
/// As with AddNetworkPrefab, this is specific to the client it's called on -
|
||||||
|
/// calling it on the server does not automatically remove anything on any of the
|
||||||
|
/// client processes.
|
||||||
|
///
|
||||||
|
/// Like AddNetworkPrefab, when NetworkConfig.ForceSamePrefabs is enabled,
|
||||||
|
/// this cannot be called after connecting.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="prefab"></param>
|
||||||
|
public void RemoveNetworkPrefab(GameObject prefab)
|
||||||
|
{
|
||||||
|
if (m_NetworkManager.IsListening && m_NetworkManager.NetworkConfig.ForceSamePrefabs)
|
||||||
|
{
|
||||||
|
throw new Exception($"Prefabs cannot be removed after starting {nameof(NetworkManager)} when {nameof(NetworkConfig.ForceSamePrefabs)} is enabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalObjectIdHash = prefab.GetComponent<NetworkObject>().GlobalObjectIdHash;
|
||||||
|
m_NetworkManager.NetworkConfig.Prefabs.Remove(prefab);
|
||||||
|
if (ContainsHandler(globalObjectIdHash))
|
||||||
|
{
|
||||||
|
RemoveHandler(globalObjectIdHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If one exists, registers the player prefab
|
||||||
|
/// </summary>
|
||||||
|
internal void RegisterPlayerPrefab()
|
||||||
|
{
|
||||||
|
var networkConfig = m_NetworkManager.NetworkConfig;
|
||||||
|
// If we have a player prefab, then we need to verify it is in the list of NetworkPrefabOverrideLinks for client side spawning.
|
||||||
|
if (networkConfig.PlayerPrefab != null)
|
||||||
|
{
|
||||||
|
if (networkConfig.PlayerPrefab.TryGetComponent<NetworkObject>(out var playerPrefabNetworkObject))
|
||||||
|
{
|
||||||
|
//In the event there is no NetworkPrefab entry (i.e. no override for default player prefab)
|
||||||
|
if (!networkConfig.Prefabs.NetworkPrefabOverrideLinks.ContainsKey(playerPrefabNetworkObject.GlobalObjectIdHash))
|
||||||
|
{
|
||||||
|
//Then add a new entry for the player prefab
|
||||||
|
AddNetworkPrefab(networkConfig.PlayerPrefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Provide the name of the prefab with issues so the user can more easily find the prefab and fix it
|
||||||
|
Debug.LogError($"{nameof(NetworkConfig.PlayerPrefab)} (\"{networkConfig.PlayerPrefab.name}\") has no NetworkObject assigned to it!.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Initialize(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
m_NetworkManager = networkManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NetworkSpawnManager
|
public class NetworkSpawnManager
|
||||||
{
|
{
|
||||||
|
// Stores the objects that need to be shown at end-of-frame
|
||||||
|
internal Dictionary<ulong, List<NetworkObject>> ObjectsToShowToClient = new Dictionary<ulong, List<NetworkObject>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently spawned objects
|
/// The currently spawned objects
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -37,6 +40,41 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<ulong, ulong> m_ObjectToOwnershipTable = new Dictionary<ulong, ulong>();
|
private Dictionary<ulong, ulong> m_ObjectToOwnershipTable = new Dictionary<ulong, ulong>();
|
||||||
|
|
||||||
|
internal void MarkObjectForShowingTo(NetworkObject networkObject, ulong clientId)
|
||||||
|
{
|
||||||
|
if (!ObjectsToShowToClient.ContainsKey(clientId))
|
||||||
|
{
|
||||||
|
ObjectsToShowToClient.Add(clientId, new List<NetworkObject>());
|
||||||
|
}
|
||||||
|
ObjectsToShowToClient[clientId].Add(networkObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns whether any matching objects would have become visible and were returned to hidden state
|
||||||
|
internal bool RemoveObjectFromShowingTo(NetworkObject networkObject, ulong clientId)
|
||||||
|
{
|
||||||
|
var ret = false;
|
||||||
|
if (!ObjectsToShowToClient.ContainsKey(clientId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// probably overkill, but deals with multiple entries
|
||||||
|
while (ObjectsToShowToClient[clientId].Contains(networkObject))
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Object was shown and hidden from the same client in the same Network frame. As a result, the client will _not_ receive a NetworkSpawn");
|
||||||
|
ObjectsToShowToClient[clientId].Remove(networkObject);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
networkObject.Observers.Remove(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to update a NetworkObject's ownership
|
/// Used to update a NetworkObject's ownership
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -75,12 +113,6 @@ namespace Unity.Netcode
|
|||||||
// Remove the previous owner's entry
|
// Remove the previous owner's entry
|
||||||
OwnershipToObjectsTable[previousOwner].Remove(networkObject.NetworkObjectId);
|
OwnershipToObjectsTable[previousOwner].Remove(networkObject.NetworkObjectId);
|
||||||
|
|
||||||
// Server or Host alway invokes the lost ownership notification locally
|
|
||||||
if (NetworkManager.IsServer)
|
|
||||||
{
|
|
||||||
networkObject.InvokeBehaviourOnLostOwnership();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are removing the entry (i.e. despawning or client lost ownership)
|
// If we are removing the entry (i.e. despawning or client lost ownership)
|
||||||
if (isRemoving)
|
if (isRemoving)
|
||||||
{
|
{
|
||||||
@@ -105,12 +137,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
// Add the new ownership entry
|
// Add the new ownership entry
|
||||||
OwnershipToObjectsTable[newOwner].Add(networkObject.NetworkObjectId, networkObject);
|
OwnershipToObjectsTable[newOwner].Add(networkObject.NetworkObjectId, networkObject);
|
||||||
|
|
||||||
// Server or Host always invokes the gained ownership notification locally
|
|
||||||
if (NetworkManager.IsServer)
|
|
||||||
{
|
|
||||||
networkObject.InvokeBehaviourOnGainedOwnership();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (isRemoving)
|
else if (isRemoving)
|
||||||
{
|
{
|
||||||
@@ -141,11 +167,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public NetworkManager NetworkManager { get; }
|
public NetworkManager NetworkManager { get; }
|
||||||
|
|
||||||
internal NetworkSpawnManager(NetworkManager networkManager)
|
|
||||||
{
|
|
||||||
NetworkManager = networkManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal readonly Queue<ReleasedNetworkId> ReleasedNetworkObjectIds = new Queue<ReleasedNetworkId>();
|
internal readonly Queue<ReleasedNetworkId> ReleasedNetworkObjectIds = new Queue<ReleasedNetworkId>();
|
||||||
private ulong m_NetworkObjectIdCounter;
|
private ulong m_NetworkObjectIdCounter;
|
||||||
|
|
||||||
@@ -194,43 +215,6 @@ namespace Unity.Netcode
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveOwnership(NetworkObject networkObject)
|
|
||||||
{
|
|
||||||
if (!NetworkManager.IsServer)
|
|
||||||
{
|
|
||||||
throw new NotServerException("Only the server can change ownership");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!networkObject.IsSpawned)
|
|
||||||
{
|
|
||||||
throw new SpawnStateException("Object is not spawned");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we made it here then we are the server and if the server is determined to already be the owner
|
|
||||||
// then ignore the RemoveOwnership invocation.
|
|
||||||
if (networkObject.OwnerClientId == NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.OwnerClientId = NetworkManager.ServerClientId;
|
|
||||||
|
|
||||||
// Server removes the entry and takes over ownership before notifying
|
|
||||||
UpdateOwnershipTable(networkObject, NetworkManager.ServerClientId, true);
|
|
||||||
|
|
||||||
var message = new ChangeOwnershipMessage
|
|
||||||
{
|
|
||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
|
||||||
OwnerClientId = networkObject.OwnerClientId
|
|
||||||
};
|
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
|
||||||
|
|
||||||
foreach (var client in NetworkManager.ConnectedClients)
|
|
||||||
{
|
|
||||||
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper function to get a network client for a clientId from the NetworkManager.
|
/// Helper function to get a network client for a clientId from the NetworkManager.
|
||||||
/// On the server this will check the <see cref="NetworkManager.ConnectedClients"/> list.
|
/// On the server this will check the <see cref="NetworkManager.ConnectedClients"/> list.
|
||||||
@@ -256,6 +240,11 @@ namespace Unity.Netcode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void RemoveOwnership(NetworkObject networkObject)
|
||||||
|
{
|
||||||
|
ChangeOwnership(networkObject, NetworkManager.ServerClientId);
|
||||||
|
}
|
||||||
|
|
||||||
internal void ChangeOwnership(NetworkObject networkObject, ulong clientId)
|
internal void ChangeOwnership(NetworkObject networkObject, ulong clientId)
|
||||||
{
|
{
|
||||||
if (!NetworkManager.IsServer)
|
if (!NetworkManager.IsServer)
|
||||||
@@ -268,14 +257,22 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is not spawned");
|
throw new SpawnStateException("Object is not spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var previous = networkObject.OwnerClientId;
|
||||||
|
// Assign the new owner
|
||||||
networkObject.OwnerClientId = clientId;
|
networkObject.OwnerClientId = clientId;
|
||||||
|
|
||||||
|
// Always notify locally on the server when ownership is lost
|
||||||
|
networkObject.InvokeBehaviourOnLostOwnership();
|
||||||
|
|
||||||
networkObject.MarkVariablesDirty(true);
|
networkObject.MarkVariablesDirty(true);
|
||||||
NetworkManager.BehaviourUpdater.AddForUpdate(networkObject);
|
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);
|
||||||
|
|
||||||
|
// Always notify locally on the server when a new owner is assigned
|
||||||
|
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||||
|
|
||||||
var message = new ChangeOwnershipMessage
|
var message = new ChangeOwnershipMessage
|
||||||
{
|
{
|
||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
NetworkObjectId = networkObject.NetworkObjectId,
|
||||||
@@ -286,10 +283,16 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (networkObject.IsNetworkVisibleTo(client.Value.ClientId))
|
if (networkObject.IsNetworkVisibleTo(client.Value.ClientId))
|
||||||
{
|
{
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, client.Value.ClientId);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, client.Value.ClientId);
|
||||||
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After we have sent the change ownership message to all client observers, invoke the ownership changed notification.
|
||||||
|
/// !!Important!!
|
||||||
|
/// This gets called specifically *after* sending the ownership message so any additional messages that need to proceed an ownership
|
||||||
|
/// change can be sent from NetworkBehaviours that override the <see cref="NetworkBehaviour.OnOwnershipChanged"></see>
|
||||||
|
networkObject.InvokeOwnershipChanged(previous, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool HasPrefab(NetworkObject.SceneObject sceneObject)
|
internal bool HasPrefab(NetworkObject.SceneObject sceneObject)
|
||||||
@@ -584,8 +587,10 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager.IsServer)
|
// If we are the server and should spawn with observers
|
||||||
|
if (NetworkManager.IsServer && networkObject.SpawnWithObservers)
|
||||||
{
|
{
|
||||||
|
// Add client observers
|
||||||
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
|
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
|
||||||
{
|
{
|
||||||
if (networkObject.CheckObjectVisibility == null || networkObject.CheckObjectVisibility(NetworkManager.ConnectedClientsList[i].ClientId))
|
if (networkObject.CheckObjectVisibility == null || networkObject.CheckObjectVisibility(NetworkManager.ConnectedClientsList[i].ClientId))
|
||||||
@@ -600,7 +605,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
networkObject.InvokeBehaviourNetworkSpawn();
|
networkObject.InvokeBehaviourNetworkSpawn();
|
||||||
|
|
||||||
NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredMessageManager.TriggerType.OnSpawn, networkId);
|
NetworkManager.DeferredMessageManager.ProcessTriggers(IDeferredNetworkMessageManager.TriggerType.OnSpawn, networkId);
|
||||||
|
|
||||||
// propagate the IsSceneObject setting to child NetworkObjects
|
// propagate the IsSceneObject setting to child NetworkObjects
|
||||||
var children = networkObject.GetComponentsInChildren<NetworkObject>();
|
var children = networkObject.GetComponentsInChildren<NetworkObject>();
|
||||||
@@ -633,7 +638,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ObjectInfo = networkObject.GetMessageSceneObject(clientId)
|
ObjectInfo = networkObject.GetMessageSceneObject(clientId)
|
||||||
};
|
};
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
||||||
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,7 +888,7 @@ namespace Unity.Netcode
|
|||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
NetworkObjectId = networkObject.NetworkObjectId,
|
||||||
DestroyGameObject = networkObject.IsSceneObject != false ? destroyGameObject : true
|
DestroyGameObject = networkObject.IsSceneObject != false ? destroyGameObject : true
|
||||||
};
|
};
|
||||||
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, m_TargetClientIds);
|
var size = NetworkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, m_TargetClientIds);
|
||||||
foreach (var targetClientId in m_TargetClientIds)
|
foreach (var targetClientId in m_TargetClientIds)
|
||||||
{
|
{
|
||||||
NetworkManager.NetworkMetrics.TrackObjectDestroySent(targetClientId, networkObject, size);
|
NetworkManager.NetworkMetrics.TrackObjectDestroySent(targetClientId, networkObject, size);
|
||||||
@@ -917,32 +922,62 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates all spawned <see cref="NetworkObject.Observers"/> for the specified client
|
/// Updates all spawned <see cref="NetworkObject.Observers"/> for the specified newly connected client
|
||||||
/// Note: if the clientId is the server then it is observable to all spawned <see cref="NetworkObject"/>'s
|
/// Note: if the clientId is the server then it is observable to all spawned <see cref="NetworkObject"/>'s
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method is to only to be used for newly connected clients in order to update the observers list for
|
||||||
|
/// each NetworkObject instance.
|
||||||
|
/// </remarks>
|
||||||
internal void UpdateObservedNetworkObjects(ulong clientId)
|
internal void UpdateObservedNetworkObjects(ulong clientId)
|
||||||
{
|
{
|
||||||
foreach (var sobj in SpawnedObjectsList)
|
foreach (var sobj in SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
|
// If the NetworkObject has no visibility check then prepare to add this client as an observer
|
||||||
if (sobj.CheckObjectVisibility == null)
|
if (sobj.CheckObjectVisibility == null)
|
||||||
{
|
{
|
||||||
if (!sobj.Observers.Contains(clientId))
|
// If the client is not part of the observers and spawn with observers is enabled on this instance or the clientId is the server
|
||||||
|
if (!sobj.Observers.Contains(clientId) && (sobj.SpawnWithObservers || clientId == NetworkManager.ServerClientId))
|
||||||
{
|
{
|
||||||
sobj.Observers.Add(clientId);
|
sobj.Observers.Add(clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// CheckObject visibility overrides SpawnWithObservers under this condition
|
||||||
if (sobj.CheckObjectVisibility(clientId))
|
if (sobj.CheckObjectVisibility(clientId))
|
||||||
{
|
{
|
||||||
sobj.Observers.Add(clientId);
|
sobj.Observers.Add(clientId);
|
||||||
}
|
}
|
||||||
else if (sobj.Observers.Contains(clientId))
|
else // Otherwise, if the observers contains the clientId (shouldn't happen) then remove it since CheckObjectVisibility returned false
|
||||||
|
if (sobj.Observers.Contains(clientId))
|
||||||
{
|
{
|
||||||
sobj.Observers.Remove(clientId);
|
sobj.Observers.Remove(clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <see cref="NetworkBehaviourUpdater.NetworkBehaviourUpdater_Tick"/>
|
||||||
|
/// </summary>
|
||||||
|
internal void HandleNetworkObjectShow()
|
||||||
|
{
|
||||||
|
// Handle NetworkObjects to show
|
||||||
|
foreach (var client in ObjectsToShowToClient)
|
||||||
|
{
|
||||||
|
ulong clientId = client.Key;
|
||||||
|
foreach (var networkObject in client.Value)
|
||||||
|
{
|
||||||
|
SendSpawnCallForObject(clientId, networkObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectsToShowToClient.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal NetworkSpawnManager(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
NetworkManager = networkManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Unity.Profiling;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
@@ -8,6 +9,31 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NetworkTimeSystem
|
public class NetworkTimeSystem
|
||||||
{
|
{
|
||||||
|
/// <remarks>
|
||||||
|
/// This was the original comment when it lived in NetworkManager:
|
||||||
|
/// todo talk with UX/Product, find good default value for this
|
||||||
|
/// </remarks>
|
||||||
|
private const float k_DefaultBufferSizeSec = 0.05f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time synchronization frequency defaults to 1 synchronization message per second
|
||||||
|
/// </summary>
|
||||||
|
private const double k_TimeSyncFrequency = 1.0d;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The threshold, in seconds, used to force a hard catchup of network time
|
||||||
|
/// </summary>
|
||||||
|
private const double k_HardResetThresholdSeconds = 0.2d;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default adjustment ratio
|
||||||
|
/// </summary>
|
||||||
|
private const double k_DefaultAdjustmentRatio = 0.01d;
|
||||||
|
|
||||||
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
|
private static ProfilerMarker s_SyncTime = new ProfilerMarker($"{nameof(NetworkManager)}.SyncTime");
|
||||||
|
#endif
|
||||||
|
|
||||||
private double m_TimeSec;
|
private double m_TimeSec;
|
||||||
private double m_CurrentLocalTimeOffset;
|
private double m_CurrentLocalTimeOffset;
|
||||||
private double m_DesiredLocalTimeOffset;
|
private double m_DesiredLocalTimeOffset;
|
||||||
@@ -50,6 +76,16 @@ namespace Unity.Netcode
|
|||||||
internal double LastSyncedServerTimeSec { get; private set; }
|
internal double LastSyncedServerTimeSec { get; private set; }
|
||||||
internal double LastSyncedRttSec { get; private set; }
|
internal double LastSyncedRttSec { get; private set; }
|
||||||
|
|
||||||
|
private NetworkConnectionManager m_ConnectionManager;
|
||||||
|
private NetworkTransport m_NetworkTransport;
|
||||||
|
private NetworkTickSystem m_NetworkTickSystem;
|
||||||
|
private NetworkManager m_NetworkManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="k_TimeSyncFrequency"/>
|
||||||
|
/// </summary>
|
||||||
|
private int m_TimeSyncFrequencyTicks;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The constructor class for <see cref="NetworkTickSystem"/>
|
/// The constructor class for <see cref="NetworkTickSystem"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -57,7 +93,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="serverBufferSec">The amount of the time in seconds the client should buffer incoming messages from the server.</param>
|
/// <param name="serverBufferSec">The amount of the time in seconds the client should buffer incoming messages from the server.</param>
|
||||||
/// <param name="hardResetThresholdSec">The threshold, in seconds, used to force a hard catchup of network time.</param>
|
/// <param name="hardResetThresholdSec">The threshold, in seconds, used to force a hard catchup of network time.</param>
|
||||||
/// <param name="adjustmentRatio">The ratio at which the NetworkTimeSystem speeds up or slows down time.</param>
|
/// <param name="adjustmentRatio">The ratio at which the NetworkTimeSystem speeds up or slows down time.</param>
|
||||||
public NetworkTimeSystem(double localBufferSec, double serverBufferSec, double hardResetThresholdSec, double adjustmentRatio = 0.01d)
|
public NetworkTimeSystem(double localBufferSec, double serverBufferSec = k_DefaultBufferSizeSec, double hardResetThresholdSec = k_HardResetThresholdSeconds, double adjustmentRatio = k_DefaultAdjustmentRatio)
|
||||||
{
|
{
|
||||||
LocalBufferSec = localBufferSec;
|
LocalBufferSec = localBufferSec;
|
||||||
ServerBufferSec = serverBufferSec;
|
ServerBufferSec = serverBufferSec;
|
||||||
@@ -65,6 +101,89 @@ namespace Unity.Netcode
|
|||||||
AdjustmentRatio = adjustmentRatio;
|
AdjustmentRatio = adjustmentRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The primary time system is initialized when a server-host or client is started
|
||||||
|
/// </summary>
|
||||||
|
internal NetworkTickSystem Initialize(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
m_NetworkManager = networkManager;
|
||||||
|
m_ConnectionManager = networkManager.ConnectionManager;
|
||||||
|
m_NetworkTransport = networkManager.NetworkConfig.NetworkTransport;
|
||||||
|
m_TimeSyncFrequencyTicks = (int)(k_TimeSyncFrequency * networkManager.NetworkConfig.TickRate);
|
||||||
|
m_NetworkTickSystem = new NetworkTickSystem(networkManager.NetworkConfig.TickRate, 0, 0);
|
||||||
|
// Only the server side needs to register for tick based time synchronization
|
||||||
|
if (m_ConnectionManager.LocalClient.IsServer)
|
||||||
|
{
|
||||||
|
m_NetworkTickSystem.Tick += OnTickSyncTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_NetworkTickSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void UpdateTime()
|
||||||
|
{
|
||||||
|
// As a client wait to run the time system until we are connected.
|
||||||
|
// As a client or server don't worry about the time system if we are no longer processing messages
|
||||||
|
if (!m_ConnectionManager.LocalClient.IsServer && !m_ConnectionManager.LocalClient.IsConnected)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only update RTT here, server time is updated by time sync messages
|
||||||
|
var reset = Advance(m_NetworkManager.RealTimeProvider.UnscaledDeltaTime);
|
||||||
|
if (reset)
|
||||||
|
{
|
||||||
|
m_NetworkTickSystem.Reset(LocalTime, ServerTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_NetworkTickSystem.UpdateTick(LocalTime, ServerTime);
|
||||||
|
|
||||||
|
if (!m_ConnectionManager.LocalClient.IsServer)
|
||||||
|
{
|
||||||
|
Sync(LastSyncedServerTimeSec + m_NetworkManager.RealTimeProvider.UnscaledDeltaTime, m_NetworkTransport.GetCurrentRtt(NetworkManager.ServerClientId) / 1000d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server-Side:
|
||||||
|
/// Synchronizes time with clients based on the given <see cref="m_TimeSyncFrequencyTicks"/>.
|
||||||
|
/// Also: <see cref="k_TimeSyncFrequency"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The default is to send 1 time synchronization message per second
|
||||||
|
/// </remarks>
|
||||||
|
private void OnTickSyncTime()
|
||||||
|
{
|
||||||
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
|
s_SyncTime.Begin();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check if we need to send a time synchronization message, and if so send it
|
||||||
|
if (m_ConnectionManager.LocalClient.IsServer && m_NetworkTickSystem.ServerTime.Tick % m_TimeSyncFrequencyTicks == 0)
|
||||||
|
{
|
||||||
|
var message = new TimeSyncMessage
|
||||||
|
{
|
||||||
|
Tick = m_NetworkTickSystem.ServerTime.Tick
|
||||||
|
};
|
||||||
|
m_ConnectionManager.SendMessage(ref message, NetworkDelivery.Unreliable, m_ConnectionManager.ConnectedClientIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
|
s_SyncTime.End();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoke when shutting down the NetworkManager
|
||||||
|
/// </summary>
|
||||||
|
internal void Shutdown()
|
||||||
|
{
|
||||||
|
if (m_ConnectionManager.LocalClient.IsServer)
|
||||||
|
{
|
||||||
|
m_NetworkTickSystem.Tick -= OnTickSyncTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="NetworkTimeSystem"/> class for a server instance.
|
/// Creates a new instance of the <see cref="NetworkTimeSystem"/> class for a server instance.
|
||||||
/// The server will not apply any buffer values which ensures that local time equals server time.
|
/// The server will not apply any buffer values which ensures that local time equals server time.
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ namespace Unity.Netcode.Transports.UNET
|
|||||||
public override bool StartClient()
|
public override bool StartClient()
|
||||||
{
|
{
|
||||||
m_ServerHostId = UnityEngine.Networking.NetworkTransport.AddHost(new HostTopology(GetConfig(), 1), 0, null);
|
m_ServerHostId = UnityEngine.Networking.NetworkTransport.AddHost(new HostTopology(GetConfig(), 1), 0, null);
|
||||||
m_ServerConnectionId = UnityEngine.Networking.NetworkTransport.Connect(m_ServerHostId, ConnectAddress, ConnectPort, 0, out byte error);
|
m_ServerConnectionId = UnityEngine.Networking.NetworkTransport.Connect(m_ServerHostId, ConnectAddress, GetConnectPort(), 0, out byte error);
|
||||||
return (NetworkError)error == NetworkError.Ok;
|
return (NetworkError)error == NetworkError.Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ namespace Unity.Netcode.Transports.UNET
|
|||||||
{
|
{
|
||||||
var topology = new HostTopology(GetConfig(), MaxConnections);
|
var topology = new HostTopology(GetConfig(), MaxConnections);
|
||||||
// Undocumented, but AddHost returns -1 in case of any type of failure. See UNET::NetLibraryManager::AddHost
|
// Undocumented, but AddHost returns -1 in case of any type of failure. See UNET::NetLibraryManager::AddHost
|
||||||
return -1 != UnityEngine.Networking.NetworkTransport.AddHost(topology, ServerListenPort, null);
|
return -1 != UnityEngine.Networking.NetworkTransport.AddHost(topology, GetServerListenPort(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DisconnectRemoteClient(ulong clientId)
|
public override void DisconnectRemoteClient(ulong clientId)
|
||||||
@@ -281,6 +281,26 @@ namespace Unity.Netcode.Transports.UNET
|
|||||||
|
|
||||||
return connectionConfig;
|
return connectionConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GetConnectPort()
|
||||||
|
{
|
||||||
|
if (NetworkManager && NetworkManager.PortOverride.Overidden)
|
||||||
|
{
|
||||||
|
return NetworkManager.PortOverride.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnectPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetServerListenPort()
|
||||||
|
{
|
||||||
|
if (NetworkManager && NetworkManager.PortOverride.Overidden)
|
||||||
|
{
|
||||||
|
return NetworkManager.PortOverride.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerListenPort;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
|||||||
@@ -252,15 +252,17 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
/// this could lead to reading messages from a corrupted queue.
|
/// this could lead to reading messages from a corrupted queue.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="writer">The <see cref="DataStreamWriter"/> to write to.</param>
|
/// <param name="writer">The <see cref="DataStreamWriter"/> to write to.</param>
|
||||||
|
/// <param name="maxBytes">Max number of bytes to copy (0 means writer capacity).</param>
|
||||||
/// <returns>How many bytes were written to the writer.</returns>
|
/// <returns>How many bytes were written to the writer.</returns>
|
||||||
public int FillWriterWithBytes(ref DataStreamWriter writer)
|
public int FillWriterWithBytes(ref DataStreamWriter writer, int maxBytes = 0)
|
||||||
{
|
{
|
||||||
if (!IsCreated || Length == 0)
|
if (!IsCreated || Length == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var copyLength = Math.Min(writer.Capacity, Length);
|
var maxLength = maxBytes == 0 ? writer.Capacity : Math.Min(maxBytes, writer.Capacity);
|
||||||
|
var copyLength = Math.Min(maxLength, Length);
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -402,6 +402,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
/// - packet jitter (variances in latency, see: https://en.wikipedia.org/wiki/Jitter)
|
/// - packet jitter (variances in latency, see: https://en.wikipedia.org/wiki/Jitter)
|
||||||
/// - packet drop rate (packet loss)
|
/// - packet drop rate (packet loss)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
#if UTP_TRANSPORT_2_0_ABOVE
|
#if UTP_TRANSPORT_2_0_ABOVE
|
||||||
[Obsolete("DebugSimulator is no longer supported and has no effect. Use Network Simulator from the Multiplayer Tools package.", false)]
|
[Obsolete("DebugSimulator is no longer supported and has no effect. Use Network Simulator from the Multiplayer Tools package.", false)]
|
||||||
#endif
|
#endif
|
||||||
@@ -524,7 +525,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
//reflection, but this does not live in the context of a performance-critical loop, it runs once at initial connection time.
|
//reflection, but this does not live in the context of a performance-critical loop, it runs once at initial connection time.
|
||||||
if (m_RelayServerData.Equals(default(RelayServerData)))
|
if (m_RelayServerData.Equals(default(RelayServerData)))
|
||||||
{
|
{
|
||||||
Debug.LogError("You must call SetRelayServerData() at least once before calling StartRelayServer.");
|
Debug.LogError("You must call SetRelayServerData() at least once before calling StartClient.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,6 +537,13 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
serverEndpoint = ConnectionData.ServerEndPoint;
|
serverEndpoint = ConnectionData.ServerEndPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify the endpoint is valid before proceeding
|
||||||
|
if (serverEndpoint.Family == NetworkFamily.Invalid)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Target server network address ({ConnectionData.Address}) is {nameof(NetworkFamily.Invalid)}!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
InitDriver();
|
InitDriver();
|
||||||
|
|
||||||
var bindEndpoint = serverEndpoint.Family == NetworkFamily.Ipv6 ? NetworkEndpoint.AnyIpv6 : NetworkEndpoint.AnyIpv4;
|
var bindEndpoint = serverEndpoint.Family == NetworkFamily.Ipv6 ? NetworkEndpoint.AnyIpv6 : NetworkEndpoint.AnyIpv4;
|
||||||
@@ -554,6 +562,13 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
|
|
||||||
private bool ServerBindAndListen(NetworkEndpoint endPoint)
|
private bool ServerBindAndListen(NetworkEndpoint endPoint)
|
||||||
{
|
{
|
||||||
|
// Verify the endpoint is valid before proceeding
|
||||||
|
if (endPoint.Family == NetworkFamily.Invalid)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Network listen address ({ConnectionData.Address}) is {nameof(NetworkFamily.Invalid)}!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
InitDriver();
|
InitDriver();
|
||||||
|
|
||||||
int result = m_Driver.Bind(endPoint);
|
int result = m_Driver.Bind(endPoint);
|
||||||
@@ -671,9 +686,11 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
/// <param name="packetDelay">Packet delay in milliseconds.</param>
|
/// <param name="packetDelay">Packet delay in milliseconds.</param>
|
||||||
/// <param name="packetJitter">Packet jitter in milliseconds.</param>
|
/// <param name="packetJitter">Packet jitter in milliseconds.</param>
|
||||||
/// <param name="dropRate">Packet drop percentage.</param>
|
/// <param name="dropRate">Packet drop percentage.</param>
|
||||||
|
|
||||||
#if UTP_TRANSPORT_2_0_ABOVE
|
#if UTP_TRANSPORT_2_0_ABOVE
|
||||||
[Obsolete("SetDebugSimulatorParameters is no longer supported and has no effect. Use Network Simulator from the Multiplayer Tools package.", false)]
|
[Obsolete("SetDebugSimulatorParameters is no longer supported and has no effect. Use Network Simulator from the Multiplayer Tools package.", false)]
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public void SetDebugSimulatorParameters(int packetDelay, int packetJitter, int dropRate)
|
public void SetDebugSimulatorParameters(int packetDelay, int packetJitter, int dropRate)
|
||||||
{
|
{
|
||||||
if (m_Driver.IsCreated)
|
if (m_Driver.IsCreated)
|
||||||
@@ -696,7 +713,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
//reflection, but this does not live in the context of a performance-critical loop, it runs once at initial connection time.
|
//reflection, but this does not live in the context of a performance-critical loop, it runs once at initial connection time.
|
||||||
if (m_RelayServerData.Equals(default(RelayServerData)))
|
if (m_RelayServerData.Equals(default(RelayServerData)))
|
||||||
{
|
{
|
||||||
Debug.LogError("You must call SetRelayServerData() at least once before calling StartRelayServer.");
|
Debug.LogError("You must call SetRelayServerData() at least once before calling StartServer.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -713,6 +730,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
public SendTarget Target;
|
public SendTarget Target;
|
||||||
public BatchedSendQueue Queue;
|
public BatchedSendQueue Queue;
|
||||||
public NetworkPipeline ReliablePipeline;
|
public NetworkPipeline ReliablePipeline;
|
||||||
|
public int MTU;
|
||||||
|
|
||||||
public void Execute()
|
public void Execute()
|
||||||
{
|
{
|
||||||
@@ -735,7 +753,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
// in the stream (the send queue does that automatically) we are sure they'll be
|
// 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
|
// reassembled properly at the other end. This allows us to lift the limit of ~44KB
|
||||||
// on reliable payloads (because of the reliable window size).
|
// on reliable payloads (because of the reliable window size).
|
||||||
var written = pipeline == ReliablePipeline ? Queue.FillWriterWithBytes(ref writer) : Queue.FillWriterWithMessages(ref writer);
|
var written = pipeline == ReliablePipeline ? Queue.FillWriterWithBytes(ref writer, MTU) : Queue.FillWriterWithMessages(ref writer);
|
||||||
|
|
||||||
result = Driver.EndSend(writer);
|
result = Driver.EndSend(writer);
|
||||||
if (result == written)
|
if (result == written)
|
||||||
@@ -769,12 +787,21 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mtu = 0;
|
||||||
|
if (NetworkManager)
|
||||||
|
{
|
||||||
|
var ngoClientId = NetworkManager.ConnectionManager.TransportIdToClientId(sendTarget.ClientId);
|
||||||
|
mtu = NetworkManager.GetPeerMTU(ngoClientId);
|
||||||
|
}
|
||||||
|
|
||||||
new SendBatchedMessagesJob
|
new SendBatchedMessagesJob
|
||||||
{
|
{
|
||||||
Driver = m_Driver.ToConcurrent(),
|
Driver = m_Driver.ToConcurrent(),
|
||||||
Target = sendTarget,
|
Target = sendTarget,
|
||||||
Queue = queue,
|
Queue = queue,
|
||||||
ReliablePipeline = m_ReliableSequencedPipeline
|
ReliablePipeline = m_ReliableSequencedPipeline,
|
||||||
|
MTU = mtu,
|
||||||
}.Run();
|
}.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,7 +966,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var transportClientId = NetworkManager.ClientIdToTransportId(ngoConnectionId);
|
var transportClientId = NetworkManager.ConnectionManager.ClientIdToTransportId(ngoConnectionId);
|
||||||
ExtractNetworkMetricsForClient(transportClientId);
|
ExtractNetworkMetricsForClient(transportClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1163,7 +1190,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
|
|
||||||
if (NetworkManager != null)
|
if (NetworkManager != null)
|
||||||
{
|
{
|
||||||
var transportId = NetworkManager.ClientIdToTransportId(clientId);
|
var transportId = NetworkManager.ConnectionManager.ClientIdToTransportId(clientId);
|
||||||
|
|
||||||
var rtt = ExtractRtt(ParseClientId(transportId));
|
var rtt = ExtractRtt(ParseClientId(transportId));
|
||||||
if (rtt > 0)
|
if (rtt > 0)
|
||||||
@@ -1185,6 +1212,11 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
|
|
||||||
NetworkManager = networkManager;
|
NetworkManager = networkManager;
|
||||||
|
|
||||||
|
if (NetworkManager && NetworkManager.PortOverride.Overidden)
|
||||||
|
{
|
||||||
|
ConnectionData.Port = NetworkManager.PortOverride.Value;
|
||||||
|
}
|
||||||
|
|
||||||
m_RealTimeProvider = NetworkManager ? NetworkManager.RealTimeProvider : new RealTimeProvider();
|
m_RealTimeProvider = NetworkManager ? NetworkManager.RealTimeProvider : new RealTimeProvider();
|
||||||
|
|
||||||
m_NetworkSettings = new NetworkSettings(Allocator.Persistent);
|
m_NetworkSettings = new NetworkSettings(Allocator.Persistent);
|
||||||
@@ -1268,7 +1300,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
// provide any reliability guarantees anymore. Disconnect the client since at
|
// provide any reliability guarantees anymore. Disconnect the client since at
|
||||||
// this point they're bound to become desynchronized.
|
// this point they're bound to become desynchronized.
|
||||||
|
|
||||||
var ngoClientId = NetworkManager?.TransportIdToClientId(clientId) ?? clientId;
|
var ngoClientId = NetworkManager?.ConnectionManager.TransportIdToClientId(clientId) ?? clientId;
|
||||||
Debug.LogError($"Couldn't add payload of size {payload.Count} to reliable send queue. " +
|
Debug.LogError($"Couldn't add payload of size {payload.Count} to reliable send queue. " +
|
||||||
$"Closing connection {ngoClientId} as reliability guarantees can't be maintained.");
|
$"Closing connection {ngoClientId} as reliability guarantees can't be maintained.");
|
||||||
|
|
||||||
@@ -1368,11 +1400,8 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
if (!m_Driver.IsCreated)
|
if (m_Driver.IsCreated)
|
||||||
{
|
{
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush all send queues to the network. NGO can be configured to flush its message
|
// Flush all send queues to the network. NGO can be configured to flush its message
|
||||||
// queue on shutdown. But this only calls the Send() method, which doesn't actually
|
// queue on shutdown. But this only calls the Send() method, which doesn't actually
|
||||||
// get anything to the network.
|
// get anything to the network.
|
||||||
@@ -1385,6 +1414,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
// actually get the messages on the wire. (Normally a flush send would be sufficient,
|
// actually get the messages on the wire. (Normally a flush send would be sufficient,
|
||||||
// but there might be disconnect messages and those require an update call.)
|
// but there might be disconnect messages and those require an update call.)
|
||||||
m_Driver.ScheduleUpdate().Complete();
|
m_Driver.ScheduleUpdate().Complete();
|
||||||
|
}
|
||||||
|
|
||||||
DisposeInternals();
|
DisposeInternals();
|
||||||
|
|
||||||
@@ -1548,6 +1578,21 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if UTP_TRANSPORT_2_1_ABOVE
|
||||||
|
if (m_ProtocolType == ProtocolType.RelayUnityTransport)
|
||||||
|
{
|
||||||
|
if (m_UseWebSockets && m_RelayServerData.IsWebSocket == 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("Transport is configured to use WebSockets, but Relay server data isn't. Be sure to use \"wss\" as the connection type when creating the server data (instead of \"dtls\" or \"udp\").");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_UseWebSockets && m_RelayServerData.IsWebSocket != 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("Relay server data indicates usage of WebSockets, but \"Use WebSockets\" checkbox isn't checked under \"Unity Transport\" component.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if UTP_TRANSPORT_2_0_ABOVE
|
#if UTP_TRANSPORT_2_0_ABOVE
|
||||||
if (m_UseWebSockets)
|
if (m_UseWebSockets)
|
||||||
{
|
{
|
||||||
@@ -1555,7 +1600,7 @@ namespace Unity.Netcode.Transports.UTP
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#if UNITY_WEBGL
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||||
Debug.LogWarning($"WebSockets were used even though they're not selected in NetworkManager. You should check {nameof(UseWebSockets)}', on the Unity Transport component, to silence this warning.");
|
Debug.LogWarning($"WebSockets were used even though they're not selected in NetworkManager. You should check {nameof(UseWebSockets)}', on the Unity Transport component, to silence this warning.");
|
||||||
driver = NetworkDriver.Create(new WebSocketNetworkInterface(), m_NetworkSettings);
|
driver = NetworkDriver.Create(new WebSocketNetworkInterface(), m_NetworkSettings);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -36,6 +36,16 @@
|
|||||||
"name": "com.unity.transport",
|
"name": "com.unity.transport",
|
||||||
"expression": "2.0.0-exp",
|
"expression": "2.0.0-exp",
|
||||||
"define": "UTP_TRANSPORT_2_0_ABOVE"
|
"define": "UTP_TRANSPORT_2_0_ABOVE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.unity.transport",
|
||||||
|
"expression": "2.1.0",
|
||||||
|
"define": "UTP_TRANSPORT_2_1_ABOVE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Unity",
|
||||||
|
"expression": "2023",
|
||||||
|
"define": "UNITY_DEDICATED_SERVER_ARGUMENTS_PRESENT"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -913,6 +913,9 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
if (CoroutineRunner == null)
|
if (CoroutineRunner == null)
|
||||||
{
|
{
|
||||||
CoroutineRunner = new GameObject("UnitTestSceneHandlerCoroutine").AddComponent<CoroutineRunner>();
|
CoroutineRunner = new GameObject("UnitTestSceneHandlerCoroutine").AddComponent<CoroutineRunner>();
|
||||||
|
// Move the CoroutineRunner into the DDOL in case we unload the scene it was instantiated in.
|
||||||
|
// (which if that gets destroyed then it basically stops all integration test queue processing)
|
||||||
|
Object.DontDestroyOnLoad(CoroutineRunner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
Assert.IsNotNull(m_MessageReceiptCheck, $"{nameof(m_MessageReceiptCheck)} is null, did you forget to initialize?");
|
Assert.IsNotNull(m_MessageReceiptCheck, $"{nameof(m_MessageReceiptCheck)} is null, did you forget to initialize?");
|
||||||
MessageHooks.ReceiptCheck = m_MessageReceiptCheck;
|
MessageHooks.ReceiptCheck = m_MessageReceiptCheck;
|
||||||
}
|
}
|
||||||
Assert.IsNotNull(m_NetworkManager.MessagingSystem, $"{nameof(NetworkManager.MessagingSystem)} is null! Did you forget to start first?");
|
Assert.IsNotNull(m_NetworkManager.ConnectionManager.MessageManager, $"{nameof(NetworkMessageManager)} is null! Did you forget to start first?");
|
||||||
m_NetworkManager.MessagingSystem.Hook(MessageHooks);
|
m_NetworkManager.ConnectionManager.MessageManager.Hook(MessageHooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AssignMessageType<T>() where T : INetworkMessage
|
internal void AssignMessageType<T>() where T : INetworkMessage
|
||||||
@@ -115,7 +115,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
internal void RemoveHook()
|
internal void RemoveHook()
|
||||||
{
|
{
|
||||||
m_NetworkManager.MessagingSystem.Unhook(MessageHooks);
|
m_NetworkManager.ConnectionManager.MessageManager.Unhook(MessageHooks);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AssignMessageType(Type type)
|
internal void AssignMessageType(Type type)
|
||||||
|
|||||||
@@ -431,6 +431,18 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CreateAndStartNewClient Only
|
||||||
|
/// Override this method to bypass the waiting for a client to connect.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Use this for testing connection and disconnection scenarios
|
||||||
|
/// </remarks>
|
||||||
|
protected virtual bool ShouldWaitForNewClientToConnect(NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This will create, start, and connect a new client while in the middle of an
|
/// This will create, start, and connect a new client while in the middle of an
|
||||||
/// integration test.
|
/// integration test.
|
||||||
@@ -448,13 +460,15 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
if (LogAllMessages)
|
if (LogAllMessages)
|
||||||
{
|
{
|
||||||
networkManager.MessagingSystem.Hook(new DebugNetworkHooks());
|
networkManager.ConnectionManager.MessageManager.Hook(new DebugNetworkHooks());
|
||||||
}
|
}
|
||||||
|
|
||||||
AddRemoveNetworkManager(networkManager, true);
|
AddRemoveNetworkManager(networkManager, true);
|
||||||
|
|
||||||
OnNewClientStarted(networkManager);
|
OnNewClientStarted(networkManager);
|
||||||
|
|
||||||
|
if (ShouldWaitForNewClientToConnect(networkManager))
|
||||||
|
{
|
||||||
// Wait for the new client to connect
|
// Wait for the new client to connect
|
||||||
yield return WaitForClientsConnectedOrTimeOut();
|
yield return WaitForClientsConnectedOrTimeOut();
|
||||||
|
|
||||||
@@ -469,6 +483,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
ClientNetworkManagerPostStart(networkManager);
|
ClientNetworkManagerPostStart(networkManager);
|
||||||
VerboseDebug($"[{networkManager.name}] Created and connected!");
|
VerboseDebug($"[{networkManager.name}] Created and connected!");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This will create, start, and connect a new client while in the middle of an
|
/// This will create, start, and connect a new client while in the middle of an
|
||||||
@@ -487,7 +502,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
if (LogAllMessages)
|
if (LogAllMessages)
|
||||||
{
|
{
|
||||||
networkManager.MessagingSystem.Hook(new DebugNetworkHooks());
|
networkManager.ConnectionManager.MessageManager.Hook(new DebugNetworkHooks());
|
||||||
}
|
}
|
||||||
|
|
||||||
AddRemoveNetworkManager(networkManager, true);
|
AddRemoveNetworkManager(networkManager, true);
|
||||||
@@ -871,7 +886,6 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
{
|
{
|
||||||
IntegrationTestSceneHandler.CanClientsLoad += ClientSceneHandler_CanClientsLoad;
|
IntegrationTestSceneHandler.CanClientsLoad += ClientSceneHandler_CanClientsLoad;
|
||||||
IntegrationTestSceneHandler.CanClientsUnload += ClientSceneHandler_CanClientsUnload;
|
IntegrationTestSceneHandler.CanClientsUnload += ClientSceneHandler_CanClientsUnload;
|
||||||
NetcodeIntegrationTestHelpers.RegisterSceneManagerHandler(m_ServerNetworkManager, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ClientSceneHandler_CanClientsUnload()
|
private bool ClientSceneHandler_CanClientsUnload()
|
||||||
@@ -1056,10 +1070,10 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected void EnableMessageLogging()
|
protected void EnableMessageLogging()
|
||||||
{
|
{
|
||||||
m_ServerNetworkManager.MessagingSystem.Hook(new DebugNetworkHooks());
|
m_ServerNetworkManager.ConnectionManager.MessageManager.Hook(new DebugNetworkHooks());
|
||||||
foreach (var client in m_ClientNetworkManagers)
|
foreach (var client in m_ClientNetworkManagers)
|
||||||
{
|
{
|
||||||
client.MessagingSystem.Hook(new DebugNetworkHooks());
|
client.ConnectionManager.MessageManager.Hook(new DebugNetworkHooks());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1578,12 +1592,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
foreach (var behaviour in Object.FindObjectsOfType<NetworkBehaviour>())
|
foreach (var behaviour in Object.FindObjectsOfType<NetworkBehaviour>())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
var method = behaviour.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
|
var method = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
if (method == null)
|
|
||||||
{
|
|
||||||
method = behaviour.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
method?.Invoke(behaviour, new object[] { });
|
method?.Invoke(behaviour, new object[] { });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,8 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
|
|
||||||
if (!networkManager.IsServer || networkManager.IsServer && serverSideSceneManager)
|
if (!networkManager.IsServer || networkManager.IsServer && serverSideSceneManager)
|
||||||
{
|
{
|
||||||
RegisterSceneManagerHandler(networkManager);
|
// Pass along the serverSideSceneManager property (otherwise the server won't register properly)
|
||||||
|
RegisterSceneManagerHandler(networkManager, serverSideSceneManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +304,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
{
|
{
|
||||||
clientToStart.StartClient();
|
clientToStart.StartClient();
|
||||||
s_Hooks[clientToStart] = new MultiInstanceHooks();
|
s_Hooks[clientToStart] = new MultiInstanceHooks();
|
||||||
clientToStart.MessagingSystem.Hook(s_Hooks[clientToStart]);
|
clientToStart.ConnectionManager.MessageManager.Hook(s_Hooks[clientToStart]);
|
||||||
if (!NetworkManagerInstances.Contains(clientToStart))
|
if (!NetworkManagerInstances.Contains(clientToStart))
|
||||||
{
|
{
|
||||||
NetworkManagerInstances.Add(clientToStart);
|
NetworkManagerInstances.Add(clientToStart);
|
||||||
@@ -405,7 +406,10 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
// scene to synchronize NetworkObjects. Next, add the currently active test runner scene to the scenes
|
// scene to synchronize NetworkObjects. Next, add the currently active test runner scene to the scenes
|
||||||
// loaded and register the server to client scene handle since host-server shares the test runner scene
|
// loaded and register the server to client scene handle since host-server shares the test runner scene
|
||||||
// with the clients.
|
// with the clients.
|
||||||
networkManager.SceneManager.GetAndAddNewlyLoadedSceneByName(scene.name);
|
if (!networkManager.SceneManager.ScenesLoaded.ContainsKey(scene.handle))
|
||||||
|
{
|
||||||
|
networkManager.SceneManager.ScenesLoaded.Add(scene.handle, scene);
|
||||||
|
}
|
||||||
networkManager.SceneManager.ServerSceneHandleToClientSceneHandle.Add(scene.handle, scene.handle);
|
networkManager.SceneManager.ServerSceneHandleToClientSceneHandle.Add(scene.handle, scene.handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,11 +444,11 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
}
|
}
|
||||||
|
|
||||||
var hooks = new MultiInstanceHooks();
|
var hooks = new MultiInstanceHooks();
|
||||||
server.MessagingSystem.Hook(hooks);
|
server.ConnectionManager.MessageManager.Hook(hooks);
|
||||||
s_Hooks[server] = hooks;
|
s_Hooks[server] = hooks;
|
||||||
|
|
||||||
// if set, then invoke this for the server
|
// Register the server side handler (always pass true for server)
|
||||||
RegisterHandlers(server);
|
RegisterHandlers(server, true);
|
||||||
|
|
||||||
callback?.Invoke();
|
callback?.Invoke();
|
||||||
|
|
||||||
@@ -452,7 +456,7 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
{
|
{
|
||||||
clients[i].StartClient();
|
clients[i].StartClient();
|
||||||
hooks = new MultiInstanceHooks();
|
hooks = new MultiInstanceHooks();
|
||||||
clients[i].MessagingSystem.Hook(hooks);
|
clients[i].ConnectionManager.MessageManager.Hook(hooks);
|
||||||
s_Hooks[clients[i]] = hooks;
|
s_Hooks[clients[i]] = hooks;
|
||||||
|
|
||||||
// if set, then invoke this for the client
|
// if set, then invoke this for the client
|
||||||
@@ -919,6 +923,24 @@ namespace Unity.Netcode.TestHelpers.Runtime
|
|||||||
var res = check.Result;
|
var res = check.Result;
|
||||||
result.Result = res;
|
result.Result = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static uint GetGlobalObjectIdHash(NetworkObject networkObject)
|
||||||
|
{
|
||||||
|
return networkObject.GlobalObjectIdHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
public static void SetRefreshAllPrefabsCallback(Action scenesProcessed)
|
||||||
|
{
|
||||||
|
NetworkObjectRefreshTool.AllScenesProcessed = scenesProcessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RefreshAllPrefabInstances(NetworkObject networkObject, Action scenesProcessed)
|
||||||
|
{
|
||||||
|
NetworkObjectRefreshTool.AllScenesProcessed = scenesProcessed;
|
||||||
|
networkObject.RefreshAllPrefabInstances();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty MonoBehaviour that is a holder of coroutine
|
// Empty MonoBehaviour that is a holder of coroutine
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ using System.Collections.Generic;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
|
||||||
namespace Unity.Netcode.RuntimeTests
|
namespace Unity.Netcode.RuntimeTests
|
||||||
{
|
{
|
||||||
public class NetcodeLogAssert
|
public class NetcodeLogAssert : IDisposable
|
||||||
{
|
{
|
||||||
private struct LogData
|
private struct LogData
|
||||||
{
|
{
|
||||||
@@ -20,8 +21,11 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
private List<LogData> AllLogs { get; }
|
private List<LogData> AllLogs { get; }
|
||||||
|
|
||||||
public NetcodeLogAssert()
|
private bool m_ResetIgnoreFailingMessagesOnTearDown;
|
||||||
|
public NetcodeLogAssert(bool ignorFailingMessages = false, bool resetOnTearDown = true)
|
||||||
{
|
{
|
||||||
|
LogAssert.ignoreFailingMessages = ignorFailingMessages;
|
||||||
|
m_ResetIgnoreFailingMessagesOnTearDown = resetOnTearDown;
|
||||||
AllLogs = new List<LogData>();
|
AllLogs = new List<LogData>();
|
||||||
Activate();
|
Activate();
|
||||||
}
|
}
|
||||||
@@ -51,6 +55,16 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnityTearDown]
|
||||||
|
public void OnTearDown()
|
||||||
|
{
|
||||||
|
// Defaults to true and will reset LogAssert.ignoreFailingMessages during tear down
|
||||||
|
if (m_ResetIgnoreFailingMessagesOnTearDown)
|
||||||
|
{
|
||||||
|
LogAssert.ignoreFailingMessages = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
@@ -59,6 +73,8 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
|
|
||||||
private void Dispose(bool disposing)
|
private void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
// Always reset when disposing
|
||||||
|
LogAssert.ignoreFailingMessages = false;
|
||||||
if (m_Disposed)
|
if (m_Disposed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -131,6 +147,7 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
if (logEvent.LogType == type && messageRegex.IsMatch(logEvent.Message))
|
if (logEvent.LogType == type && messageRegex.IsMatch(logEvent.Message))
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +158,23 @@ namespace Unity.Netcode.RuntimeTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasLogBeenReceived(LogType type, string message)
|
||||||
|
{
|
||||||
|
var found = false;
|
||||||
|
lock (m_Lock)
|
||||||
|
{
|
||||||
|
foreach (var logEvent in AllLogs)
|
||||||
|
{
|
||||||
|
if (logEvent.LogType == type && message.Equals(logEvent.Message))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
lock (m_Lock)
|
lock (m_Lock)
|
||||||
|
|||||||
92
Tests/Editor/Messaging/DisconnectOnSendTests.cs
Normal file
92
Tests/Editor/Messaging/DisconnectOnSendTests.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.EditorTests
|
||||||
|
{
|
||||||
|
public class DisconnectOnSendTests
|
||||||
|
{
|
||||||
|
private struct TestMessage : INetworkMessage, INetworkSerializeByMemcpy
|
||||||
|
{
|
||||||
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Version => 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DisconnectOnSendMessageSender : INetworkMessageSender
|
||||||
|
{
|
||||||
|
public NetworkMessageManager MessageManager;
|
||||||
|
|
||||||
|
public void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData)
|
||||||
|
{
|
||||||
|
MessageManager.ClientDisconnected(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestMessageProvider : INetworkMessageProvider
|
||||||
|
{
|
||||||
|
// Keep track of what we sent
|
||||||
|
private List<NetworkMessageManager.MessageWithHandler> m_MessageList = new List<NetworkMessageManager.MessageWithHandler>
|
||||||
|
{
|
||||||
|
new NetworkMessageManager.MessageWithHandler
|
||||||
|
{
|
||||||
|
MessageType = typeof(TestMessage),
|
||||||
|
Handler = NetworkMessageManager.ReceiveMessage<TestMessage>,
|
||||||
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessage>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
|
{
|
||||||
|
return m_MessageList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestMessageProvider m_TestMessageProvider;
|
||||||
|
private DisconnectOnSendMessageSender m_MessageSender;
|
||||||
|
private NetworkMessageManager m_MessageManager;
|
||||||
|
private ulong[] m_Clients = { 0 };
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
m_MessageSender = new DisconnectOnSendMessageSender();
|
||||||
|
m_TestMessageProvider = new TestMessageProvider();
|
||||||
|
m_MessageManager = new NetworkMessageManager(m_MessageSender, this, m_TestMessageProvider);
|
||||||
|
m_MessageSender.MessageManager = m_MessageManager;
|
||||||
|
m_MessageManager.ClientConnected(0);
|
||||||
|
m_MessageManager.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void TearDown()
|
||||||
|
{
|
||||||
|
m_MessageManager.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestMessage GetMessage()
|
||||||
|
{
|
||||||
|
return new TestMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenDisconnectIsCalledDuringSend_NoErrorsOccur()
|
||||||
|
{
|
||||||
|
var message = GetMessage();
|
||||||
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
|
|
||||||
|
// This is where an exception would be thrown and logged.
|
||||||
|
m_MessageManager.ProcessSendQueues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Tests/Editor/Messaging/DisconnectOnSendTests.cs.meta
Normal file
3
Tests/Editor/Messaging/DisconnectOnSendTests.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7984df99de5c4b85a1b8567582d00c64
|
||||||
|
timeCreated: 1680888331
|
||||||
@@ -39,17 +39,17 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public int Version => 0;
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestMessageProvider : IMessageProvider
|
private class TestMessageProvider : INetworkMessageProvider
|
||||||
{
|
{
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
return new List<MessagingSystem.MessageWithHandler>
|
return new List<NetworkMessageManager.MessageWithHandler>
|
||||||
{
|
{
|
||||||
new MessagingSystem.MessageWithHandler
|
new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessage),
|
MessageType = typeof(TestMessage),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessage>,
|
Handler = NetworkMessageManager.ReceiveMessage<TestMessage>,
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessage>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessage>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
AdditionalGarbageData,
|
AdditionalGarbageData,
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestMessageSender : IMessageSender
|
private class TestMessageSender : INetworkMessageSender
|
||||||
{
|
{
|
||||||
|
|
||||||
public TypeOfCorruption Corruption;
|
public TypeOfCorruption Corruption;
|
||||||
@@ -108,7 +108,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessagingSystem m_MessagingSystem;
|
private NetworkMessageManager m_MessageManager;
|
||||||
private TestMessageSender m_MessageSender;
|
private TestMessageSender m_MessageSender;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@@ -118,16 +118,16 @@ namespace Unity.Netcode.EditorTests
|
|||||||
TestMessage.Deserialized = false;
|
TestMessage.Deserialized = false;
|
||||||
m_MessageSender = new TestMessageSender();
|
m_MessageSender = new TestMessageSender();
|
||||||
|
|
||||||
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, new TestMessageProvider());
|
m_MessageManager = new NetworkMessageManager(m_MessageSender, this, new TestMessageProvider());
|
||||||
|
|
||||||
m_MessagingSystem.ClientConnected(0);
|
m_MessageManager.ClientConnected(0);
|
||||||
m_MessagingSystem.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
m_MessageManager.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void TearDown()
|
public void TearDown()
|
||||||
{
|
{
|
||||||
m_MessagingSystem.Dispose();
|
m_MessageManager.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestMessage GetMessage()
|
private TestMessage GetMessage()
|
||||||
@@ -159,14 +159,14 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dummy batch header
|
// Dummy batch header
|
||||||
var batchHeader = new BatchHeader
|
var batchHeader = new NetworkBatchHeader
|
||||||
{
|
{
|
||||||
BatchCount = 1
|
BatchCount = 1
|
||||||
};
|
};
|
||||||
var messageHeader = new MessageHeader
|
var messageHeader = new NetworkMessageHeader
|
||||||
{
|
{
|
||||||
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
||||||
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
|
MessageType = m_MessageManager.GetMessageType(typeof(TestMessage)),
|
||||||
};
|
};
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
|
|
||||||
@@ -182,11 +182,11 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
// Fill out the rest of the batch header
|
// Fill out the rest of the batch header
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
batchHeader = new BatchHeader
|
batchHeader = new NetworkBatchHeader
|
||||||
{
|
{
|
||||||
Magic = BatchHeader.MagicValue,
|
Magic = NetworkBatchHeader.MagicValue,
|
||||||
BatchSize = writer.Length,
|
BatchSize = writer.Length,
|
||||||
BatchHash = XXHash.Hash64(writer.GetUnsafePtr() + sizeof(BatchHeader), writer.Length - sizeof(BatchHeader)),
|
BatchHash = XXHash.Hash64(writer.GetUnsafePtr() + sizeof(NetworkBatchHeader), writer.Length - sizeof(NetworkBatchHeader)),
|
||||||
BatchCount = 1
|
BatchCount = 1
|
||||||
};
|
};
|
||||||
writer.WriteValue(batchHeader);
|
writer.WriteValue(batchHeader);
|
||||||
@@ -194,7 +194,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
var receivedMessage = m_MessageSender.MessageQueue[0];
|
var receivedMessage = m_MessageSender.MessageQueue[0];
|
||||||
m_MessageSender.MessageQueue.Clear();
|
m_MessageSender.MessageQueue.Clear();
|
||||||
m_MessagingSystem.HandleIncomingData(0, new ArraySegment<byte>(receivedMessage), 0);
|
m_MessageManager.HandleIncomingData(0, new ArraySegment<byte>(receivedMessage), 0);
|
||||||
Assert.IsFalse(TestMessage.Deserialized);
|
Assert.IsFalse(TestMessage.Deserialized);
|
||||||
Assert.IsFalse(TestMessage.Handled);
|
Assert.IsFalse(TestMessage.Handled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,23 +39,23 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public int Version => 0;
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestMessageProvider : IMessageProvider
|
private class TestMessageProvider : INetworkMessageProvider
|
||||||
{
|
{
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
return new List<MessagingSystem.MessageWithHandler>
|
return new List<NetworkMessageManager.MessageWithHandler>
|
||||||
{
|
{
|
||||||
new MessagingSystem.MessageWithHandler
|
new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessage),
|
MessageType = typeof(TestMessage),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessage>,
|
Handler = NetworkMessageManager.ReceiveMessage<TestMessage>,
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessage>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessage>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessagingSystem m_MessagingSystem;
|
private NetworkMessageManager m_MessageManager;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
@@ -64,14 +64,14 @@ namespace Unity.Netcode.EditorTests
|
|||||||
TestMessage.Handled = false;
|
TestMessage.Handled = false;
|
||||||
TestMessage.DeserializedValues.Clear();
|
TestMessage.DeserializedValues.Clear();
|
||||||
|
|
||||||
m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this, new TestMessageProvider());
|
m_MessageManager = new NetworkMessageManager(new NopMessageSender(), this, new TestMessageProvider());
|
||||||
m_MessagingSystem.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
m_MessageManager.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void TearDown()
|
public void TearDown()
|
||||||
{
|
{
|
||||||
m_MessagingSystem.Dispose();
|
m_MessageManager.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestMessage GetMessage()
|
private TestMessage GetMessage()
|
||||||
@@ -88,10 +88,10 @@ namespace Unity.Netcode.EditorTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void WhenHandlingAMessage_ReceiveMethodIsCalled()
|
public void WhenHandlingAMessage_ReceiveMethodIsCalled()
|
||||||
{
|
{
|
||||||
var messageHeader = new MessageHeader
|
var messageHeader = new NetworkMessageHeader
|
||||||
{
|
{
|
||||||
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
||||||
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
|
MessageType = m_MessageManager.GetMessageType(typeof(TestMessage)),
|
||||||
};
|
};
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.HandleMessage(messageHeader, reader, 0, 0, 0);
|
m_MessageManager.HandleMessage(messageHeader, reader, 0, 0, 0);
|
||||||
Assert.IsTrue(TestMessage.Deserialized);
|
Assert.IsTrue(TestMessage.Deserialized);
|
||||||
Assert.IsTrue(TestMessage.Handled);
|
Assert.IsTrue(TestMessage.Handled);
|
||||||
Assert.AreEqual(1, TestMessage.DeserializedValues.Count);
|
Assert.AreEqual(1, TestMessage.DeserializedValues.Count);
|
||||||
@@ -116,14 +116,14 @@ namespace Unity.Netcode.EditorTests
|
|||||||
[Test]
|
[Test]
|
||||||
public unsafe void WhenHandlingIncomingData_ReceiveIsNotCalledBeforeProcessingIncomingMessageQueue()
|
public unsafe void WhenHandlingIncomingData_ReceiveIsNotCalledBeforeProcessingIncomingMessageQueue()
|
||||||
{
|
{
|
||||||
var batchHeader = new BatchHeader
|
var batchHeader = new NetworkBatchHeader
|
||||||
{
|
{
|
||||||
BatchCount = 1
|
BatchCount = 1
|
||||||
};
|
};
|
||||||
var messageHeader = new MessageHeader
|
var messageHeader = new NetworkMessageHeader
|
||||||
{
|
{
|
||||||
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
||||||
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
|
MessageType = m_MessageManager.GetMessageType(typeof(TestMessage)),
|
||||||
};
|
};
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
|
|
||||||
@@ -139,11 +139,11 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
// Fill out the rest of the batch header
|
// Fill out the rest of the batch header
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
batchHeader = new BatchHeader
|
batchHeader = new NetworkBatchHeader
|
||||||
{
|
{
|
||||||
Magic = BatchHeader.MagicValue,
|
Magic = NetworkBatchHeader.MagicValue,
|
||||||
BatchSize = writer.Length,
|
BatchSize = writer.Length,
|
||||||
BatchHash = XXHash.Hash64(writer.GetUnsafePtr() + sizeof(BatchHeader), writer.Length - sizeof(BatchHeader)),
|
BatchHash = XXHash.Hash64(writer.GetUnsafePtr() + sizeof(NetworkBatchHeader), writer.Length - sizeof(NetworkBatchHeader)),
|
||||||
BatchCount = 1
|
BatchCount = 1
|
||||||
};
|
};
|
||||||
writer.WriteValue(batchHeader);
|
writer.WriteValue(batchHeader);
|
||||||
@@ -151,7 +151,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
|
m_MessageManager.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
|
||||||
Assert.IsFalse(TestMessage.Deserialized);
|
Assert.IsFalse(TestMessage.Deserialized);
|
||||||
Assert.IsFalse(TestMessage.Handled);
|
Assert.IsFalse(TestMessage.Handled);
|
||||||
Assert.IsEmpty(TestMessage.DeserializedValues);
|
Assert.IsEmpty(TestMessage.DeserializedValues);
|
||||||
@@ -162,14 +162,14 @@ namespace Unity.Netcode.EditorTests
|
|||||||
[Test]
|
[Test]
|
||||||
public unsafe void WhenReceivingAMessageAndProcessingMessageQueue_ReceiveMethodIsCalled()
|
public unsafe void WhenReceivingAMessageAndProcessingMessageQueue_ReceiveMethodIsCalled()
|
||||||
{
|
{
|
||||||
var batchHeader = new BatchHeader
|
var batchHeader = new NetworkBatchHeader
|
||||||
{
|
{
|
||||||
BatchCount = 1
|
BatchCount = 1
|
||||||
};
|
};
|
||||||
var messageHeader = new MessageHeader
|
var messageHeader = new NetworkMessageHeader
|
||||||
{
|
{
|
||||||
MessageSize = (uint)UnsafeUtility.SizeOf<TestMessage>(),
|
MessageSize = (uint)UnsafeUtility.SizeOf<TestMessage>(),
|
||||||
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
|
MessageType = m_MessageManager.GetMessageType(typeof(TestMessage)),
|
||||||
};
|
};
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
|
|
||||||
@@ -183,11 +183,11 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
// Fill out the rest of the batch header
|
// Fill out the rest of the batch header
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
batchHeader = new BatchHeader
|
batchHeader = new NetworkBatchHeader
|
||||||
{
|
{
|
||||||
Magic = BatchHeader.MagicValue,
|
Magic = NetworkBatchHeader.MagicValue,
|
||||||
BatchSize = writer.Length,
|
BatchSize = writer.Length,
|
||||||
BatchHash = XXHash.Hash64(writer.GetUnsafePtr() + sizeof(BatchHeader), writer.Length - sizeof(BatchHeader)),
|
BatchHash = XXHash.Hash64(writer.GetUnsafePtr() + sizeof(NetworkBatchHeader), writer.Length - sizeof(NetworkBatchHeader)),
|
||||||
BatchCount = 1
|
BatchCount = 1
|
||||||
};
|
};
|
||||||
writer.WriteValue(batchHeader);
|
writer.WriteValue(batchHeader);
|
||||||
@@ -195,8 +195,8 @@ namespace Unity.Netcode.EditorTests
|
|||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
|
m_MessageManager.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
|
||||||
m_MessagingSystem.ProcessIncomingMessageQueue();
|
m_MessageManager.ProcessIncomingMessageQueue();
|
||||||
Assert.IsTrue(TestMessage.Deserialized);
|
Assert.IsTrue(TestMessage.Deserialized);
|
||||||
Assert.IsTrue(TestMessage.Handled);
|
Assert.IsTrue(TestMessage.Handled);
|
||||||
Assert.AreEqual(1, TestMessage.DeserializedValues.Count);
|
Assert.AreEqual(1, TestMessage.DeserializedValues.Count);
|
||||||
@@ -208,14 +208,14 @@ namespace Unity.Netcode.EditorTests
|
|||||||
[Test]
|
[Test]
|
||||||
public unsafe void WhenReceivingMultipleMessagesAndProcessingMessageQueue_ReceiveMethodIsCalledMultipleTimes()
|
public unsafe void WhenReceivingMultipleMessagesAndProcessingMessageQueue_ReceiveMethodIsCalledMultipleTimes()
|
||||||
{
|
{
|
||||||
var batchHeader = new BatchHeader
|
var batchHeader = new NetworkBatchHeader
|
||||||
{
|
{
|
||||||
BatchCount = 2
|
BatchCount = 2
|
||||||
};
|
};
|
||||||
var messageHeader = new MessageHeader
|
var messageHeader = new NetworkMessageHeader
|
||||||
{
|
{
|
||||||
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
||||||
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
|
MessageType = m_MessageManager.GetMessageType(typeof(TestMessage)),
|
||||||
};
|
};
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
var message2 = GetMessage();
|
var message2 = GetMessage();
|
||||||
@@ -233,11 +233,11 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
// Fill out the rest of the batch header
|
// Fill out the rest of the batch header
|
||||||
writer.Seek(0);
|
writer.Seek(0);
|
||||||
batchHeader = new BatchHeader
|
batchHeader = new NetworkBatchHeader
|
||||||
{
|
{
|
||||||
Magic = BatchHeader.MagicValue,
|
Magic = NetworkBatchHeader.MagicValue,
|
||||||
BatchSize = writer.Length,
|
BatchSize = writer.Length,
|
||||||
BatchHash = XXHash.Hash64(writer.GetUnsafePtr() + sizeof(BatchHeader), writer.Length - sizeof(BatchHeader)),
|
BatchHash = XXHash.Hash64(writer.GetUnsafePtr() + sizeof(NetworkBatchHeader), writer.Length - sizeof(NetworkBatchHeader)),
|
||||||
BatchCount = 2
|
BatchCount = 2
|
||||||
};
|
};
|
||||||
writer.WriteValue(batchHeader);
|
writer.WriteValue(batchHeader);
|
||||||
@@ -245,12 +245,12 @@ namespace Unity.Netcode.EditorTests
|
|||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
|
m_MessageManager.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
|
||||||
Assert.IsFalse(TestMessage.Deserialized);
|
Assert.IsFalse(TestMessage.Deserialized);
|
||||||
Assert.IsFalse(TestMessage.Handled);
|
Assert.IsFalse(TestMessage.Handled);
|
||||||
Assert.IsEmpty(TestMessage.DeserializedValues);
|
Assert.IsEmpty(TestMessage.DeserializedValues);
|
||||||
|
|
||||||
m_MessagingSystem.ProcessIncomingMessageQueue();
|
m_MessageManager.ProcessIncomingMessageQueue();
|
||||||
Assert.IsTrue(TestMessage.Deserialized);
|
Assert.IsTrue(TestMessage.Deserialized);
|
||||||
Assert.IsTrue(TestMessage.Handled);
|
Assert.IsTrue(TestMessage.Handled);
|
||||||
Assert.AreEqual(2, TestMessage.DeserializedValues.Count);
|
Assert.AreEqual(2, TestMessage.DeserializedValues.Count);
|
||||||
|
|||||||
@@ -48,23 +48,23 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
public int Version => 0;
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
private class TestMessageProviderOne : IMessageProvider
|
private class TestMessageProviderOne : INetworkMessageProvider
|
||||||
{
|
{
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
return new List<MessagingSystem.MessageWithHandler>
|
return new List<NetworkMessageManager.MessageWithHandler>
|
||||||
{
|
{
|
||||||
new MessagingSystem.MessageWithHandler
|
new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessageOne),
|
MessageType = typeof(TestMessageOne),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessageOne>,
|
Handler = NetworkMessageManager.ReceiveMessage<TestMessageOne>,
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessageOne>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessageOne>
|
||||||
},
|
},
|
||||||
new MessagingSystem.MessageWithHandler
|
new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessageTwo),
|
MessageType = typeof(TestMessageTwo),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessageTwo>,
|
Handler = NetworkMessageManager.ReceiveMessage<TestMessageTwo>,
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessageTwo>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessageTwo>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -91,17 +91,17 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
public int Version => 0;
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
private class TestMessageProviderTwo : IMessageProvider
|
private class TestMessageProviderTwo : INetworkMessageProvider
|
||||||
{
|
{
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
return new List<MessagingSystem.MessageWithHandler>
|
return new List<NetworkMessageManager.MessageWithHandler>
|
||||||
{
|
{
|
||||||
new MessagingSystem.MessageWithHandler
|
new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessageThree),
|
MessageType = typeof(TestMessageThree),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessageThree>,
|
Handler = NetworkMessageManager.ReceiveMessage<TestMessageThree>,
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessageThree>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessageThree>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -127,17 +127,17 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
public int Version => 0;
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
private class TestMessageProviderThree : IMessageProvider
|
private class TestMessageProviderThree : INetworkMessageProvider
|
||||||
{
|
{
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
return new List<MessagingSystem.MessageWithHandler>
|
return new List<NetworkMessageManager.MessageWithHandler>
|
||||||
{
|
{
|
||||||
new MessagingSystem.MessageWithHandler
|
new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessageFour),
|
MessageType = typeof(TestMessageFour),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessageFour>,
|
Handler = NetworkMessageManager.ReceiveMessage<TestMessageFour>,
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessageFour>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessageFour>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -148,9 +148,9 @@ namespace Unity.Netcode.EditorTests
|
|||||||
{
|
{
|
||||||
var sender = new NopMessageSender();
|
var sender = new NopMessageSender();
|
||||||
|
|
||||||
using var systemOne = new MessagingSystem(sender, null, new TestMessageProviderOne());
|
using var systemOne = new NetworkMessageManager(sender, null, new TestMessageProviderOne());
|
||||||
using var systemTwo = new MessagingSystem(sender, null, new TestMessageProviderTwo());
|
using var systemTwo = new NetworkMessageManager(sender, null, new TestMessageProviderTwo());
|
||||||
using var systemThree = new MessagingSystem(sender, null, new TestMessageProviderThree());
|
using var systemThree = new NetworkMessageManager(sender, null, new TestMessageProviderThree());
|
||||||
|
|
||||||
using (systemOne)
|
using (systemOne)
|
||||||
using (systemTwo)
|
using (systemTwo)
|
||||||
@@ -172,18 +172,18 @@ namespace Unity.Netcode.EditorTests
|
|||||||
{
|
{
|
||||||
var sender = new NopMessageSender();
|
var sender = new NopMessageSender();
|
||||||
|
|
||||||
using var systemOne = new MessagingSystem(sender, null, new TestMessageProviderOne());
|
using var systemOne = new NetworkMessageManager(sender, null, new TestMessageProviderOne());
|
||||||
using var systemTwo = new MessagingSystem(sender, null, new TestMessageProviderTwo());
|
using var systemTwo = new NetworkMessageManager(sender, null, new TestMessageProviderTwo());
|
||||||
using var systemThree = new MessagingSystem(sender, null, new TestMessageProviderThree());
|
using var systemThree = new NetworkMessageManager(sender, null, new TestMessageProviderThree());
|
||||||
|
|
||||||
using (systemOne)
|
using (systemOne)
|
||||||
using (systemTwo)
|
using (systemTwo)
|
||||||
using (systemThree)
|
using (systemThree)
|
||||||
{
|
{
|
||||||
MessagingSystem.MessageHandler handlerOne = MessagingSystem.ReceiveMessage<TestMessageOne>;
|
NetworkMessageManager.MessageHandler handlerOne = NetworkMessageManager.ReceiveMessage<TestMessageOne>;
|
||||||
MessagingSystem.MessageHandler handlerTwo = MessagingSystem.ReceiveMessage<TestMessageTwo>;
|
NetworkMessageManager.MessageHandler handlerTwo = NetworkMessageManager.ReceiveMessage<TestMessageTwo>;
|
||||||
MessagingSystem.MessageHandler handlerThree = MessagingSystem.ReceiveMessage<TestMessageThree>;
|
NetworkMessageManager.MessageHandler handlerThree = NetworkMessageManager.ReceiveMessage<TestMessageThree>;
|
||||||
MessagingSystem.MessageHandler handlerFour = MessagingSystem.ReceiveMessage<TestMessageFour>;
|
NetworkMessageManager.MessageHandler handlerFour = NetworkMessageManager.ReceiveMessage<TestMessageFour>;
|
||||||
|
|
||||||
Assert.AreEqual(handlerOne, systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageOne))]);
|
Assert.AreEqual(handlerOne, systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageOne))]);
|
||||||
Assert.AreEqual(handlerTwo, systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageTwo))]);
|
Assert.AreEqual(handlerTwo, systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageTwo))]);
|
||||||
@@ -216,29 +216,29 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
#pragma warning restore IDE1006
|
#pragma warning restore IDE1006
|
||||||
|
|
||||||
internal class OrderingMessageProvider : IMessageProvider
|
internal class OrderingMessageProvider : INetworkMessageProvider
|
||||||
{
|
{
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
var listMessages = new List<MessagingSystem.MessageWithHandler>();
|
var listMessages = new List<NetworkMessageManager.MessageWithHandler>();
|
||||||
|
|
||||||
var messageWithHandler = new MessagingSystem.MessageWithHandler
|
var messageWithHandler = new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(zzzLateLexicographicNetworkMessage),
|
MessageType = typeof(zzzLateLexicographicNetworkMessage),
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<zzzLateLexicographicNetworkMessage>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<zzzLateLexicographicNetworkMessage>
|
||||||
};
|
};
|
||||||
listMessages.Add(messageWithHandler);
|
listMessages.Add(messageWithHandler);
|
||||||
|
|
||||||
messageWithHandler.MessageType = typeof(ConnectionRequestMessage);
|
messageWithHandler.MessageType = typeof(ConnectionRequestMessage);
|
||||||
messageWithHandler.GetVersion = MessagingSystem.CreateMessageAndGetVersion<ConnectionRequestMessage>;
|
messageWithHandler.GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<ConnectionRequestMessage>;
|
||||||
listMessages.Add(messageWithHandler);
|
listMessages.Add(messageWithHandler);
|
||||||
|
|
||||||
messageWithHandler.MessageType = typeof(ConnectionApprovedMessage);
|
messageWithHandler.MessageType = typeof(ConnectionApprovedMessage);
|
||||||
messageWithHandler.GetVersion = MessagingSystem.CreateMessageAndGetVersion<ConnectionApprovedMessage>;
|
messageWithHandler.GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<ConnectionApprovedMessage>;
|
||||||
listMessages.Add(messageWithHandler);
|
listMessages.Add(messageWithHandler);
|
||||||
|
|
||||||
messageWithHandler.MessageType = typeof(AAAEarlyLexicographicNetworkMessage);
|
messageWithHandler.MessageType = typeof(AAAEarlyLexicographicNetworkMessage);
|
||||||
messageWithHandler.GetVersion = MessagingSystem.CreateMessageAndGetVersion<AAAEarlyLexicographicNetworkMessage>;
|
messageWithHandler.GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<AAAEarlyLexicographicNetworkMessage>;
|
||||||
listMessages.Add(messageWithHandler);
|
listMessages.Add(messageWithHandler);
|
||||||
|
|
||||||
return listMessages;
|
return listMessages;
|
||||||
@@ -250,18 +250,18 @@ namespace Unity.Netcode.EditorTests
|
|||||||
{
|
{
|
||||||
var sender = new NopMessageSender();
|
var sender = new NopMessageSender();
|
||||||
var provider = new OrderingMessageProvider();
|
var provider = new OrderingMessageProvider();
|
||||||
using var messagingSystem = new MessagingSystem(sender, null, provider);
|
using var messageManager = new NetworkMessageManager(sender, null, provider);
|
||||||
|
|
||||||
// the 2 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(messageManager.MessageTypes[0], typeof(ConnectionApprovedMessage));
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[1], typeof(ConnectionRequestMessage));
|
Assert.AreEqual(messageManager.MessageTypes[1], typeof(ConnectionRequestMessage));
|
||||||
|
|
||||||
// the other should follow after
|
// the other should follow after
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[2], typeof(AAAEarlyLexicographicNetworkMessage));
|
Assert.AreEqual(messageManager.MessageTypes[2], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
Assert.AreEqual(messageManager.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
||||||
|
|
||||||
// there should not be any extras
|
// there should not be any extras
|
||||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 4);
|
Assert.AreEqual(messageManager.MessageHandlerCount, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ 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, int targetVersion)
|
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||||
{
|
{
|
||||||
Serialized = true;
|
Serialized = true;
|
||||||
@@ -36,7 +37,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public int Version => 0;
|
public int Version => 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestMessageSender : IMessageSender
|
private class TestMessageSender : INetworkMessageSender
|
||||||
{
|
{
|
||||||
public List<byte[]> MessageQueue = new List<byte[]>();
|
public List<byte[]> MessageQueue = new List<byte[]>();
|
||||||
|
|
||||||
@@ -46,30 +47,31 @@ namespace Unity.Netcode.EditorTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestMessageProvider : IMessageProvider, IDisposable
|
private class TestMessageProvider : INetworkMessageProvider, IDisposable
|
||||||
{
|
{
|
||||||
// Keep track of what we sent
|
// Keep track of what we sent
|
||||||
private List<List<MessagingSystem.MessageWithHandler>> m_CachedMessages = new List<List<MessagingSystem.MessageWithHandler>>();
|
private List<List<NetworkMessageManager.MessageWithHandler>> m_CachedMessages = new List<List<NetworkMessageManager.MessageWithHandler>>();
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (var cachedItem in m_CachedMessages)
|
foreach (var cachedItem in m_CachedMessages)
|
||||||
{
|
{
|
||||||
// Clear out any references to MessagingSystem.MessageWithHandlers
|
// Clear out any references to NetworkMessageManager.MessageWithHandlers
|
||||||
cachedItem.Clear();
|
cachedItem.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_CachedMessages.Clear();
|
m_CachedMessages.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
var messageList = new List<MessagingSystem.MessageWithHandler>
|
var messageList = new List<NetworkMessageManager.MessageWithHandler>
|
||||||
{
|
{
|
||||||
new MessagingSystem.MessageWithHandler
|
new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessage),
|
MessageType = typeof(TestMessage),
|
||||||
Handler = MessagingSystem.ReceiveMessage<TestMessage>,
|
Handler = NetworkMessageManager.ReceiveMessage<TestMessage>,
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessage>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessage>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Track messages sent
|
// Track messages sent
|
||||||
@@ -80,7 +82,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
|
|
||||||
private TestMessageProvider m_TestMessageProvider;
|
private TestMessageProvider m_TestMessageProvider;
|
||||||
private TestMessageSender m_MessageSender;
|
private TestMessageSender m_MessageSender;
|
||||||
private MessagingSystem m_MessagingSystem;
|
private NetworkMessageManager m_MessageManager;
|
||||||
private ulong[] m_Clients = { 0 };
|
private ulong[] m_Clients = { 0 };
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
@@ -89,16 +91,16 @@ namespace Unity.Netcode.EditorTests
|
|||||||
TestMessage.Serialized = false;
|
TestMessage.Serialized = false;
|
||||||
m_MessageSender = new TestMessageSender();
|
m_MessageSender = new TestMessageSender();
|
||||||
m_TestMessageProvider = new TestMessageProvider();
|
m_TestMessageProvider = new TestMessageProvider();
|
||||||
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, m_TestMessageProvider);
|
m_MessageManager = new NetworkMessageManager(m_MessageSender, this, m_TestMessageProvider);
|
||||||
m_MessagingSystem.ClientConnected(0);
|
m_MessageManager.ClientConnected(0);
|
||||||
m_MessagingSystem.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
m_MessageManager.SetVersion(0, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void TearDown()
|
public void TearDown()
|
||||||
{
|
{
|
||||||
m_TestMessageProvider.Dispose();
|
m_TestMessageProvider.Dispose();
|
||||||
m_MessagingSystem.Dispose();
|
m_MessageManager.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestMessage GetMessage()
|
private TestMessage GetMessage()
|
||||||
@@ -116,7 +118,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void WhenSendingMessage_SerializeIsCalled()
|
public void WhenSendingMessage_SerializeIsCalled()
|
||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
Assert.IsTrue(TestMessage.Serialized);
|
Assert.IsTrue(TestMessage.Serialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +126,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void WhenSendingMessage_NothingIsSentBeforeProcessingSendQueue()
|
public void WhenSendingMessage_NothingIsSentBeforeProcessingSendQueue()
|
||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
Assert.IsEmpty(m_MessageSender.MessageQueue);
|
Assert.IsEmpty(m_MessageSender.MessageQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,9 +134,9 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void WhenProcessingSendQueue_MessageIsSent()
|
public void WhenProcessingSendQueue_MessageIsSent()
|
||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
|
|
||||||
m_MessagingSystem.ProcessSendQueues();
|
m_MessageManager.ProcessSendQueues();
|
||||||
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,11 +144,11 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void WhenSendingMultipleMessages_MessagesAreBatched()
|
public void WhenSendingMultipleMessages_MessagesAreBatched()
|
||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
|
|
||||||
m_MessagingSystem.ProcessSendQueues();
|
m_MessageManager.ProcessSendQueues();
|
||||||
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,40 +157,100 @@ namespace Unity.Netcode.EditorTests
|
|||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
|
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
|
||||||
for (var i = 0; i < (1300 - UnsafeUtility.SizeOf<BatchHeader>()) / size; ++i)
|
for (var i = 0; i < (m_MessageManager.NonFragmentedMessageMaxSize - UnsafeUtility.SizeOf<NetworkBatchHeader>()) / size; ++i)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_MessagingSystem.ProcessSendQueues();
|
m_MessageManager.ProcessSendQueues();
|
||||||
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void WhenExceedingBatchSize_NewBatchesAreCreated()
|
public void WhenExceedingBatchSize_NewBatchesAreCreated([Values(500, 1000, 1300, 2000)] int maxMessageSize)
|
||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
|
m_MessageManager.NonFragmentedMessageMaxSize = maxMessageSize;
|
||||||
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
|
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
|
||||||
for (var i = 0; i < ((1300 - UnsafeUtility.SizeOf<BatchHeader>()) / size) + 1; ++i)
|
for (var i = 0; i < ((m_MessageManager.NonFragmentedMessageMaxSize - UnsafeUtility.SizeOf<NetworkBatchHeader>()) / size) + 1; ++i)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_MessagingSystem.ProcessSendQueues();
|
m_MessageManager.ProcessSendQueues();
|
||||||
Assert.AreEqual(2, m_MessageSender.MessageQueue.Count);
|
Assert.AreEqual(2, m_MessageSender.MessageQueue.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void WhenExceedingMTUSizeWithFragmentedDelivery_NewBatchesAreNotCreated()
|
public void WhenExceedingPerClientBatchSizeLessThanDefault_NewBatchesAreCreated([Values(500, 1000, 1300, 2000)] int maxMessageSize)
|
||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
|
m_MessageManager.NonFragmentedMessageMaxSize = maxMessageSize * 5;
|
||||||
for (var i = 0; i < ((1300 - UnsafeUtility.SizeOf<BatchHeader>()) / size) + 1; ++i)
|
var clients = new ulong[] { 0, 1, 2 };
|
||||||
|
m_MessageManager.ClientConnected(1);
|
||||||
|
m_MessageManager.ClientConnected(2);
|
||||||
|
m_MessageManager.SetVersion(1, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
|
m_MessageManager.SetVersion(2, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < clients.Length; ++i)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, m_Clients);
|
m_MessageManager.PeerMTUSizes[clients[i]] = maxMessageSize * (i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_MessagingSystem.ProcessSendQueues();
|
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
|
||||||
|
for (var i = 0; i < clients.Length; ++i)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < ((m_MessageManager.PeerMTUSizes[clients[i]] - UnsafeUtility.SizeOf<NetworkBatchHeader>()) / size) + 1; ++j)
|
||||||
|
{
|
||||||
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, clients[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_MessageManager.ProcessSendQueues();
|
||||||
|
Assert.AreEqual(2 * clients.Length, m_MessageSender.MessageQueue.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenExceedingPerClientBatchSizeGreaterThanDefault_OnlyOneNewBatcheIsCreated([Values(500, 1000, 1300, 2000)] int maxMessageSize)
|
||||||
|
{
|
||||||
|
var message = GetMessage();
|
||||||
|
m_MessageManager.NonFragmentedMessageMaxSize = 128;
|
||||||
|
var clients = new ulong[] { 0, 1, 2 };
|
||||||
|
m_MessageManager.ClientConnected(1);
|
||||||
|
m_MessageManager.ClientConnected(2);
|
||||||
|
m_MessageManager.SetVersion(1, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
|
m_MessageManager.SetVersion(2, XXHash.Hash32(typeof(TestMessage).FullName), 0);
|
||||||
|
|
||||||
|
for (var i = 0; i < clients.Length; ++i)
|
||||||
|
{
|
||||||
|
m_MessageManager.PeerMTUSizes[clients[i]] = maxMessageSize * (i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
|
||||||
|
for (var i = 0; i < clients.Length; ++i)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < ((m_MessageManager.PeerMTUSizes[clients[i]] - UnsafeUtility.SizeOf<NetworkBatchHeader>()) / size) + 1; ++j)
|
||||||
|
{
|
||||||
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, clients[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_MessageManager.ProcessSendQueues();
|
||||||
|
Assert.AreEqual(2 * clients.Length, m_MessageSender.MessageQueue.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WhenExceedingMTUSizeWithFragmentedDelivery_NewBatchesAreNotCreated([Values(500, 1000, 1300, 2000)] int maxMessageSize)
|
||||||
|
{
|
||||||
|
var message = GetMessage();
|
||||||
|
m_MessageManager.NonFragmentedMessageMaxSize = maxMessageSize;
|
||||||
|
var size = UnsafeUtility.SizeOf<TestMessage>() + 2; // MessageHeader packed with this message will be 2 bytes
|
||||||
|
for (var i = 0; i < ((m_MessageManager.NonFragmentedMessageMaxSize - UnsafeUtility.SizeOf<NetworkBatchHeader>()) / size) + 1; ++i)
|
||||||
|
{
|
||||||
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, m_Clients);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_MessageManager.ProcessSendQueues();
|
||||||
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,11 +258,11 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void WhenSwitchingDelivery_NewBatchesAreCreated()
|
public void WhenSwitchingDelivery_NewBatchesAreCreated()
|
||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Unreliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Unreliable, m_Clients);
|
||||||
|
|
||||||
m_MessagingSystem.ProcessSendQueues();
|
m_MessageManager.ProcessSendQueues();
|
||||||
Assert.AreEqual(2, m_MessageSender.MessageQueue.Count);
|
Assert.AreEqual(2, m_MessageSender.MessageQueue.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,11 +270,11 @@ namespace Unity.Netcode.EditorTests
|
|||||||
public void WhenSwitchingChannel_NewBatchesAreNotCreated()
|
public void WhenSwitchingChannel_NewBatchesAreNotCreated()
|
||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
|
|
||||||
m_MessagingSystem.ProcessSendQueues();
|
m_MessageManager.ProcessSendQueues();
|
||||||
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,22 +283,22 @@ namespace Unity.Netcode.EditorTests
|
|||||||
{
|
{
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
var message2 = GetMessage();
|
var message2 = GetMessage();
|
||||||
m_MessagingSystem.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message, NetworkDelivery.Reliable, m_Clients);
|
||||||
m_MessagingSystem.SendMessage(ref message2, NetworkDelivery.Reliable, m_Clients);
|
m_MessageManager.SendMessage(ref message2, NetworkDelivery.Reliable, m_Clients);
|
||||||
|
|
||||||
m_MessagingSystem.ProcessSendQueues();
|
m_MessageManager.ProcessSendQueues();
|
||||||
var reader = new FastBufferReader(m_MessageSender.MessageQueue[0], Allocator.Temp);
|
var reader = new FastBufferReader(m_MessageSender.MessageQueue[0], Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out BatchHeader header);
|
reader.ReadValueSafe(out NetworkBatchHeader header);
|
||||||
Assert.AreEqual(2, header.BatchCount);
|
Assert.AreEqual(2, header.BatchCount);
|
||||||
|
|
||||||
MessageHeader messageHeader;
|
NetworkMessageHeader messageHeader;
|
||||||
|
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out messageHeader.MessageType);
|
ByteUnpacker.ReadValueBitPacked(reader, out messageHeader.MessageType);
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out messageHeader.MessageSize);
|
ByteUnpacker.ReadValueBitPacked(reader, out messageHeader.MessageSize);
|
||||||
|
|
||||||
Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader.MessageType);
|
Assert.AreEqual(m_MessageManager.GetMessageType(typeof(TestMessage)), messageHeader.MessageType);
|
||||||
Assert.AreEqual(UnsafeUtility.SizeOf<TestMessage>(), messageHeader.MessageSize);
|
Assert.AreEqual(UnsafeUtility.SizeOf<TestMessage>(), messageHeader.MessageSize);
|
||||||
reader.ReadValueSafe(out TestMessage receivedMessage);
|
reader.ReadValueSafe(out TestMessage receivedMessage);
|
||||||
Assert.AreEqual(message, receivedMessage);
|
Assert.AreEqual(message, receivedMessage);
|
||||||
@@ -244,24 +306,24 @@ namespace Unity.Netcode.EditorTests
|
|||||||
ByteUnpacker.ReadValueBitPacked(reader, out messageHeader.MessageType);
|
ByteUnpacker.ReadValueBitPacked(reader, out messageHeader.MessageType);
|
||||||
ByteUnpacker.ReadValueBitPacked(reader, out messageHeader.MessageSize);
|
ByteUnpacker.ReadValueBitPacked(reader, out messageHeader.MessageSize);
|
||||||
|
|
||||||
Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader.MessageType);
|
Assert.AreEqual(m_MessageManager.GetMessageType(typeof(TestMessage)), messageHeader.MessageType);
|
||||||
Assert.AreEqual(UnsafeUtility.SizeOf<TestMessage>(), messageHeader.MessageSize);
|
Assert.AreEqual(UnsafeUtility.SizeOf<TestMessage>(), messageHeader.MessageSize);
|
||||||
reader.ReadValueSafe(out TestMessage receivedMessage2);
|
reader.ReadValueSafe(out TestMessage receivedMessage2);
|
||||||
Assert.AreEqual(message2, receivedMessage2);
|
Assert.AreEqual(message2, receivedMessage2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestNoHandlerMessageProvider : IMessageProvider
|
private class TestNoHandlerMessageProvider : INetworkMessageProvider
|
||||||
{
|
{
|
||||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
public List<NetworkMessageManager.MessageWithHandler> GetMessages()
|
||||||
{
|
{
|
||||||
return new List<MessagingSystem.MessageWithHandler>
|
return new List<NetworkMessageManager.MessageWithHandler>
|
||||||
{
|
{
|
||||||
new MessagingSystem.MessageWithHandler
|
new NetworkMessageManager.MessageWithHandler
|
||||||
{
|
{
|
||||||
MessageType = typeof(TestMessage),
|
MessageType = typeof(TestMessage),
|
||||||
Handler = null,
|
Handler = null,
|
||||||
GetVersion = MessagingSystem.CreateMessageAndGetVersion<TestMessage>
|
GetVersion = NetworkMessageManager.CreateMessageAndGetVersion<TestMessage>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -270,25 +332,26 @@ namespace Unity.Netcode.EditorTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void WhenReceivingAMessageWithoutAHandler_ExceptionIsLogged()
|
public void WhenReceivingAMessageWithoutAHandler_ExceptionIsLogged()
|
||||||
{
|
{
|
||||||
// If a MessagingSystem already exists then dispose of it before creating a new MessagingSystem (otherwise memory leak)
|
// If a NetworkMessageManager already exists then dispose of it before creating a new NetworkMessageManager (otherwise memory leak)
|
||||||
if (m_MessagingSystem != null)
|
if (m_MessageManager != null)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.Dispose();
|
m_MessageManager.Dispose();
|
||||||
m_MessagingSystem = null;
|
m_MessageManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since m_MessagingSystem is disposed during teardown we don't need to worry about that here.
|
// Since m_MessageManager is disposed during teardown we don't need to worry about that here.
|
||||||
m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this, new TestNoHandlerMessageProvider());
|
m_MessageManager = new NetworkMessageManager(new NopMessageSender(), this, new TestNoHandlerMessageProvider());
|
||||||
m_MessagingSystem.ClientConnected(0);
|
m_MessageManager.ClientConnected(0);
|
||||||
|
|
||||||
var messageHeader = new MessageHeader
|
var messageHeader = new NetworkMessageHeader
|
||||||
{
|
{
|
||||||
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
||||||
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
|
MessageType = m_MessageManager.GetMessageType(typeof(TestMessage)),
|
||||||
};
|
};
|
||||||
|
|
||||||
var message = GetMessage();
|
var message = GetMessage();
|
||||||
|
|
||||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
var writer = new FastBufferWriter(m_MessageManager.NonFragmentedMessageMaxSize, Allocator.Temp);
|
||||||
using (writer)
|
using (writer)
|
||||||
{
|
{
|
||||||
writer.TryBeginWrite(FastBufferWriter.GetWriteSize(message));
|
writer.TryBeginWrite(FastBufferWriter.GetWriteSize(message));
|
||||||
@@ -297,7 +360,7 @@ namespace Unity.Netcode.EditorTests
|
|||||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
m_MessagingSystem.HandleMessage(messageHeader, reader, 0, 0, 0);
|
m_MessageManager.HandleMessage(messageHeader, reader, 0, 0, 0);
|
||||||
LogAssert.Expect(LogType.Exception, new Regex(".*HandlerNotRegisteredException.*"));
|
LogAssert.Expect(LogType.Exception, new Regex(".*HandlerNotRegisteredException.*"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user