Compare commits
3 Commits
1.0.0-pre.
...
1.0.0-pre.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b4aaa8b59 | ||
|
|
4818405514 | ||
|
|
36d07fad5e |
78
CHANGELOG.md
78
CHANGELOG.md
@@ -6,6 +6,84 @@ 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.0.0-pre.6] - 2022-03-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- NetworkAnimator now properly synchrhonizes all animation layers as well as runtime-adjusted weighting between them (#1765)
|
||||||
|
- Added first set of tests for NetworkAnimator - parameter syncing, trigger set / reset, override network animator (#1735)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed an issue where sometimes the first client to connect to the server could see messages from the server as coming from itself. (#1683)
|
||||||
|
- Fixed an issue where clients seemed to be able to send messages to ClientId 1, but these messages would actually still go to the server (id 0) instead of that client. (#1683)
|
||||||
|
- Improved clarity of error messaging when a client attempts to send a message to a destination other than the server, which isn't allowed. (#1683)
|
||||||
|
- Disallowed async keyword in RPCs (#1681)
|
||||||
|
- Fixed an issue where Alpha release versions of Unity (version 2022.2.0a5 and later) will not compile due to the UNet Transport no longer existing (#1678)
|
||||||
|
- Fixed messages larger than 64k being written with incorrectly truncated message size in header (#1686) (credit: @kaen)
|
||||||
|
- Fixed overloading RPC methods causing collisions and failing on IL2CPP targets. (#1694)
|
||||||
|
- Fixed spawn flow to propagate `IsSceneObject` down to children NetworkObjects, decouple implicit relationship between object spawning & `IsSceneObject` flag (#1685)
|
||||||
|
- Fixed error when serializing ConnectionApprovalMessage with scene management disabled when one or more objects is hidden via the CheckObjectVisibility delegate (#1720)
|
||||||
|
- Fixed CheckObjectVisibility delegate not being properly invoked for connecting clients when Scene Management is enabled. (#1680)
|
||||||
|
- Fixed NetworkList to properly call INetworkSerializable's NetworkSerialize() method (#1682)
|
||||||
|
- Fixed NetworkVariables containing more than 1300 bytes of data (such as large NetworkLists) no longer cause an OverflowException (the limit on data size is now whatever limit the chosen transport imposes on fragmented NetworkDelivery mechanisms) (#1725)
|
||||||
|
- Fixed ServerRpcParams and ClientRpcParams must be the last parameter of an RPC in order to function properly. Added a compile-time check to ensure this is the case and trigger an error if they're placed elsewhere (#1721)
|
||||||
|
- Fixed FastBufferReader being created with a length of 1 if provided an input of length 0 (#1724)
|
||||||
|
- Fixed The NetworkConfig's checksum hash includes the NetworkTick so that clients with a different tickrate than the server are identified and not allowed to connect (#1728)
|
||||||
|
- Fixed OwnedObjects not being properly modified when using ChangeOwnership (#1731)
|
||||||
|
- Improved performance in NetworkAnimator (#1735)
|
||||||
|
- Removed the "always sync" network animator (aka "autosend") parameters (#1746)
|
||||||
|
|
||||||
|
## [1.0.0-pre.5] - 2022-01-26
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `PreviousValue` in `NetworkListEvent`, when `Value` has changed (#1528)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- NetworkManager's GameObject is no longer allowed to be nested under one or more GameObject(s).(#1484)
|
||||||
|
- NetworkManager DontDestroy property was removed and now NetworkManager always is migrated into the DontDestroyOnLoad scene. (#1484)'
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed network tick value sometimes being duplicated or skipped. (#1614)
|
||||||
|
- Fixed The ClientNetworkTransform sample script to allow for owner changes at runtime. (#1606)
|
||||||
|
- Fixed When the LogLevel is set to developer NetworkBehaviour generates warning messages when it should not (#1631)
|
||||||
|
- Fixed NetworkTransport Initialize now can receive the associated NetworkManager instance to avoid using NetworkManager.Singleton in transport layer (#1677)
|
||||||
|
- Fixed a bug where NetworkList.Contains value was inverted (#1363)
|
||||||
|
|
||||||
|
## [1.0.0-pre.4] - 2021-01-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `com.unity.modules.physics` and `com.unity.modules.physics2d` package dependencies (#1565)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Removed `com.unity.modules.ai` package dependency (#1565)
|
||||||
|
- Removed `FixedQueue`, `StreamExtensions`, `TypeExtensions` (#1398)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed in-scene NetworkObjects that are moved into the DDOL scene not getting restored to their original active state (enabled/disabled) after a full scene transition (#1354)
|
||||||
|
- Fixed invalid IL code being generated when using `this` instead of `this ref` for the FastBufferReader/FastBufferWriter parameter of an extension method. (#1393)
|
||||||
|
- Fixed an issue where if you are running as a server (not host) the LoadEventCompleted and UnloadEventCompleted events would fire early by the NetworkSceneManager (#1379)
|
||||||
|
- Fixed a runtime error when sending an array of an INetworkSerializable type that's implemented as a struct (#1402)
|
||||||
|
- NetworkConfig will no longer throw an OverflowException in GetConfig() when ForceSamePrefabs is enabled and the number of prefabs causes the config blob size to exceed 1300 bytes. (#1385)
|
||||||
|
- Fixed NetworkVariable not calling NetworkSerialize on INetworkSerializable types (#1383)
|
||||||
|
- Fixed NullReferenceException on ImportReferences call in NetworkBehaviourILPP (#1434)
|
||||||
|
- Fixed NetworkObjects not being despawned before they are destroyed during shutdown for client, host, and server instances. (#1390)
|
||||||
|
- Fixed KeyNotFound exception when removing ownership of a newly spawned NetworkObject that is already owned by the server. (#1500)
|
||||||
|
- Fixed NetworkManager.LocalClient not being set when starting as a host. (#1511)
|
||||||
|
- Fixed a few memory leak cases when shutting down NetworkManager during Incoming Message Queue processing. (#1323)
|
||||||
|
- Fixed network tick value sometimes being duplicated or skipped. (#1614)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- The SDK no longer limits message size to 64k. (The transport may still impose its own limits, but the SDK no longer does.) (#1384)
|
||||||
|
- Updated com.unity.collections to 1.1.0 (#1451)
|
||||||
|
- NetworkManager's GameObject is no longer allowed to be nested under one or more GameObject(s).(#1484)
|
||||||
|
- NetworkManager DontDestroy property was removed and now NetworkManager always is migrated into the DontDestroyOnLoad scene. (#1484)
|
||||||
|
|
||||||
## [1.0.0-pre.3] - 2021-10-22
|
## [1.0.0-pre.3] - 2021-10-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private bool InvalidState => m_Buffer.Count == 0 && m_LifetimeConsumedCount == 0;
|
private bool InvalidState => m_Buffer.Count == 0 && m_LifetimeConsumedCount == 0;
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
m_Buffer.Clear();
|
||||||
|
m_EndTimeConsumed = 0.0d;
|
||||||
|
m_StartTimeConsumed = 0.0d;
|
||||||
|
}
|
||||||
|
|
||||||
public void ResetTo(T targetValue, double serverTime)
|
public void ResetTo(T targetValue, double serverTime)
|
||||||
{
|
{
|
||||||
m_LifetimeConsumedCount = 1;
|
m_LifetimeConsumedCount = 1;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode.Components
|
namespace Unity.Netcode.Components
|
||||||
@@ -14,24 +13,19 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
internal struct AnimationMessage : INetworkSerializable
|
internal struct AnimationMessage : INetworkSerializable
|
||||||
{
|
{
|
||||||
public int StateHash; // if non-zero, then Play() this animation, skipping transitions
|
// state hash per layer. if non-zero, then Play() this animation, skipping transitions
|
||||||
|
public int StateHash;
|
||||||
public float NormalizedTime;
|
public float NormalizedTime;
|
||||||
|
public int Layer;
|
||||||
|
public float Weight;
|
||||||
public byte[] Parameters;
|
public byte[] Parameters;
|
||||||
|
|
||||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||||
{
|
{
|
||||||
serializer.SerializeValue(ref StateHash);
|
serializer.SerializeValue(ref StateHash);
|
||||||
serializer.SerializeValue(ref NormalizedTime);
|
serializer.SerializeValue(ref NormalizedTime);
|
||||||
serializer.SerializeValue(ref Parameters);
|
serializer.SerializeValue(ref Layer);
|
||||||
}
|
serializer.SerializeValue(ref Weight);
|
||||||
}
|
|
||||||
|
|
||||||
internal struct AnimationParametersMessage : INetworkSerializable
|
|
||||||
{
|
|
||||||
public byte[] Parameters;
|
|
||||||
|
|
||||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
|
||||||
{
|
|
||||||
serializer.SerializeValue(ref Parameters);
|
serializer.SerializeValue(ref Parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,8 +43,6 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SerializeField] private Animator m_Animator;
|
[SerializeField] private Animator m_Animator;
|
||||||
[SerializeField] private uint m_ParameterSendBits;
|
|
||||||
[SerializeField] private float m_SendRate = 0.1f;
|
|
||||||
|
|
||||||
public Animator Animator
|
public Animator Animator
|
||||||
{
|
{
|
||||||
@@ -58,43 +50,17 @@ namespace Unity.Netcode.Components
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
m_Animator = value;
|
m_Animator = value;
|
||||||
ResetParameterOptions();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
private bool m_SendMessagesAllowed = false;
|
||||||
* AutoSend is the ability to select which parameters linked to this animator
|
|
||||||
* get replicated on a regular basis regardless of a state change. The thinking
|
|
||||||
* behind this is that many of the parameters people use are usually booleans
|
|
||||||
* which result in a state change and thus would cause a full sync of state.
|
|
||||||
* Thus if you really care about a parameter syncing then you need to be explict
|
|
||||||
* by selecting it in the inspector when an NetworkAnimator is selected.
|
|
||||||
*/
|
|
||||||
public void SetParameterAutoSend(int index, bool value)
|
|
||||||
{
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
m_ParameterSendBits |= (uint)(1 << index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_ParameterSendBits &= (uint)(~(1 << index));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetParameterAutoSend(int index)
|
|
||||||
{
|
|
||||||
return (m_ParameterSendBits & (uint)(1 << index)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animators only support up to 32 params
|
// Animators only support up to 32 params
|
||||||
public static int K_MaxAnimationParams = 32;
|
public static int K_MaxAnimationParams = 32;
|
||||||
|
|
||||||
private int m_TransitionHash;
|
private int[] m_TransitionHash;
|
||||||
private double m_NextSendTime = 0.0f;
|
private int[] m_AnimationHash;
|
||||||
|
private float[] m_LayerWeights;
|
||||||
private int m_AnimationHash;
|
|
||||||
public int AnimationHash { get => m_AnimationHash; }
|
|
||||||
|
|
||||||
private unsafe struct AnimatorParamCache
|
private unsafe struct AnimatorParamCache
|
||||||
{
|
{
|
||||||
@@ -103,11 +69,11 @@ namespace Unity.Netcode.Components
|
|||||||
public fixed byte Value[4]; // this is a max size of 4 bytes
|
public fixed byte Value[4]; // this is a max size of 4 bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// 128bytes per Animator
|
// 128 bytes per Animator
|
||||||
private FastBufferWriter m_ParameterWriter = new FastBufferWriter(K_MaxAnimationParams * sizeof(float), Allocator.Persistent);
|
private FastBufferWriter m_ParameterWriter = new FastBufferWriter(K_MaxAnimationParams * sizeof(float), Allocator.Persistent);
|
||||||
private NativeArray<AnimatorParamCache> m_CachedAnimatorParameters;
|
private NativeArray<AnimatorParamCache> m_CachedAnimatorParameters;
|
||||||
|
|
||||||
// We cache these values because UnsafeUtility.EnumToInt use direct IL that allows a nonboxing conversion
|
// We cache these values because UnsafeUtility.EnumToInt uses direct IL that allows a non-boxing conversion
|
||||||
private struct AnimationParamEnumWrapper
|
private struct AnimationParamEnumWrapper
|
||||||
{
|
{
|
||||||
public static readonly int AnimatorControllerParameterInt;
|
public static readonly int AnimatorControllerParameterInt;
|
||||||
@@ -122,25 +88,6 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ResetParameterOptions()
|
|
||||||
{
|
|
||||||
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
|
||||||
{
|
|
||||||
NetworkLog.LogInfoServer("ResetParameterOptions");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ParameterSendBits = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool sendMessagesAllowed
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return IsServer && NetworkObject.IsSpawned;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
if (m_CachedAnimatorParameters.IsCreated)
|
if (m_CachedAnimatorParameters.IsCreated)
|
||||||
@@ -153,25 +100,36 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
public override void OnNetworkSpawn()
|
public override void OnNetworkSpawn()
|
||||||
{
|
{
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
m_SendMessagesAllowed = true;
|
||||||
|
int layers = m_Animator.layerCount;
|
||||||
|
|
||||||
|
m_TransitionHash = new int[layers];
|
||||||
|
m_AnimationHash = new int[layers];
|
||||||
|
m_LayerWeights = new float[layers];
|
||||||
|
}
|
||||||
|
|
||||||
var parameters = m_Animator.parameters;
|
var parameters = m_Animator.parameters;
|
||||||
m_CachedAnimatorParameters = new NativeArray<AnimatorParamCache>(parameters.Length, Allocator.Persistent);
|
m_CachedAnimatorParameters = new NativeArray<AnimatorParamCache>(parameters.Length, Allocator.Persistent);
|
||||||
|
|
||||||
m_AnimationHash = -1;
|
|
||||||
|
|
||||||
for (var i = 0; i < parameters.Length; i++)
|
for (var i = 0; i < parameters.Length; i++)
|
||||||
{
|
{
|
||||||
var parameter = parameters[i];
|
var parameter = parameters[i];
|
||||||
|
|
||||||
if (m_Animator.IsParameterControlledByCurve(parameter.nameHash))
|
if (m_Animator.IsParameterControlledByCurve(parameter.nameHash))
|
||||||
{
|
{
|
||||||
//we are ignoring parameters that are controlled by animation curves - syncing the layer states indirectly syncs the values that are driven by the animation curves
|
// we are ignoring parameters that are controlled by animation curves - syncing the layer
|
||||||
|
// states indirectly syncs the values that are driven by the animation curves
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cacheParam = new AnimatorParamCache();
|
var cacheParam = new AnimatorParamCache
|
||||||
|
{
|
||||||
|
Type = UnsafeUtility.EnumToInt(parameter.type),
|
||||||
|
Hash = parameter.nameHash
|
||||||
|
};
|
||||||
|
|
||||||
cacheParam.Type = UnsafeUtility.EnumToInt(parameter.type);
|
|
||||||
cacheParam.Hash = parameter.nameHash;
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
switch (parameter.type)
|
switch (parameter.type)
|
||||||
@@ -199,115 +157,100 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkDespawn()
|
||||||
|
{
|
||||||
|
m_SendMessagesAllowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
private void FixedUpdate()
|
private void FixedUpdate()
|
||||||
{
|
{
|
||||||
if (!sendMessagesAllowed)
|
if (!m_SendMessagesAllowed || !m_Animator || !m_Animator.enabled)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stateHash;
|
for (int layer = 0; layer < m_Animator.layerCount; layer++)
|
||||||
float normalizedTime;
|
|
||||||
if (!CheckAnimStateChanged(out stateHash, out normalizedTime))
|
|
||||||
{
|
{
|
||||||
// We only want to check and send if we don't have any other state to since
|
int stateHash;
|
||||||
// as we will sync all params as part of the state sync
|
float normalizedTime;
|
||||||
CheckAndSend();
|
if (!CheckAnimStateChanged(out stateHash, out normalizedTime, layer))
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var animMsg = new AnimationMessage();
|
|
||||||
animMsg.StateHash = stateHash;
|
|
||||||
animMsg.NormalizedTime = normalizedTime;
|
|
||||||
|
|
||||||
m_ParameterWriter.Seek(0);
|
|
||||||
m_ParameterWriter.Truncate();
|
|
||||||
|
|
||||||
WriteParameters(m_ParameterWriter, false);
|
|
||||||
animMsg.Parameters = m_ParameterWriter.ToArray();
|
|
||||||
|
|
||||||
SendAnimStateClientRpc(animMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckAndSend()
|
|
||||||
{
|
|
||||||
var networkTime = NetworkManager.ServerTime.Time;
|
|
||||||
if (sendMessagesAllowed && m_SendRate != 0 && m_NextSendTime < networkTime)
|
|
||||||
{
|
|
||||||
m_NextSendTime = networkTime + m_SendRate;
|
|
||||||
|
|
||||||
m_ParameterWriter.Seek(0);
|
|
||||||
m_ParameterWriter.Truncate();
|
|
||||||
|
|
||||||
if (WriteParameters(m_ParameterWriter, true))
|
|
||||||
{
|
|
||||||
// we then sync the params we care about
|
|
||||||
var animMsg = new AnimationParametersMessage()
|
|
||||||
{
|
|
||||||
Parameters = m_ParameterWriter.ToArray()
|
|
||||||
};
|
|
||||||
|
|
||||||
SendParamsClientRpc(animMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CheckAnimStateChanged(out int stateHash, out float normalizedTime)
|
|
||||||
{
|
|
||||||
stateHash = 0;
|
|
||||||
normalizedTime = 0;
|
|
||||||
|
|
||||||
if (m_Animator.IsInTransition(0))
|
|
||||||
{
|
|
||||||
AnimatorTransitionInfo tt = m_Animator.GetAnimatorTransitionInfo(0);
|
|
||||||
if (tt.fullPathHash != m_TransitionHash)
|
|
||||||
{
|
|
||||||
// first time in this transition
|
|
||||||
m_TransitionHash = tt.fullPathHash;
|
|
||||||
m_AnimationHash = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimatorStateInfo st = m_Animator.GetCurrentAnimatorStateInfo(0);
|
|
||||||
if (st.fullPathHash != m_AnimationHash)
|
|
||||||
{
|
|
||||||
// first time in this animation state
|
|
||||||
if (m_AnimationHash != 0)
|
|
||||||
{
|
|
||||||
// came from another animation directly - from Play()
|
|
||||||
stateHash = st.fullPathHash;
|
|
||||||
normalizedTime = st.normalizedTime;
|
|
||||||
}
|
|
||||||
m_TransitionHash = 0;
|
|
||||||
m_AnimationHash = st.fullPathHash;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* $AS TODO: Right now we are not checking for changed values this is because
|
|
||||||
the read side of this function doesn't have similar logic which would cause
|
|
||||||
an overflow read because it doesn't know if the value is there or not. So
|
|
||||||
there needs to be logic to track which indexes changed in order for there
|
|
||||||
to be proper value change checking. Will revist in 1.1.0.
|
|
||||||
*/
|
|
||||||
private unsafe bool WriteParameters(FastBufferWriter writer, bool autoSend)
|
|
||||||
{
|
|
||||||
if (m_CachedAnimatorParameters == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < m_CachedAnimatorParameters.Length; i++)
|
|
||||||
{
|
|
||||||
if (autoSend && !GetParameterAutoSend(i))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var animMsg = new AnimationMessage
|
||||||
|
{
|
||||||
|
StateHash = stateHash,
|
||||||
|
NormalizedTime = normalizedTime,
|
||||||
|
Layer = layer,
|
||||||
|
Weight = m_LayerWeights[layer]
|
||||||
|
};
|
||||||
|
|
||||||
|
m_ParameterWriter.Seek(0);
|
||||||
|
m_ParameterWriter.Truncate();
|
||||||
|
|
||||||
|
WriteParameters(m_ParameterWriter);
|
||||||
|
animMsg.Parameters = m_ParameterWriter.ToArray();
|
||||||
|
|
||||||
|
SendAnimStateClientRpc(animMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckAnimStateChanged(out int stateHash, out float normalizedTime, int layer)
|
||||||
|
{
|
||||||
|
bool shouldUpdate = false;
|
||||||
|
stateHash = 0;
|
||||||
|
normalizedTime = 0;
|
||||||
|
|
||||||
|
float layerWeightNow = m_Animator.GetLayerWeight(layer);
|
||||||
|
|
||||||
|
if (!Mathf.Approximately(layerWeightNow, m_LayerWeights[layer]))
|
||||||
|
{
|
||||||
|
m_LayerWeights[layer] = layerWeightNow;
|
||||||
|
shouldUpdate = true;
|
||||||
|
}
|
||||||
|
if (m_Animator.IsInTransition(layer))
|
||||||
|
{
|
||||||
|
AnimatorTransitionInfo tt = m_Animator.GetAnimatorTransitionInfo(layer);
|
||||||
|
if (tt.fullPathHash != m_TransitionHash[layer])
|
||||||
|
{
|
||||||
|
// first time in this transition for this layer
|
||||||
|
m_TransitionHash[layer] = tt.fullPathHash;
|
||||||
|
m_AnimationHash[layer] = 0;
|
||||||
|
shouldUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AnimatorStateInfo st = m_Animator.GetCurrentAnimatorStateInfo(layer);
|
||||||
|
if (st.fullPathHash != m_AnimationHash[layer])
|
||||||
|
{
|
||||||
|
// first time in this animation state
|
||||||
|
if (m_AnimationHash[layer] != 0)
|
||||||
|
{
|
||||||
|
// came from another animation directly - from Play()
|
||||||
|
stateHash = st.fullPathHash;
|
||||||
|
normalizedTime = st.normalizedTime;
|
||||||
|
}
|
||||||
|
m_TransitionHash[layer] = 0;
|
||||||
|
m_AnimationHash[layer] = st.fullPathHash;
|
||||||
|
shouldUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shouldUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* $AS TODO: Right now we are not checking for changed values this is because
|
||||||
|
the read side of this function doesn't have similar logic which would cause
|
||||||
|
an overflow read because it doesn't know if the value is there or not. So
|
||||||
|
there needs to be logic to track which indexes changed in order for there
|
||||||
|
to be proper value change checking. Will revist in 1.1.0.
|
||||||
|
*/
|
||||||
|
private unsafe void WriteParameters(FastBufferWriter writer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_CachedAnimatorParameters.Length; i++)
|
||||||
|
{
|
||||||
ref var cacheValue = ref UnsafeUtility.ArrayElementAsRef<AnimatorParamCache>(m_CachedAnimatorParameters.GetUnsafePtr(), i);
|
ref var cacheValue = ref UnsafeUtility.ArrayElementAsRef<AnimatorParamCache>(m_CachedAnimatorParameters.GetUnsafePtr(), i);
|
||||||
var hash = cacheValue.Hash;
|
var hash = cacheValue.Hash;
|
||||||
|
|
||||||
@@ -340,24 +283,12 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we do not write any values to the writer then we should not send any data
|
|
||||||
return writer.Length > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void ReadParameters(FastBufferReader reader, bool autoSend)
|
private unsafe void ReadParameters(FastBufferReader reader)
|
||||||
{
|
{
|
||||||
if (m_CachedAnimatorParameters == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < m_CachedAnimatorParameters.Length; i++)
|
for (int i = 0; i < m_CachedAnimatorParameters.Length; i++)
|
||||||
{
|
{
|
||||||
if (autoSend && !GetParameterAutoSend(i))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ref var cacheValue = ref UnsafeUtility.ArrayElementAsRef<AnimatorParamCache>(m_CachedAnimatorParameters.GetUnsafePtr(), i);
|
ref var cacheValue = ref UnsafeUtility.ArrayElementAsRef<AnimatorParamCache>(m_CachedAnimatorParameters.GetUnsafePtr(), i);
|
||||||
var hash = cacheValue.Hash;
|
var hash = cacheValue.Hash;
|
||||||
|
|
||||||
@@ -391,28 +322,20 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[ClientRpc]
|
/// <summary>
|
||||||
private unsafe void SendParamsClientRpc(AnimationParametersMessage animSnapshot, ClientRpcParams clientRpcParams = default)
|
/// Internally-called RPC client receiving function to update some animation parameters on a client when
|
||||||
{
|
/// the server wants to update them
|
||||||
if (animSnapshot.Parameters != null)
|
/// </summary>
|
||||||
{
|
/// <param name="animSnapshot">the payload containing the parameters to apply</param>
|
||||||
// We use a fixed value here to avoid the copy of data from the byte buffer since we own the data
|
/// <param name="clientRpcParams">unused</param>
|
||||||
fixed (byte* parameters = animSnapshot.Parameters)
|
|
||||||
{
|
|
||||||
var reader = new FastBufferReader(parameters, Allocator.None, animSnapshot.Parameters.Length);
|
|
||||||
ReadParameters(reader, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ClientRpc]
|
[ClientRpc]
|
||||||
private unsafe void SendAnimStateClientRpc(AnimationMessage animSnapshot, ClientRpcParams clientRpcParams = default)
|
private unsafe void SendAnimStateClientRpc(AnimationMessage animSnapshot, ClientRpcParams clientRpcParams = default)
|
||||||
{
|
{
|
||||||
if (animSnapshot.StateHash != 0)
|
if (animSnapshot.StateHash != 0)
|
||||||
{
|
{
|
||||||
m_AnimationHash = animSnapshot.StateHash;
|
m_Animator.Play(animSnapshot.StateHash, animSnapshot.Layer, animSnapshot.NormalizedTime);
|
||||||
m_Animator.Play(animSnapshot.StateHash, 0, animSnapshot.NormalizedTime);
|
|
||||||
}
|
}
|
||||||
|
m_Animator.SetLayerWeight(animSnapshot.Layer, animSnapshot.Weight);
|
||||||
|
|
||||||
if (animSnapshot.Parameters != null && animSnapshot.Parameters.Length != 0)
|
if (animSnapshot.Parameters != null && animSnapshot.Parameters.Length != 0)
|
||||||
{
|
{
|
||||||
@@ -420,11 +343,17 @@ namespace Unity.Netcode.Components
|
|||||||
fixed (byte* parameters = animSnapshot.Parameters)
|
fixed (byte* parameters = animSnapshot.Parameters)
|
||||||
{
|
{
|
||||||
var reader = new FastBufferReader(parameters, Allocator.None, animSnapshot.Parameters.Length);
|
var reader = new FastBufferReader(parameters, Allocator.None, animSnapshot.Parameters.Length);
|
||||||
ReadParameters(reader, false);
|
ReadParameters(reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internally-called RPC client receiving function to update a trigger when the server wants to forward
|
||||||
|
/// a trigger for a client to play / reset
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="animSnapshot">the payload containing the trigger data to apply</param>
|
||||||
|
/// <param name="clientRpcParams">unused</param>
|
||||||
[ClientRpc]
|
[ClientRpc]
|
||||||
private void SendAnimTriggerClientRpc(AnimationTriggerMessage animSnapshot, ClientRpcParams clientRpcParams = default)
|
private void SendAnimTriggerClientRpc(AnimationTriggerMessage animSnapshot, ClientRpcParams clientRpcParams = default)
|
||||||
{
|
{
|
||||||
@@ -438,11 +367,24 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the trigger for the associated animation
|
||||||
|
/// Note, triggers are special vs other kinds of parameters. For all the other parameters we watch for changes
|
||||||
|
/// in FixedUpdate and users can just set them normally off of Animator. But because triggers are transitory
|
||||||
|
/// and likely to come and go between FixedUpdate calls, we require users to set them here to guarantee us to
|
||||||
|
/// catch it...then we forward it to the Animator component
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="triggerName">The string name of the trigger to activate</param>
|
||||||
public void SetTrigger(string triggerName)
|
public void SetTrigger(string triggerName)
|
||||||
{
|
{
|
||||||
SetTrigger(Animator.StringToHash(triggerName));
|
SetTrigger(Animator.StringToHash(triggerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the trigger for the associated animation. See note for SetTrigger(string)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hash">The hash for the trigger to activate</param>
|
||||||
|
/// <param name="reset">If true, resets the trigger</param>
|
||||||
public void SetTrigger(int hash, bool reset = false)
|
public void SetTrigger(int hash, bool reset = false)
|
||||||
{
|
{
|
||||||
var animMsg = new AnimationTriggerMessage();
|
var animMsg = new AnimationTriggerMessage();
|
||||||
@@ -451,15 +393,38 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
if (IsServer)
|
if (IsServer)
|
||||||
{
|
{
|
||||||
|
// trigger the animation locally on the server...
|
||||||
|
if (reset)
|
||||||
|
{
|
||||||
|
m_Animator.ResetTrigger(hash);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_Animator.SetTrigger(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...then tell all the clients to do the same
|
||||||
SendAnimTriggerClientRpc(animMsg);
|
SendAnimTriggerClientRpc(animMsg);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Trying to call NetworkAnimator.SetTrigger on a client...ignoring");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the trigger for the associated animation. See note for SetTrigger(string)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="triggerName">The string name of the trigger to reset</param>
|
||||||
public void ResetTrigger(string triggerName)
|
public void ResetTrigger(string triggerName)
|
||||||
{
|
{
|
||||||
ResetTrigger(Animator.StringToHash(triggerName));
|
ResetTrigger(Animator.StringToHash(triggerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the trigger for the associated animation. See note for SetTrigger(string)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hash">The hash for the trigger to reset</param>
|
||||||
public void ResetTrigger(int hash)
|
public void ResetTrigger(int hash)
|
||||||
{
|
{
|
||||||
SetTrigger(hash, true);
|
SetTrigger(hash, true);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
private void FixedUpdate()
|
private void FixedUpdate()
|
||||||
{
|
{
|
||||||
if (NetworkManager.IsListening)
|
if (IsSpawned)
|
||||||
{
|
{
|
||||||
if (HasAuthority != m_IsAuthority)
|
if (HasAuthority != m_IsAuthority)
|
||||||
{
|
{
|
||||||
@@ -74,7 +74,6 @@ namespace Unity.Netcode.Components
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnNetworkDespawn()
|
public override void OnNetworkDespawn()
|
||||||
{
|
{
|
||||||
m_IsAuthority = false;
|
|
||||||
UpdateRigidbodyKinematicMode();
|
UpdateRigidbodyKinematicMode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,8 +260,10 @@ namespace Unity.Netcode.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Tooltip("Sets whether this transform should sync in local space or in world space")]
|
[Tooltip("Sets whether this transform should sync in local space or in world space")]
|
||||||
public bool InLocalSpace = false;
|
public bool InLocalSpace = false;
|
||||||
|
private bool m_LastInterpolateLocal = false; // was the last frame local
|
||||||
|
|
||||||
public bool Interpolate = true;
|
public bool Interpolate = true;
|
||||||
|
private bool m_LastInterpolate = true; // was the last frame interpolated
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to determine who can write to this transform. Server only for this transform.
|
/// Used to determine who can write to this transform. Server only for this transform.
|
||||||
@@ -367,7 +369,11 @@ namespace Unity.Netcode.Components
|
|||||||
private void CommitLocallyAndReplicate(NetworkTransformState networkState)
|
private void CommitLocallyAndReplicate(NetworkTransformState networkState)
|
||||||
{
|
{
|
||||||
m_ReplicatedNetworkState.Value = networkState;
|
m_ReplicatedNetworkState.Value = networkState;
|
||||||
AddInterpolatedState(networkState);
|
|
||||||
|
if (Interpolate)
|
||||||
|
{
|
||||||
|
AddInterpolatedState(networkState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetInterpolatedStateToCurrentAuthoritativeState()
|
private void ResetInterpolatedStateToCurrentAuthoritativeState()
|
||||||
@@ -532,7 +538,12 @@ namespace Unity.Netcode.Components
|
|||||||
// again, we should be using quats here
|
// again, we should be using quats here
|
||||||
if (SyncRotAngleX || SyncRotAngleY || SyncRotAngleZ)
|
if (SyncRotAngleX || SyncRotAngleY || SyncRotAngleZ)
|
||||||
{
|
{
|
||||||
var eulerAngles = m_RotationInterpolator.GetInterpolatedValue().eulerAngles;
|
var eulerAngles = new Vector3();
|
||||||
|
if (Interpolate)
|
||||||
|
{
|
||||||
|
eulerAngles = m_RotationInterpolator.GetInterpolatedValue().eulerAngles;
|
||||||
|
}
|
||||||
|
|
||||||
if (SyncRotAngleX)
|
if (SyncRotAngleX)
|
||||||
{
|
{
|
||||||
interpolatedRotAngles.x = networkState.IsTeleportingNextFrame || !Interpolate ? networkState.Rotation.x : eulerAngles.x;
|
interpolatedRotAngles.x = networkState.IsTeleportingNextFrame || !Interpolate ? networkState.Rotation.x : eulerAngles.x;
|
||||||
@@ -603,10 +614,46 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddInterpolatedState(NetworkTransformState newState)
|
private void AddInterpolatedState(NetworkTransformState newState, bool reset = false)
|
||||||
{
|
{
|
||||||
var sentTime = newState.SentTime;
|
var sentTime = newState.SentTime;
|
||||||
|
|
||||||
|
if (reset)
|
||||||
|
{
|
||||||
|
if (newState.HasPositionX)
|
||||||
|
{
|
||||||
|
m_PositionXInterpolator.ResetTo(newState.PositionX, sentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState.HasPositionY)
|
||||||
|
{
|
||||||
|
m_PositionYInterpolator.ResetTo(newState.PositionY, sentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState.HasPositionZ)
|
||||||
|
{
|
||||||
|
m_PositionZInterpolator.ResetTo(newState.PositionZ, sentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_RotationInterpolator.ResetTo(Quaternion.Euler(newState.Rotation), sentTime);
|
||||||
|
|
||||||
|
if (newState.HasScaleX)
|
||||||
|
{
|
||||||
|
m_ScaleXInterpolator.ResetTo(newState.ScaleX, sentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState.HasScaleY)
|
||||||
|
{
|
||||||
|
m_ScaleYInterpolator.ResetTo(newState.ScaleY, sentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newState.HasScaleZ)
|
||||||
|
{
|
||||||
|
m_ScaleZInterpolator.ResetTo(newState.ScaleZ, sentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (newState.HasPositionX)
|
if (newState.HasPositionX)
|
||||||
{
|
{
|
||||||
m_PositionXInterpolator.AddMeasurement(newState.PositionX, sentTime);
|
m_PositionXInterpolator.AddMeasurement(newState.PositionX, sentTime);
|
||||||
@@ -656,7 +703,11 @@ namespace Unity.Netcode.Components
|
|||||||
|
|
||||||
Debug.DrawLine(newState.Position, newState.Position + Vector3.up + Vector3.left, Color.green, 10, false);
|
Debug.DrawLine(newState.Position, newState.Position + Vector3.up + Vector3.left, Color.green, 10, false);
|
||||||
|
|
||||||
AddInterpolatedState(newState);
|
if (Interpolate)
|
||||||
|
{
|
||||||
|
AddInterpolatedState(newState, (newState.InLocalSpace != m_LastInterpolateLocal));
|
||||||
|
}
|
||||||
|
m_LastInterpolateLocal = newState.InLocalSpace;
|
||||||
|
|
||||||
if (m_CachedNetworkManager.LogLevel == LogLevel.Developer)
|
if (m_CachedNetworkManager.LogLevel == LogLevel.Developer)
|
||||||
{
|
{
|
||||||
@@ -807,11 +858,22 @@ namespace Unity.Netcode.Components
|
|||||||
// conditional to users only making transform update changes in FixedUpdate.
|
// conditional to users only making transform update changes in FixedUpdate.
|
||||||
protected virtual void Update()
|
protected virtual void Update()
|
||||||
{
|
{
|
||||||
if (!NetworkObject.IsSpawned)
|
if (!IsSpawned)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Interpolate && m_LastInterpolate)
|
||||||
|
{
|
||||||
|
// if we just stopped interpolating, let's clear the interpolators
|
||||||
|
foreach (var interpolator in m_AllFloatInterpolators)
|
||||||
|
{
|
||||||
|
interpolator.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_LastInterpolate = Interpolate;
|
||||||
|
|
||||||
if (CanCommitToTransform)
|
if (CanCommitToTransform)
|
||||||
{
|
{
|
||||||
if (m_CachedIsServer)
|
if (m_CachedIsServer)
|
||||||
@@ -831,12 +893,15 @@ namespace Unity.Netcode.Components
|
|||||||
var cachedServerTime = serverTime.Time;
|
var cachedServerTime = serverTime.Time;
|
||||||
var cachedRenderTime = serverTime.TimeTicksAgo(1).Time;
|
var cachedRenderTime = serverTime.TimeTicksAgo(1).Time;
|
||||||
|
|
||||||
foreach (var interpolator in m_AllFloatInterpolators)
|
if (Interpolate)
|
||||||
{
|
{
|
||||||
interpolator.Update(cachedDeltaTime, cachedRenderTime, cachedServerTime);
|
foreach (var interpolator in m_AllFloatInterpolators)
|
||||||
}
|
{
|
||||||
|
interpolator.Update(cachedDeltaTime, cachedRenderTime, cachedServerTime);
|
||||||
|
}
|
||||||
|
|
||||||
m_RotationInterpolator.Update(cachedDeltaTime, cachedRenderTime, cachedServerTime);
|
m_RotationInterpolator.Update(cachedDeltaTime, cachedRenderTime, cachedServerTime);
|
||||||
|
}
|
||||||
|
|
||||||
if (!CanCommitToTransform)
|
if (!CanCommitToTransform)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,13 +5,5 @@
|
|||||||
"Unity.Netcode.Runtime",
|
"Unity.Netcode.Runtime",
|
||||||
"Unity.Collections"
|
"Unity.Collections"
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
"allowUnsafeCode": true
|
||||||
"excludePlatforms": [],
|
|
||||||
"allowUnsafeCode": true,
|
|
||||||
"overrideReferences": false,
|
|
||||||
"precompiledReferences": [],
|
|
||||||
"autoReferenced": true,
|
|
||||||
"defineConstraints": [],
|
|
||||||
"versionDefines": [],
|
|
||||||
"noEngineReferences": false
|
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,10 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
public static readonly string ClientRpcAttribute_FullName = typeof(ClientRpcAttribute).FullName;
|
public static readonly string ClientRpcAttribute_FullName = typeof(ClientRpcAttribute).FullName;
|
||||||
public static readonly string ServerRpcParams_FullName = typeof(ServerRpcParams).FullName;
|
public static readonly string ServerRpcParams_FullName = typeof(ServerRpcParams).FullName;
|
||||||
public static readonly string ClientRpcParams_FullName = typeof(ClientRpcParams).FullName;
|
public static readonly string ClientRpcParams_FullName = typeof(ClientRpcParams).FullName;
|
||||||
|
public static readonly string ClientRpcSendParams_FullName = typeof(ClientRpcSendParams).FullName;
|
||||||
|
public static readonly string ClientRpcReceiveParams_FullName = typeof(ClientRpcReceiveParams).FullName;
|
||||||
|
public static readonly string ServerRpcSendParams_FullName = typeof(ServerRpcSendParams).FullName;
|
||||||
|
public static readonly string ServerRpcReceiveParams_FullName = typeof(ServerRpcReceiveParams).FullName;
|
||||||
public static readonly string INetworkSerializable_FullName = typeof(INetworkSerializable).FullName;
|
public static readonly string INetworkSerializable_FullName = typeof(INetworkSerializable).FullName;
|
||||||
public static readonly string UnityColor_FullName = typeof(Color).FullName;
|
public static readonly string UnityColor_FullName = typeof(Color).FullName;
|
||||||
public static readonly string UnityColor32_FullName = typeof(Color32).FullName;
|
public static readonly string UnityColor32_FullName = typeof(Color32).FullName;
|
||||||
@@ -264,6 +268,28 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddWarning(this List<DiagnosticMessage> diagnostics, string message)
|
||||||
|
{
|
||||||
|
diagnostics.AddWarning((SequencePoint)null, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddWarning(this List<DiagnosticMessage> diagnostics, MethodDefinition methodDefinition, string message)
|
||||||
|
{
|
||||||
|
diagnostics.AddWarning(methodDefinition.DebugInformation.SequencePoints.FirstOrDefault(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddWarning(this List<DiagnosticMessage> diagnostics, SequencePoint sequencePoint, string message)
|
||||||
|
{
|
||||||
|
diagnostics.Add(new DiagnosticMessage
|
||||||
|
{
|
||||||
|
DiagnosticType = DiagnosticType.Warning,
|
||||||
|
File = sequencePoint?.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""),
|
||||||
|
Line = sequencePoint?.StartLine ?? 0,
|
||||||
|
Column = sequencePoint?.StartColumn ?? 0,
|
||||||
|
MessageData = $" - {message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void RemoveRecursiveReferences(this ModuleDefinition moduleDefinition)
|
public static void RemoveRecursiveReferences(this ModuleDefinition moduleDefinition)
|
||||||
{
|
{
|
||||||
// Weird behavior from Cecil: When importing a reference to a specific implementation of a generic
|
// Weird behavior from Cecil: When importing a reference to a specific implementation of a generic
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ using MethodAttributes = Mono.Cecil.MethodAttributes;
|
|||||||
|
|
||||||
namespace Unity.Netcode.Editor.CodeGen
|
namespace Unity.Netcode.Editor.CodeGen
|
||||||
{
|
{
|
||||||
|
|
||||||
internal sealed class INetworkMessageILPP : ILPPInterface
|
internal sealed class INetworkMessageILPP : ILPPInterface
|
||||||
{
|
{
|
||||||
public override ILPPInterface GetInstance() => this;
|
public override ILPPInterface GetInstance() => this;
|
||||||
@@ -31,7 +30,6 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
m_Diagnostics.Clear();
|
m_Diagnostics.Clear();
|
||||||
|
|
||||||
// read
|
// read
|
||||||
@@ -95,27 +93,23 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private TypeReference m_FastBufferReader_TypeRef;
|
private MethodReference m_MessagingSystem_ReceiveMessage_MethodRef;
|
||||||
private TypeReference m_NetworkContext_TypeRef;
|
|
||||||
private TypeReference m_MessagingSystem_MessageWithHandler_TypeRef;
|
private TypeReference m_MessagingSystem_MessageWithHandler_TypeRef;
|
||||||
private MethodReference m_MessagingSystem_MessageHandler_Constructor_TypeRef;
|
private MethodReference m_MessagingSystem_MessageHandler_Constructor_TypeRef;
|
||||||
private FieldReference m_ILPPMessageProvider___network_message_types_FieldRef;
|
private FieldReference m_ILPPMessageProvider___network_message_types_FieldRef;
|
||||||
private FieldReference m_MessagingSystem_MessageWithHandler_MessageType_FieldRef;
|
private FieldReference m_MessagingSystem_MessageWithHandler_MessageType_FieldRef;
|
||||||
private FieldReference m_MessagingSystem_MessageWithHandler_Handler_FieldRef;
|
private FieldReference m_MessagingSystem_MessageWithHandler_Handler_FieldRef;
|
||||||
private 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 bool ImportReferences(ModuleDefinition moduleDefinition)
|
private bool ImportReferences(ModuleDefinition moduleDefinition)
|
||||||
{
|
{
|
||||||
m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(typeof(FastBufferReader));
|
m_MessagingSystem_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(typeof(MessagingSystem.MessageHandler).GetConstructors()[0]);
|
||||||
m_NetworkContext_TypeRef = moduleDefinition.ImportReference(typeof(NetworkContext));
|
|
||||||
m_MessagingSystem_MessageHandler_Constructor_TypeRef =
|
|
||||||
moduleDefinition.ImportReference(typeof(MessagingSystem.MessageHandler).GetConstructors()[0]);
|
|
||||||
|
|
||||||
var messageWithHandlerType = typeof(MessagingSystem.MessageWithHandler);
|
var messageWithHandlerType = typeof(MessagingSystem.MessageWithHandler);
|
||||||
m_MessagingSystem_MessageWithHandler_TypeRef =
|
m_MessagingSystem_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerType);
|
||||||
moduleDefinition.ImportReference(messageWithHandlerType);
|
|
||||||
foreach (var fieldInfo in messageWithHandlerType.GetFields())
|
foreach (var fieldInfo in messageWithHandlerType.GetFields())
|
||||||
{
|
{
|
||||||
switch (fieldInfo.Name)
|
switch (fieldInfo.Name)
|
||||||
@@ -162,38 +156,18 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var messagingSystemType = typeof(MessagingSystem);
|
||||||
return true;
|
foreach (var methodInfo in messagingSystemType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
|
||||||
}
|
|
||||||
|
|
||||||
private MethodReference GetNetworkMessageRecieveHandler(TypeDefinition typeDefinition)
|
|
||||||
{
|
|
||||||
SequencePoint typeSequence = null;
|
|
||||||
foreach (var method in typeDefinition.Methods)
|
|
||||||
{
|
{
|
||||||
var resolved = method.Resolve();
|
switch (methodInfo.Name)
|
||||||
var methodSequence = resolved.DebugInformation.SequencePoints.FirstOrDefault();
|
|
||||||
if (typeSequence == null || methodSequence.StartLine < typeSequence.StartLine)
|
|
||||||
{
|
{
|
||||||
typeSequence = methodSequence;
|
case k_ReceiveMessageName:
|
||||||
}
|
m_MessagingSystem_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||||
|
break;
|
||||||
if (resolved.IsStatic && resolved.IsPublic && resolved.Name == "Receive" && resolved.Parameters.Count == 2
|
|
||||||
&& !resolved.Parameters[0].IsIn
|
|
||||||
&& !resolved.Parameters[0].ParameterType.IsByReference
|
|
||||||
&& resolved.Parameters[0].ParameterType.Resolve() ==
|
|
||||||
m_FastBufferReader_TypeRef.Resolve()
|
|
||||||
&& resolved.Parameters[1].IsIn
|
|
||||||
&& resolved.Parameters[1].ParameterType.IsByReference
|
|
||||||
&& resolved.Parameters[1].ParameterType.GetElementType().Resolve() == m_NetworkContext_TypeRef.Resolve()
|
|
||||||
&& resolved.ReturnType == resolved.Module.TypeSystem.Void)
|
|
||||||
{
|
|
||||||
return method;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Diagnostics.AddError(typeSequence, $"Class {typeDefinition.FullName} does not implement required method: `public static void Receive(FastBufferReader, in NetworkContext)`");
|
return true;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodDefinition GetOrCreateStaticConstructor(TypeDefinition typeDefinition)
|
private MethodDefinition GetOrCreateStaticConstructor(TypeDefinition typeDefinition)
|
||||||
@@ -264,11 +238,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
foreach (var type in networkMessageTypes)
|
foreach (var type in networkMessageTypes)
|
||||||
{
|
{
|
||||||
var receiveMethod = GetNetworkMessageRecieveHandler(type);
|
var receiveMethod = new GenericInstanceMethod(m_MessagingSystem_ReceiveMessage_MethodRef);
|
||||||
if (receiveMethod == null)
|
receiveMethod.GenericArguments.Add(type);
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CreateInstructionsToRegisterType(processor, instructions, type, receiveMethod);
|
CreateInstructionsToRegisterType(processor, instructions, type, receiveMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
167
Editor/CodeGen/INetworkSerializableILPP.cs
Normal file
167
Editor/CodeGen/INetworkSerializableILPP.cs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Rocks;
|
||||||
|
using Unity.CompilationPipeline.Common.Diagnostics;
|
||||||
|
using Unity.CompilationPipeline.Common.ILPostProcessing;
|
||||||
|
using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostProcessor;
|
||||||
|
using MethodAttributes = Mono.Cecil.MethodAttributes;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Editor.CodeGen
|
||||||
|
{
|
||||||
|
|
||||||
|
internal sealed class INetworkSerializableILPP : ILPPInterface
|
||||||
|
{
|
||||||
|
public override ILPPInterface GetInstance() => this;
|
||||||
|
|
||||||
|
public override bool WillProcess(ICompiledAssembly compiledAssembly) =>
|
||||||
|
compiledAssembly.Name == CodeGenHelpers.RuntimeAssemblyName ||
|
||||||
|
compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == CodeGenHelpers.RuntimeAssemblyName);
|
||||||
|
|
||||||
|
private readonly List<DiagnosticMessage> m_Diagnostics = new List<DiagnosticMessage>();
|
||||||
|
|
||||||
|
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly)
|
||||||
|
{
|
||||||
|
if (!WillProcess(compiledAssembly))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
m_Diagnostics.Clear();
|
||||||
|
|
||||||
|
// read
|
||||||
|
var assemblyDefinition = CodeGenHelpers.AssemblyDefinitionFor(compiledAssembly, out var resolver);
|
||||||
|
if (assemblyDefinition == null)
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError($"Cannot read assembly definition: {compiledAssembly.Name}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process
|
||||||
|
var mainModule = assemblyDefinition.MainModule;
|
||||||
|
if (mainModule != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (ImportReferences(mainModule))
|
||||||
|
{
|
||||||
|
var types = mainModule.GetTypes()
|
||||||
|
.Where(t => t.Resolve().HasInterface(CodeGenHelpers.INetworkSerializable_FullName) && !t.Resolve().IsAbstract && t.Resolve().IsValueType)
|
||||||
|
.ToList();
|
||||||
|
// process `INetworkMessage` types
|
||||||
|
if (types.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateModuleInitializer(assemblyDefinition, types);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError($"Cannot import references into main module: {mainModule.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError((e.ToString() + e.StackTrace.ToString()).Replace("\n", "|").Replace("\r", "|"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError($"Cannot get main module from assembly definition: {compiledAssembly.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
mainModule.RemoveRecursiveReferences();
|
||||||
|
|
||||||
|
// write
|
||||||
|
var pe = new MemoryStream();
|
||||||
|
var pdb = new MemoryStream();
|
||||||
|
|
||||||
|
var writerParameters = new WriterParameters
|
||||||
|
{
|
||||||
|
SymbolWriterProvider = new PortablePdbWriterProvider(),
|
||||||
|
SymbolStream = pdb,
|
||||||
|
WriteSymbols = true
|
||||||
|
};
|
||||||
|
|
||||||
|
assemblyDefinition.Write(pe, writerParameters);
|
||||||
|
|
||||||
|
return new ILPostProcessResult(new InMemoryAssembly(pe.ToArray(), pdb.ToArray()), m_Diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodReference m_InitializeDelegates_MethodRef;
|
||||||
|
|
||||||
|
private const string k_InitializeMethodName = nameof(NetworkVariableHelper.InitializeDelegates);
|
||||||
|
|
||||||
|
private bool ImportReferences(ModuleDefinition moduleDefinition)
|
||||||
|
{
|
||||||
|
|
||||||
|
var helperType = typeof(NetworkVariableHelper);
|
||||||
|
foreach (var methodInfo in helperType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
|
||||||
|
{
|
||||||
|
switch (methodInfo.Name)
|
||||||
|
{
|
||||||
|
case k_InitializeMethodName:
|
||||||
|
m_InitializeDelegates_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a static module constructor (which is executed when the module is loaded) that registers all the
|
||||||
|
// message types in the assembly with MessagingSystem.
|
||||||
|
// 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://web.archive.org/web/20100212140402/http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
|
||||||
|
private void CreateModuleInitializer(AssemblyDefinition assembly, List<TypeDefinition> networkSerializableTypes)
|
||||||
|
{
|
||||||
|
foreach (var typeDefinition in assembly.MainModule.Types)
|
||||||
|
{
|
||||||
|
if (typeDefinition.FullName == "<Module>")
|
||||||
|
{
|
||||||
|
var staticCtorMethodDef = GetOrCreateStaticConstructor(typeDefinition);
|
||||||
|
|
||||||
|
var processor = staticCtorMethodDef.Body.GetILProcessor();
|
||||||
|
|
||||||
|
var instructions = new List<Instruction>();
|
||||||
|
|
||||||
|
foreach (var type in networkSerializableTypes)
|
||||||
|
{
|
||||||
|
var method = new GenericInstanceMethod(m_InitializeDelegates_MethodRef);
|
||||||
|
method.GenericArguments.Add(type);
|
||||||
|
instructions.Add(processor.Create(OpCodes.Call, method));
|
||||||
|
}
|
||||||
|
|
||||||
|
instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Editor/CodeGen/INetworkSerializableILPP.cs.meta
Normal file
3
Editor/CodeGen/INetworkSerializableILPP.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 64a0c1e708fa46a389d64e7b4708e6c7
|
||||||
|
timeCreated: 1635535237
|
||||||
@@ -7,7 +7,6 @@ using System.Runtime.CompilerServices;
|
|||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using Mono.Cecil.Rocks;
|
using Mono.Cecil.Rocks;
|
||||||
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;
|
||||||
@@ -17,7 +16,6 @@ using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostPr
|
|||||||
|
|
||||||
namespace Unity.Netcode.Editor.CodeGen
|
namespace Unity.Netcode.Editor.CodeGen
|
||||||
{
|
{
|
||||||
|
|
||||||
internal sealed class NetworkBehaviourILPP : ILPPInterface
|
internal sealed class NetworkBehaviourILPP : ILPPInterface
|
||||||
{
|
{
|
||||||
private const string k_ReadValueMethodName = nameof(FastBufferReader.ReadValueSafe);
|
private const string k_ReadValueMethodName = nameof(FastBufferReader.ReadValueSafe);
|
||||||
@@ -25,7 +23,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
public override ILPPInterface GetInstance() => this;
|
public override ILPPInterface GetInstance() => this;
|
||||||
|
|
||||||
public override bool WillProcess(ICompiledAssembly compiledAssembly) => compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == CodeGenHelpers.RuntimeAssemblyName);
|
public override bool WillProcess(ICompiledAssembly compiledAssembly) =>
|
||||||
|
compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == CodeGenHelpers.RuntimeAssemblyName);
|
||||||
|
|
||||||
private readonly List<DiagnosticMessage> m_Diagnostics = new List<DiagnosticMessage>();
|
private readonly List<DiagnosticMessage> m_Diagnostics = new List<DiagnosticMessage>();
|
||||||
|
|
||||||
@@ -109,8 +108,10 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
private FieldReference m_NetworkManager_rpc_name_table_FieldRef;
|
private FieldReference m_NetworkManager_rpc_name_table_FieldRef;
|
||||||
private MethodReference m_NetworkManager_rpc_name_table_Add_MethodRef;
|
private MethodReference m_NetworkManager_rpc_name_table_Add_MethodRef;
|
||||||
private TypeReference m_NetworkBehaviour_TypeRef;
|
private TypeReference m_NetworkBehaviour_TypeRef;
|
||||||
private MethodReference m_NetworkBehaviour_SendServerRpc_MethodRef;
|
private MethodReference m_NetworkBehaviour_beginSendServerRpc_MethodRef;
|
||||||
private MethodReference m_NetworkBehaviour_SendClientRpc_MethodRef;
|
private MethodReference m_NetworkBehaviour_endSendServerRpc_MethodRef;
|
||||||
|
private MethodReference m_NetworkBehaviour_beginSendClientRpc_MethodRef;
|
||||||
|
private MethodReference m_NetworkBehaviour_endSendClientRpc_MethodRef;
|
||||||
private FieldReference m_NetworkBehaviour_rpc_exec_stage_FieldRef;
|
private FieldReference m_NetworkBehaviour_rpc_exec_stage_FieldRef;
|
||||||
private MethodReference m_NetworkBehaviour_getNetworkManager_MethodRef;
|
private MethodReference m_NetworkBehaviour_getNetworkManager_MethodRef;
|
||||||
private MethodReference m_NetworkBehaviour_getOwnerClientId_MethodRef;
|
private MethodReference m_NetworkBehaviour_getOwnerClientId_MethodRef;
|
||||||
@@ -124,8 +125,6 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
private TypeReference m_ClientRpcParams_TypeRef;
|
private TypeReference m_ClientRpcParams_TypeRef;
|
||||||
|
|
||||||
private TypeReference m_FastBufferWriter_TypeRef;
|
private TypeReference m_FastBufferWriter_TypeRef;
|
||||||
private MethodReference m_FastBufferWriter_Constructor;
|
|
||||||
private MethodReference m_FastBufferWriter_Dispose;
|
|
||||||
private Dictionary<string, MethodReference> m_FastBufferWriter_WriteValue_MethodRefs = new Dictionary<string, MethodReference>();
|
private Dictionary<string, MethodReference> m_FastBufferWriter_WriteValue_MethodRefs = new Dictionary<string, MethodReference>();
|
||||||
private List<MethodReference> m_FastBufferWriter_ExtensionMethodRefs = new List<MethodReference>();
|
private List<MethodReference> m_FastBufferWriter_ExtensionMethodRefs = new List<MethodReference>();
|
||||||
|
|
||||||
@@ -144,8 +143,10 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
private const string k_NetworkManager_rpc_name_table = nameof(NetworkManager.__rpc_name_table);
|
private const string k_NetworkManager_rpc_name_table = nameof(NetworkManager.__rpc_name_table);
|
||||||
|
|
||||||
private const string k_NetworkBehaviour_rpc_exec_stage = nameof(NetworkBehaviour.__rpc_exec_stage);
|
private const string k_NetworkBehaviour_rpc_exec_stage = nameof(NetworkBehaviour.__rpc_exec_stage);
|
||||||
private const string k_NetworkBehaviour_SendServerRpc = nameof(NetworkBehaviour.__sendServerRpc);
|
private const string k_NetworkBehaviour_beginSendServerRpc = nameof(NetworkBehaviour.__beginSendServerRpc);
|
||||||
private const string k_NetworkBehaviour_SendClientRpc = nameof(NetworkBehaviour.__sendClientRpc);
|
private const string k_NetworkBehaviour_endSendServerRpc = nameof(NetworkBehaviour.__endSendServerRpc);
|
||||||
|
private const string k_NetworkBehaviour_beginSendClientRpc = nameof(NetworkBehaviour.__beginSendClientRpc);
|
||||||
|
private const string k_NetworkBehaviour_endSendClientRpc = nameof(NetworkBehaviour.__endSendClientRpc);
|
||||||
private const string k_NetworkBehaviour_NetworkManager = nameof(NetworkBehaviour.NetworkManager);
|
private const string k_NetworkBehaviour_NetworkManager = nameof(NetworkBehaviour.NetworkManager);
|
||||||
private const string k_NetworkBehaviour_OwnerClientId = nameof(NetworkBehaviour.OwnerClientId);
|
private const string k_NetworkBehaviour_OwnerClientId = nameof(NetworkBehaviour.OwnerClientId);
|
||||||
|
|
||||||
@@ -234,11 +235,17 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
{
|
{
|
||||||
switch (methodInfo.Name)
|
switch (methodInfo.Name)
|
||||||
{
|
{
|
||||||
case k_NetworkBehaviour_SendServerRpc:
|
case k_NetworkBehaviour_beginSendServerRpc:
|
||||||
m_NetworkBehaviour_SendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
m_NetworkBehaviour_beginSendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||||
break;
|
break;
|
||||||
case k_NetworkBehaviour_SendClientRpc:
|
case k_NetworkBehaviour_endSendServerRpc:
|
||||||
m_NetworkBehaviour_SendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
m_NetworkBehaviour_endSendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||||
|
break;
|
||||||
|
case k_NetworkBehaviour_beginSendClientRpc:
|
||||||
|
m_NetworkBehaviour_beginSendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||||
|
break;
|
||||||
|
case k_NetworkBehaviour_endSendClientRpc:
|
||||||
|
m_NetworkBehaviour_endSendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,24 +306,22 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
var fastBufferWriterType = typeof(FastBufferWriter);
|
var fastBufferWriterType = typeof(FastBufferWriter);
|
||||||
m_FastBufferWriter_TypeRef = moduleDefinition.ImportReference(fastBufferWriterType);
|
m_FastBufferWriter_TypeRef = moduleDefinition.ImportReference(fastBufferWriterType);
|
||||||
|
|
||||||
m_FastBufferWriter_Constructor = moduleDefinition.ImportReference(
|
|
||||||
fastBufferWriterType.GetConstructor(new[] { typeof(int), typeof(Allocator), typeof(int) }));
|
|
||||||
m_FastBufferWriter_Dispose = moduleDefinition.ImportReference(fastBufferWriterType.GetMethod("Dispose"));
|
|
||||||
|
|
||||||
var fastBufferReaderType = typeof(FastBufferReader);
|
var fastBufferReaderType = typeof(FastBufferReader);
|
||||||
m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(fastBufferReaderType);
|
m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(fastBufferReaderType);
|
||||||
|
|
||||||
// Find all extension methods for FastBufferReader and FastBufferWriter to enable user-implemented
|
// Find all extension methods for FastBufferReader and FastBufferWriter to enable user-implemented
|
||||||
// methods to be called.
|
// methods to be called.
|
||||||
var assemblies = new List<AssemblyDefinition>();
|
var assemblies = new List<AssemblyDefinition> { m_MainModule.Assembly };
|
||||||
assemblies.Add(m_MainModule.Assembly);
|
|
||||||
foreach (var reference in m_MainModule.AssemblyReferences)
|
foreach (var reference in m_MainModule.AssemblyReferences)
|
||||||
{
|
{
|
||||||
assemblies.Add(m_AssemblyResolver.Resolve(reference));
|
var assembly = m_AssemblyResolver.Resolve(reference);
|
||||||
|
if (assembly != null)
|
||||||
|
{
|
||||||
|
assemblies.Add(assembly);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var extensionConstructor =
|
var extensionConstructor = moduleDefinition.ImportReference(typeof(ExtensionAttribute).GetConstructor(new Type[] { }));
|
||||||
moduleDefinition.ImportReference(typeof(ExtensionAttribute).GetConstructor(new Type[] { }));
|
|
||||||
foreach (var assembly in assemblies)
|
foreach (var assembly in assemblies)
|
||||||
{
|
{
|
||||||
foreach (var module in assembly.Modules)
|
foreach (var module in assembly.Modules)
|
||||||
@@ -328,6 +333,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var method in type.Methods)
|
foreach (var method in type.Methods)
|
||||||
{
|
{
|
||||||
if (!method.IsStatic)
|
if (!method.IsStatic)
|
||||||
@@ -352,13 +358,11 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
var parameters = method.Parameters;
|
var parameters = method.Parameters;
|
||||||
|
|
||||||
if (parameters.Count == 2
|
if (parameters.Count == 2 && parameters[0].ParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve())
|
||||||
&& parameters[0].ParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve())
|
|
||||||
{
|
{
|
||||||
m_FastBufferWriter_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method));
|
m_FastBufferWriter_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method));
|
||||||
}
|
}
|
||||||
else if (parameters.Count == 2
|
else if (parameters.Count == 2 && parameters[0].ParameterType.Resolve() == m_FastBufferReader_TypeRef.MakeByReferenceType().Resolve())
|
||||||
&& parameters[0].ParameterType.Resolve() == m_FastBufferReader_TypeRef.MakeByReferenceType().Resolve())
|
|
||||||
{
|
{
|
||||||
m_FastBufferReader_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method));
|
m_FastBufferReader_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method));
|
||||||
}
|
}
|
||||||
@@ -391,9 +395,20 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (methodDefinition.HasCustomAttributes)
|
||||||
|
{
|
||||||
|
foreach (var attribute in methodDefinition.CustomAttributes)
|
||||||
|
{
|
||||||
|
if (attribute.AttributeType.Name == nameof(AsyncStateMachineAttribute))
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.FullName}: RPCs cannot be 'async'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InjectWriteAndCallBlocks(methodDefinition, rpcAttribute, rpcMethodId);
|
InjectWriteAndCallBlocks(methodDefinition, rpcAttribute, rpcMethodId);
|
||||||
|
|
||||||
rpcHandlers.Add((rpcMethodId, GenerateStaticHandler(methodDefinition, rpcAttribute)));
|
rpcHandlers.Add((rpcMethodId, GenerateStaticHandler(methodDefinition, rpcAttribute, rpcMethodId)));
|
||||||
|
|
||||||
if (isEditorOrDevelopment)
|
if (isEditorOrDevelopment)
|
||||||
{
|
{
|
||||||
@@ -473,7 +488,6 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinition)
|
private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinition)
|
||||||
{
|
{
|
||||||
CustomAttribute rpcAttribute = null;
|
CustomAttribute rpcAttribute = null;
|
||||||
bool isServerRpc = false;
|
|
||||||
foreach (var customAttribute in methodDefinition.CustomAttributes)
|
foreach (var customAttribute in methodDefinition.CustomAttributes)
|
||||||
{
|
{
|
||||||
var customAttributeType_FullName = customAttribute.AttributeType.FullName;
|
var customAttributeType_FullName = customAttribute.AttributeType.FullName;
|
||||||
@@ -517,7 +531,6 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
if (isValid)
|
if (isValid)
|
||||||
{
|
{
|
||||||
isServerRpc = customAttributeType_FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
|
|
||||||
rpcAttribute = customAttribute;
|
rpcAttribute = customAttribute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -565,31 +578,36 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
checkType = paramType.GetElementType().Resolve();
|
checkType = paramType.GetElementType().Resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if ((parameters[0].ParameterType.Resolve() == checkType ||
|
||||||
(parameters[0].ParameterType.Resolve() == checkType
|
(parameters[0].ParameterType.Resolve() == checkType.MakeByReferenceType().Resolve() && parameters[0].IsIn)))
|
||||||
|| (parameters[0].ParameterType.Resolve() == checkType.MakeByReferenceType().Resolve() && parameters[0].IsIn)))
|
|
||||||
{
|
{
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (method.HasGenericParameters && method.GenericParameters.Count == 1)
|
if (method.HasGenericParameters && method.GenericParameters.Count == 1)
|
||||||
{
|
{
|
||||||
if (method.GenericParameters[0].HasConstraints)
|
if (method.GenericParameters[0].HasConstraints)
|
||||||
{
|
{
|
||||||
|
var meetsConstraints = true;
|
||||||
foreach (var constraint in method.GenericParameters[0].Constraints)
|
foreach (var constraint in method.GenericParameters[0].Constraints)
|
||||||
{
|
{
|
||||||
var resolvedConstraint = constraint.Resolve();
|
var resolvedConstraint = constraint.Resolve();
|
||||||
|
|
||||||
if (
|
if ((resolvedConstraint.IsInterface && !checkType.HasInterface(resolvedConstraint.FullName)) ||
|
||||||
(resolvedConstraint.IsInterface &&
|
(resolvedConstraint.IsClass && !checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName)) ||
|
||||||
checkType.HasInterface(resolvedConstraint.FullName))
|
(resolvedConstraint.Name == "ValueType" && !checkType.IsValueType))
|
||||||
|| (resolvedConstraint.IsClass &&
|
|
||||||
checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName)))
|
|
||||||
{
|
{
|
||||||
var instanceMethod = new GenericInstanceMethod(method);
|
meetsConstraints = false;
|
||||||
instanceMethod.GenericArguments.Add(checkType);
|
break;
|
||||||
return instanceMethod;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (meetsConstraints)
|
||||||
|
{
|
||||||
|
var instanceMethod = new GenericInstanceMethod(method);
|
||||||
|
instanceMethod.GenericArguments.Add(checkType);
|
||||||
|
return instanceMethod;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -613,8 +631,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
{
|
{
|
||||||
if (parameters[1].IsIn)
|
if (parameters[1].IsIn)
|
||||||
{
|
{
|
||||||
if (parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve()
|
if (parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve() &&
|
||||||
&& ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray)
|
((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray)
|
||||||
{
|
{
|
||||||
methodRef = method;
|
methodRef = method;
|
||||||
m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef;
|
m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef;
|
||||||
@@ -624,8 +642,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
if (parameters[1].ParameterType.Resolve() == paramType.Resolve()
|
if (parameters[1].ParameterType.Resolve() == paramType.Resolve() &&
|
||||||
&& parameters[1].ParameterType.IsArray == paramType.IsArray)
|
parameters[1].ParameterType.IsArray == paramType.IsArray)
|
||||||
{
|
{
|
||||||
methodRef = method;
|
methodRef = method;
|
||||||
m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef;
|
m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef;
|
||||||
@@ -696,11 +714,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
{
|
{
|
||||||
var resolvedConstraint = constraint.Resolve();
|
var resolvedConstraint = constraint.Resolve();
|
||||||
|
|
||||||
if (
|
if ((resolvedConstraint.IsInterface && checkType.HasInterface(resolvedConstraint.FullName)) ||
|
||||||
(resolvedConstraint.IsInterface &&
|
(resolvedConstraint.IsClass && checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName)))
|
||||||
checkType.HasInterface(resolvedConstraint.FullName))
|
|
||||||
|| (resolvedConstraint.IsClass &&
|
|
||||||
checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName)))
|
|
||||||
{
|
{
|
||||||
var instanceMethod = new GenericInstanceMethod(method);
|
var instanceMethod = new GenericInstanceMethod(method);
|
||||||
instanceMethod.GenericArguments.Add(checkType);
|
instanceMethod.GenericArguments.Add(checkType);
|
||||||
@@ -725,11 +740,10 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
foreach (var method in m_FastBufferReader_ExtensionMethodRefs)
|
foreach (var method in m_FastBufferReader_ExtensionMethodRefs)
|
||||||
{
|
{
|
||||||
var parameters = method.Resolve().Parameters;
|
var parameters = method.Resolve().Parameters;
|
||||||
if (
|
if (method.Name == k_ReadValueMethodName &&
|
||||||
method.Name == k_ReadValueMethodName
|
parameters[1].IsOut &&
|
||||||
&& parameters[1].IsOut
|
parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve() &&
|
||||||
&& parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve()
|
((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray)
|
||||||
&& ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray)
|
|
||||||
{
|
{
|
||||||
methodRef = method;
|
methodRef = method;
|
||||||
m_FastBufferReader_ReadValue_MethodRefs[assemblyQualifiedName] = methodRef;
|
m_FastBufferReader_ReadValue_MethodRefs[assemblyQualifiedName] = methodRef;
|
||||||
@@ -761,8 +775,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
var instructions = new List<Instruction>();
|
var instructions = new List<Instruction>();
|
||||||
var processor = methodDefinition.Body.GetILProcessor();
|
var processor = methodDefinition.Body.GetILProcessor();
|
||||||
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
|
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
|
||||||
var requireOwnership = true; // default value MUST be = `ServerRpcAttribute.RequireOwnership`
|
var requireOwnership = true; // default value MUST be == `ServerRpcAttribute.RequireOwnership`
|
||||||
var rpcDelivery = RpcDelivery.Reliable; // default value MUST be = `RpcAttribute.Delivery`
|
var rpcDelivery = RpcDelivery.Reliable; // default value MUST be == `RpcAttribute.Delivery`
|
||||||
foreach (var attrField in rpcAttribute.Fields)
|
foreach (var attrField in rpcAttribute.Fields)
|
||||||
{
|
{
|
||||||
switch (attrField.Name)
|
switch (attrField.Name)
|
||||||
@@ -786,9 +800,9 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
// NetworkManager networkManager;
|
// NetworkManager networkManager;
|
||||||
methodDefinition.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
|
methodDefinition.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
|
||||||
int netManLocIdx = methodDefinition.Body.Variables.Count - 1;
|
int netManLocIdx = methodDefinition.Body.Variables.Count - 1;
|
||||||
// NetworkSerializer serializer;
|
// FastBufferWriter bufferWriter;
|
||||||
methodDefinition.Body.Variables.Add(new VariableDefinition(m_FastBufferWriter_TypeRef));
|
methodDefinition.Body.Variables.Add(new VariableDefinition(m_FastBufferWriter_TypeRef));
|
||||||
int serializerLocIdx = methodDefinition.Body.Variables.Count - 1;
|
int bufWriterLocIdx = methodDefinition.Body.Variables.Count - 1;
|
||||||
|
|
||||||
// XXXRpcParams
|
// XXXRpcParams
|
||||||
if (!hasRpcParams)
|
if (!hasRpcParams)
|
||||||
@@ -843,6 +857,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
instructions.Add(beginInstr);
|
instructions.Add(beginInstr);
|
||||||
|
|
||||||
|
// var bufferWriter = __beginSendServerRpc(rpcMethodId, serverRpcParams, rpcDelivery) -> ServerRpc
|
||||||
|
// var bufferWriter = __beginSendClientRpc(rpcMethodId, clientRpcParams, rpcDelivery) -> ClientRpc
|
||||||
if (isServerRpc)
|
if (isServerRpc)
|
||||||
{
|
{
|
||||||
// ServerRpc
|
// ServerRpc
|
||||||
@@ -856,8 +872,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.Add(processor.Create(OpCodes.Ldarg_0));
|
instructions.Add(processor.Create(OpCodes.Ldarg_0));
|
||||||
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_getOwnerClientId_MethodRef));
|
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_getOwnerClientId_MethodRef));
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx));
|
||||||
instructions.Add(
|
instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkManager_getLocalClientId_MethodRef));
|
||||||
processor.Create(OpCodes.Callvirt, m_NetworkManager_getLocalClientId_MethodRef));
|
|
||||||
instructions.Add(processor.Create(OpCodes.Ceq));
|
instructions.Add(processor.Create(OpCodes.Ceq));
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4, 0));
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, 0));
|
||||||
instructions.Add(processor.Create(OpCodes.Ceq));
|
instructions.Add(processor.Create(OpCodes.Ceq));
|
||||||
@@ -875,8 +890,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.Add(processor.Create(OpCodes.Brfalse, logNextInstr));
|
instructions.Add(processor.Create(OpCodes.Brfalse, logNextInstr));
|
||||||
|
|
||||||
// Debug.LogError(...);
|
// Debug.LogError(...);
|
||||||
instructions.Add(processor.Create(OpCodes.Ldstr,
|
instructions.Add(processor.Create(OpCodes.Ldstr, "Only the owner can invoke a ServerRpc that requires ownership!"));
|
||||||
"Only the owner can invoke a ServerRpc that requires ownership!"));
|
|
||||||
instructions.Add(processor.Create(OpCodes.Call, m_Debug_LogError_MethodRef));
|
instructions.Add(processor.Create(OpCodes.Call, m_Debug_LogError_MethodRef));
|
||||||
|
|
||||||
instructions.Add(logNextInstr);
|
instructions.Add(logNextInstr);
|
||||||
@@ -884,31 +898,86 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.Add(roReturnInstr);
|
instructions.Add(roReturnInstr);
|
||||||
instructions.Add(roLastInstr);
|
instructions.Add(roLastInstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// var bufferWriter = __beginSendServerRpc(rpcMethodId, serverRpcParams, rpcDelivery);
|
||||||
|
instructions.Add(processor.Create(OpCodes.Ldarg_0));
|
||||||
|
|
||||||
|
// rpcMethodId
|
||||||
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
|
||||||
|
|
||||||
|
// rpcParams
|
||||||
|
instructions.Add(hasRpcParams ? processor.Create(OpCodes.Ldarg, paramCount) : processor.Create(OpCodes.Ldloc, rpcParamsIdx));
|
||||||
|
|
||||||
|
// rpcDelivery
|
||||||
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
|
||||||
|
|
||||||
|
// __beginSendServerRpc
|
||||||
|
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_beginSendServerRpc_MethodRef));
|
||||||
|
instructions.Add(processor.Create(OpCodes.Stloc, bufWriterLocIdx));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ClientRpc
|
||||||
|
|
||||||
// var writer = new FastBufferWriter(1285, Allocator.Temp, 63985);
|
// var bufferWriter = __beginSendClientRpc(rpcMethodId, clientRpcParams, rpcDelivery);
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldarg_0));
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4, 1300 - sizeof(byte) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)));
|
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4_2));
|
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4, 64000 - sizeof(byte) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)));
|
|
||||||
instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Constructor));
|
|
||||||
|
|
||||||
var firstInstruction = processor.Create(OpCodes.Nop);
|
// rpcMethodId
|
||||||
instructions.Add(firstInstruction);
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
|
||||||
|
|
||||||
|
// rpcParams
|
||||||
|
instructions.Add(hasRpcParams ? processor.Create(OpCodes.Ldarg, paramCount) : processor.Create(OpCodes.Ldloc, rpcParamsIdx));
|
||||||
|
|
||||||
|
// rpcDelivery
|
||||||
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
|
||||||
|
|
||||||
|
// __beginSendClientRpc
|
||||||
|
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_beginSendClientRpc_MethodRef));
|
||||||
|
instructions.Add(processor.Create(OpCodes.Stloc, bufWriterLocIdx));
|
||||||
|
}
|
||||||
|
|
||||||
// write method parameters into stream
|
// write method parameters into stream
|
||||||
for (int paramIndex = 0; paramIndex < paramCount; ++paramIndex)
|
for (int paramIndex = 0; paramIndex < paramCount; ++paramIndex)
|
||||||
{
|
{
|
||||||
var paramDef = methodDefinition.Parameters[paramIndex];
|
var paramDef = methodDefinition.Parameters[paramIndex];
|
||||||
var paramType = paramDef.ParameterType;
|
var paramType = paramDef.ParameterType;
|
||||||
// ServerRpcParams
|
if (paramType.FullName == CodeGenHelpers.ClientRpcSendParams_FullName ||
|
||||||
if (paramType.FullName == CodeGenHelpers.ServerRpcParams_FullName && isServerRpc && paramIndex == paramCount - 1)
|
paramType.FullName == CodeGenHelpers.ClientRpcReceiveParams_FullName)
|
||||||
{
|
{
|
||||||
|
m_Diagnostics.AddError($"Rpcs may not accept {paramType.FullName} as a parameter. Use {nameof(ClientRpcParams)} instead.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paramType.FullName == CodeGenHelpers.ServerRpcSendParams_FullName ||
|
||||||
|
paramType.FullName == CodeGenHelpers.ServerRpcReceiveParams_FullName)
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError($"Rpcs may not accept {paramType.FullName} as a parameter. Use {nameof(ServerRpcParams)} instead.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// ServerRpcParams
|
||||||
|
if (paramType.FullName == CodeGenHelpers.ServerRpcParams_FullName)
|
||||||
|
{
|
||||||
|
if (paramIndex != paramCount - 1)
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError(methodDefinition, $"{nameof(ServerRpcParams)} must be the last parameter in a ServerRpc.");
|
||||||
|
}
|
||||||
|
if (!isServerRpc)
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError($"ClientRpcs may not accept {nameof(ServerRpcParams)} as a parameter.");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// ClientRpcParams
|
// ClientRpcParams
|
||||||
if (paramType.FullName == CodeGenHelpers.ClientRpcParams_FullName && !isServerRpc && paramIndex == paramCount - 1)
|
if (paramType.FullName == CodeGenHelpers.ClientRpcParams_FullName)
|
||||||
{
|
{
|
||||||
|
if (paramIndex != paramCount - 1)
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError(methodDefinition, $"{nameof(ClientRpcParams)} must be the last parameter in a ClientRpc.");
|
||||||
|
}
|
||||||
|
if (isServerRpc)
|
||||||
|
{
|
||||||
|
m_Diagnostics.AddError($"ServerRpcs may not accept {nameof(ClientRpcParams)} as a parameter.");
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -931,8 +1000,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.Add(processor.Create(OpCodes.Cgt_Un));
|
instructions.Add(processor.Create(OpCodes.Cgt_Un));
|
||||||
instructions.Add(processor.Create(OpCodes.Stloc, isSetLocalIndex));
|
instructions.Add(processor.Create(OpCodes.Stloc, isSetLocalIndex));
|
||||||
|
|
||||||
// writer.WriteValueSafe(isSet);
|
// bufferWriter.WriteValueSafe(isSet);
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloca, isSetLocalIndex));
|
instructions.Add(processor.Create(OpCodes.Ldloca, isSetLocalIndex));
|
||||||
instructions.Add(processor.Create(OpCodes.Call, boolMethodRef));
|
instructions.Add(processor.Create(OpCodes.Call, boolMethodRef));
|
||||||
|
|
||||||
@@ -945,22 +1014,28 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
var foundMethodRef = GetWriteMethodForParameter(paramType, out var methodRef);
|
var foundMethodRef = GetWriteMethodForParameter(paramType, out var methodRef);
|
||||||
if (foundMethodRef)
|
if (foundMethodRef)
|
||||||
{
|
{
|
||||||
// writer.WriteNetworkSerializable(param) for INetworkSerializable, OR
|
// bufferWriter.WriteNetworkSerializable(param) for INetworkSerializable, OR
|
||||||
// writer.WriteNetworkSerializable(param, -1, 0) for INetworkSerializable arrays, OR
|
// bufferWriter.WriteNetworkSerializable(param, -1, 0) for INetworkSerializable arrays, OR
|
||||||
// writer.WriteValueSafe(param) for value types, OR
|
// bufferWriter.WriteValueSafe(param) for value types, OR
|
||||||
// writer.WriteValueSafe(param, -1, 0) for arrays of value types, OR
|
// bufferWriter.WriteValueSafe(param, -1, 0) for arrays of value types, OR
|
||||||
// writer.WriteValueSafe(param, false) for strings
|
// bufferWriter.WriteValueSafe(param, false) for strings
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx));
|
|
||||||
var method = methodRef.Resolve();
|
var method = methodRef.Resolve();
|
||||||
var checkParameter = method.Parameters[0];
|
var checkParameter = method.Parameters[0];
|
||||||
var isExtensionMethod = false;
|
var isExtensionMethod = false;
|
||||||
if (checkParameter.ParameterType.Resolve() ==
|
if (methodRef.Resolve().DeclaringType != m_FastBufferWriter_TypeRef.Resolve())
|
||||||
m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve())
|
|
||||||
{
|
{
|
||||||
isExtensionMethod = true;
|
isExtensionMethod = true;
|
||||||
checkParameter = method.Parameters[1];
|
checkParameter = method.Parameters[1];
|
||||||
}
|
}
|
||||||
if (checkParameter.IsIn)
|
if (!isExtensionMethod || method.Parameters[0].ParameterType.IsByReference)
|
||||||
|
{
|
||||||
|
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
instructions.Add(processor.Create(OpCodes.Ldloc, bufWriterLocIdx));
|
||||||
|
}
|
||||||
|
if (checkParameter.IsIn || checkParameter.IsOut || checkParameter.ParameterType.IsByReference)
|
||||||
{
|
{
|
||||||
instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1));
|
instructions.Add(processor.Create(OpCodes.Ldarga, paramIndex + 1));
|
||||||
}
|
}
|
||||||
@@ -969,16 +1044,14 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1));
|
instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1));
|
||||||
}
|
}
|
||||||
// Special handling for WriteValue() on arrays and strings since they have additional arguments.
|
// Special handling for WriteValue() on arrays and strings since they have additional arguments.
|
||||||
if (paramType.IsArray
|
if (paramType.IsArray && ((!isExtensionMethod && methodRef.Parameters.Count == 3) ||
|
||||||
&& ((!isExtensionMethod && methodRef.Parameters.Count == 3)
|
(isExtensionMethod && methodRef.Parameters.Count == 4)))
|
||||||
|| (isExtensionMethod && methodRef.Parameters.Count == 4)))
|
|
||||||
{
|
{
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4_M1));
|
instructions.Add(processor.Create(OpCodes.Ldc_I4_M1));
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4_0));
|
instructions.Add(processor.Create(OpCodes.Ldc_I4_0));
|
||||||
}
|
}
|
||||||
else if (paramType == typeSystem.String
|
else if (paramType == typeSystem.String && ((!isExtensionMethod && methodRef.Parameters.Count == 2) ||
|
||||||
&& ((!isExtensionMethod && methodRef.Parameters.Count == 2)
|
(isExtensionMethod && methodRef.Parameters.Count == 3)))
|
||||||
|| (isExtensionMethod && methodRef.Parameters.Count == 3)))
|
|
||||||
{
|
{
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4_0));
|
instructions.Add(processor.Create(OpCodes.Ldc_I4_0));
|
||||||
}
|
}
|
||||||
@@ -998,20 +1071,20 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
instructions.Add(endInstr);
|
instructions.Add(endInstr);
|
||||||
|
|
||||||
// __sendServerRpc(ref serializer, rpcMethodId, serverRpcParams, rpcDelivery) -> ServerRpc
|
// __endSendServerRpc(ref bufferWriter, rpcMethodId, serverRpcParams, rpcDelivery) -> ServerRpc
|
||||||
// __sendClientRpc(ref serializer, rpcMethodId, clientRpcParams, rpcDelivery) -> ClientRpc
|
// __endSendClientRpc(ref bufferWriter, rpcMethodId, clientRpcParams, rpcDelivery) -> ClientRpc
|
||||||
if (isServerRpc)
|
if (isServerRpc)
|
||||||
{
|
{
|
||||||
// ServerRpc
|
// ServerRpc
|
||||||
// __sendServerRpc(ref serializer, rpcMethodId, serverRpcParams, rpcDelivery);
|
|
||||||
|
// __endSendServerRpc(ref bufferWriter, rpcMethodId, serverRpcParams, rpcDelivery);
|
||||||
instructions.Add(processor.Create(OpCodes.Ldarg_0));
|
instructions.Add(processor.Create(OpCodes.Ldarg_0));
|
||||||
|
|
||||||
// serializer
|
// bufferWriter
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
|
||||||
|
|
||||||
// rpcMethodId
|
// rpcMethodId
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
|
||||||
|
|
||||||
if (hasRpcParams)
|
if (hasRpcParams)
|
||||||
{
|
{
|
||||||
// rpcParams
|
// rpcParams
|
||||||
@@ -1022,25 +1095,24 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
// default
|
// default
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx));
|
||||||
}
|
}
|
||||||
|
|
||||||
// rpcDelivery
|
// rpcDelivery
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
|
||||||
|
|
||||||
// EndSendServerRpc
|
// __endSendServerRpc
|
||||||
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_SendServerRpc_MethodRef));
|
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_endSendServerRpc_MethodRef));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// ClientRpc
|
// ClientRpc
|
||||||
// __sendClientRpc(ref serializer, rpcMethodId, clientRpcParams, rpcDelivery);
|
|
||||||
|
// __endSendClientRpc(ref bufferWriter, rpcMethodId, clientRpcParams, rpcDelivery);
|
||||||
instructions.Add(processor.Create(OpCodes.Ldarg_0));
|
instructions.Add(processor.Create(OpCodes.Ldarg_0));
|
||||||
|
|
||||||
// serializer
|
// bufferWriter
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
|
||||||
|
|
||||||
// rpcMethodId
|
// rpcMethodId
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
|
||||||
|
|
||||||
if (hasRpcParams)
|
if (hasRpcParams)
|
||||||
{
|
{
|
||||||
// rpcParams
|
// rpcParams
|
||||||
@@ -1051,36 +1123,11 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
// default
|
// default
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx));
|
||||||
}
|
}
|
||||||
|
|
||||||
// rpcDelivery
|
// rpcDelivery
|
||||||
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
|
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
|
||||||
|
|
||||||
// EndSendClientRpc
|
// __endSendClientRpc
|
||||||
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_SendClientRpc_MethodRef));
|
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_endSendClientRpc_MethodRef));
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// TODO: Figure out why try/catch here cause the try block not to execute at all.
|
|
||||||
// End try block
|
|
||||||
//instructions.Add(processor.Create(OpCodes.Leave, lastInstr));
|
|
||||||
|
|
||||||
// writer.Dispose();
|
|
||||||
var handlerFirst = processor.Create(OpCodes.Ldloca, serializerLocIdx);
|
|
||||||
instructions.Add(handlerFirst);
|
|
||||||
instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Dispose));
|
|
||||||
|
|
||||||
// End finally block
|
|
||||||
//instructions.Add(processor.Create(OpCodes.Endfinally));
|
|
||||||
|
|
||||||
// try { ... serialization code ... } finally { writer.Dispose(); }
|
|
||||||
/*var handler = new ExceptionHandler(ExceptionHandlerType.Finally)
|
|
||||||
{
|
|
||||||
TryStart = firstInstruction,
|
|
||||||
TryEnd = handlerFirst,
|
|
||||||
HandlerStart = handlerFirst,
|
|
||||||
HandlerEnd = lastInstr
|
|
||||||
};
|
|
||||||
processor.Body.ExceptionHandlers.Add(handler);*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instructions.Add(lastInstr);
|
instructions.Add(lastInstr);
|
||||||
@@ -1115,25 +1162,21 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
instructions.ForEach(instruction => processor.Body.Instructions.Insert(0, instruction));
|
instructions.ForEach(instruction => processor.Body.Instructions.Insert(0, instruction));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition, CustomAttribute rpcAttribute)
|
private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition, CustomAttribute rpcAttribute, uint rpcMethodId)
|
||||||
{
|
{
|
||||||
var typeSystem = methodDefinition.Module.TypeSystem;
|
var typeSystem = methodDefinition.Module.TypeSystem;
|
||||||
var nhandler = new MethodDefinition(
|
var rpcHandler = new MethodDefinition(
|
||||||
$"{methodDefinition.Name}__nhandler",
|
$"__rpc_handler_{rpcMethodId}",
|
||||||
MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig,
|
MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig,
|
||||||
methodDefinition.Module.TypeSystem.Void);
|
methodDefinition.Module.TypeSystem.Void);
|
||||||
nhandler.Parameters.Add(new ParameterDefinition("target", ParameterAttributes.None, m_NetworkBehaviour_TypeRef));
|
rpcHandler.Parameters.Add(new ParameterDefinition("target", ParameterAttributes.None, m_NetworkBehaviour_TypeRef));
|
||||||
nhandler.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, m_FastBufferReader_TypeRef));
|
rpcHandler.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, m_FastBufferReader_TypeRef));
|
||||||
nhandler.Parameters.Add(new ParameterDefinition("rpcParams", ParameterAttributes.None, m_RpcParams_TypeRef));
|
rpcHandler.Parameters.Add(new ParameterDefinition("rpcParams", ParameterAttributes.None, m_RpcParams_TypeRef));
|
||||||
|
|
||||||
var processor = nhandler.Body.GetILProcessor();
|
var processor = rpcHandler.Body.GetILProcessor();
|
||||||
|
|
||||||
// begin Try/Catch
|
|
||||||
var tryStart = processor.Create(OpCodes.Nop);
|
|
||||||
processor.Append(tryStart);
|
|
||||||
|
|
||||||
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
|
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
|
||||||
var requireOwnership = true; // default value MUST be = `ServerRpcAttribute.RequireOwnership`
|
var requireOwnership = true; // default value MUST be == `ServerRpcAttribute.RequireOwnership`
|
||||||
foreach (var attrField in rpcAttribute.Fields)
|
foreach (var attrField in rpcAttribute.Fields)
|
||||||
{
|
{
|
||||||
switch (attrField.Name)
|
switch (attrField.Name)
|
||||||
@@ -1144,10 +1187,10 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nhandler.Body.InitLocals = true;
|
rpcHandler.Body.InitLocals = true;
|
||||||
// NetworkManager networkManager;
|
// NetworkManager networkManager;
|
||||||
nhandler.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
|
rpcHandler.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
|
||||||
int netManLocIdx = nhandler.Body.Variables.Count - 1;
|
int netManLocIdx = rpcHandler.Body.Variables.Count - 1;
|
||||||
|
|
||||||
{
|
{
|
||||||
var returnInstr = processor.Create(OpCodes.Ret);
|
var returnInstr = processor.Create(OpCodes.Ret);
|
||||||
@@ -1216,8 +1259,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
var paramType = paramDef.ParameterType;
|
var paramType = paramDef.ParameterType;
|
||||||
|
|
||||||
// local variable
|
// local variable
|
||||||
nhandler.Body.Variables.Add(new VariableDefinition(paramType));
|
rpcHandler.Body.Variables.Add(new VariableDefinition(paramType));
|
||||||
int localIndex = nhandler.Body.Variables.Count - 1;
|
int localIndex = rpcHandler.Body.Variables.Count - 1;
|
||||||
paramLocalMap[paramIndex] = localIndex;
|
paramLocalMap[paramIndex] = localIndex;
|
||||||
|
|
||||||
// ServerRpcParams, ClientRpcParams
|
// ServerRpcParams, ClientRpcParams
|
||||||
@@ -1251,8 +1294,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reader.ReadValueSafe(out bool isSet)
|
// reader.ReadValueSafe(out bool isSet)
|
||||||
nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean));
|
rpcHandler.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean));
|
||||||
int isSetLocalIndex = nhandler.Body.Variables.Count - 1;
|
int isSetLocalIndex = rpcHandler.Body.Variables.Count - 1;
|
||||||
processor.Emit(OpCodes.Ldarga, 1);
|
processor.Emit(OpCodes.Ldarga, 1);
|
||||||
processor.Emit(OpCodes.Ldloca, isSetLocalIndex);
|
processor.Emit(OpCodes.Ldloca, isSetLocalIndex);
|
||||||
processor.Emit(OpCodes.Call, boolMethodRef);
|
processor.Emit(OpCodes.Call, boolMethodRef);
|
||||||
@@ -1271,7 +1314,18 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
if (foundMethodRef)
|
if (foundMethodRef)
|
||||||
{
|
{
|
||||||
// reader.ReadValueSafe(out localVar);
|
// reader.ReadValueSafe(out localVar);
|
||||||
processor.Emit(OpCodes.Ldarga, 1);
|
|
||||||
|
var checkParameter = methodRef.Resolve().Parameters[0];
|
||||||
|
|
||||||
|
var isExtensionMethod = methodRef.Resolve().DeclaringType != m_FastBufferReader_TypeRef.Resolve();
|
||||||
|
if (!isExtensionMethod || checkParameter.ParameterType.IsByReference)
|
||||||
|
{
|
||||||
|
processor.Emit(OpCodes.Ldarga, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processor.Emit(OpCodes.Ldarg, 1);
|
||||||
|
}
|
||||||
processor.Emit(OpCodes.Ldloca, localIndex);
|
processor.Emit(OpCodes.Ldloca, localIndex);
|
||||||
if (paramType == typeSystem.String)
|
if (paramType == typeSystem.String)
|
||||||
{
|
{
|
||||||
@@ -1308,55 +1362,8 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
processor.Emit(OpCodes.Ldc_I4, (int)NetworkBehaviour.__RpcExecStage.None);
|
processor.Emit(OpCodes.Ldc_I4, (int)NetworkBehaviour.__RpcExecStage.None);
|
||||||
processor.Emit(OpCodes.Stfld, m_NetworkBehaviour_rpc_exec_stage_FieldRef);
|
processor.Emit(OpCodes.Stfld, m_NetworkBehaviour_rpc_exec_stage_FieldRef);
|
||||||
|
|
||||||
// pull in the Exception Module
|
|
||||||
var exception = m_MainModule.ImportReference(typeof(Exception));
|
|
||||||
|
|
||||||
// Get Exception.ToString()
|
|
||||||
var exp = m_MainModule.ImportReference(typeof(Exception).GetMethod("ToString", new Type[] { }));
|
|
||||||
|
|
||||||
// Get String.Format (This is equivalent to an interpolated string)
|
|
||||||
var stringFormat = m_MainModule.ImportReference(typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(object) }));
|
|
||||||
|
|
||||||
nhandler.Body.Variables.Add(new VariableDefinition(exception));
|
|
||||||
int exceptionVariableIndex = nhandler.Body.Variables.Count - 1;
|
|
||||||
|
|
||||||
//try ends/catch begins
|
|
||||||
var catchEnds = processor.Create(OpCodes.Nop);
|
|
||||||
processor.Emit(OpCodes.Leave, catchEnds);
|
|
||||||
|
|
||||||
// Load the Exception onto the stack
|
|
||||||
var catchStarts = processor.Create(OpCodes.Stloc, exceptionVariableIndex);
|
|
||||||
processor.Append(catchStarts);
|
|
||||||
|
|
||||||
// Load string for the error log that will be shown
|
|
||||||
processor.Emit(OpCodes.Ldstr, $"Unhandled RPC Exception:\n {{0}}");
|
|
||||||
processor.Emit(OpCodes.Ldloc, exceptionVariableIndex);
|
|
||||||
processor.Emit(OpCodes.Callvirt, exp);
|
|
||||||
processor.Emit(OpCodes.Call, stringFormat);
|
|
||||||
|
|
||||||
// Call Debug.LogError
|
|
||||||
processor.Emit(OpCodes.Call, m_Debug_LogError_MethodRef);
|
|
||||||
|
|
||||||
// reset NetworkBehaviour.__rpc_exec_stage = __RpcExecStage.None;
|
|
||||||
processor.Emit(OpCodes.Ldarg_0);
|
|
||||||
processor.Emit(OpCodes.Ldc_I4, (int)NetworkBehaviour.__RpcExecStage.None);
|
|
||||||
processor.Emit(OpCodes.Stfld, m_NetworkBehaviour_rpc_exec_stage_FieldRef);
|
|
||||||
|
|
||||||
// catch ends
|
|
||||||
processor.Append(catchEnds);
|
|
||||||
|
|
||||||
processor.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Catch)
|
|
||||||
{
|
|
||||||
CatchType = exception,
|
|
||||||
TryStart = tryStart,
|
|
||||||
TryEnd = catchStarts,
|
|
||||||
HandlerStart = catchStarts,
|
|
||||||
HandlerEnd = catchEnds
|
|
||||||
});
|
|
||||||
|
|
||||||
processor.Emit(OpCodes.Ret);
|
processor.Emit(OpCodes.Ret);
|
||||||
|
return rpcHandler;
|
||||||
return nhandler;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
case nameof(NetworkBehaviour):
|
case nameof(NetworkBehaviour):
|
||||||
ProcessNetworkBehaviour(typeDefinition);
|
ProcessNetworkBehaviour(typeDefinition);
|
||||||
break;
|
break;
|
||||||
|
case nameof(NetworkVariableHelper):
|
||||||
|
ProcessNetworkVariableHelper(typeDefinition);
|
||||||
|
break;
|
||||||
case nameof(__RpcParams):
|
case nameof(__RpcParams):
|
||||||
typeDefinition.IsPublic = true;
|
typeDefinition.IsPublic = true;
|
||||||
break;
|
break;
|
||||||
@@ -100,6 +103,17 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ProcessNetworkVariableHelper(TypeDefinition typeDefinition)
|
||||||
|
{
|
||||||
|
foreach (var methodDefinition in typeDefinition.Methods)
|
||||||
|
{
|
||||||
|
if (methodDefinition.Name == nameof(NetworkVariableHelper.InitializeDelegates))
|
||||||
|
{
|
||||||
|
methodDefinition.IsPublic = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
|
private void ProcessNetworkBehaviour(TypeDefinition typeDefinition)
|
||||||
{
|
{
|
||||||
foreach (var nestedType in typeDefinition.NestedTypes)
|
foreach (var nestedType in typeDefinition.NestedTypes)
|
||||||
@@ -120,8 +134,10 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
foreach (var methodDefinition in typeDefinition.Methods)
|
foreach (var methodDefinition in typeDefinition.Methods)
|
||||||
{
|
{
|
||||||
if (methodDefinition.Name == nameof(NetworkBehaviour.__sendServerRpc)
|
if (methodDefinition.Name == nameof(NetworkBehaviour.__beginSendServerRpc) ||
|
||||||
|| methodDefinition.Name == nameof(NetworkBehaviour.__sendClientRpc))
|
methodDefinition.Name == nameof(NetworkBehaviour.__endSendServerRpc) ||
|
||||||
|
methodDefinition.Name == nameof(NetworkBehaviour.__beginSendClientRpc) ||
|
||||||
|
methodDefinition.Name == nameof(NetworkBehaviour.__endSendClientRpc))
|
||||||
{
|
{
|
||||||
methodDefinition.IsFamily = true;
|
methodDefinition.IsFamily = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
|
||||||
{
|
|
||||||
public class DontShowInTransportDropdownAttribute : Attribute
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5f097067d4254dc7ad018d7ad90df7c3
|
|
||||||
timeCreated: 1620386886
|
|
||||||
@@ -1,103 +1,23 @@
|
|||||||
using System;
|
|
||||||
using Unity.Netcode.Components;
|
using Unity.Netcode.Components;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Animations;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
namespace Unity.Netcode.Editor
|
||||||
{
|
{
|
||||||
public static class TextUtility
|
|
||||||
{
|
|
||||||
public static GUIContent TextContent(string name, string tooltip)
|
|
||||||
{
|
|
||||||
var newContent = new GUIContent(name);
|
|
||||||
newContent.tooltip = tooltip;
|
|
||||||
return newContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GUIContent TextContent(string name)
|
|
||||||
{
|
|
||||||
return new GUIContent(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CustomEditor(typeof(NetworkAnimator), true)]
|
[CustomEditor(typeof(NetworkAnimator), true)]
|
||||||
[CanEditMultipleObjects]
|
[CanEditMultipleObjects]
|
||||||
public class NetworkAnimatorEditor : UnityEditor.Editor
|
public class NetworkAnimatorEditor : UnityEditor.Editor
|
||||||
{
|
{
|
||||||
private NetworkAnimator m_AnimSync;
|
|
||||||
[NonSerialized] private bool m_Initialized;
|
|
||||||
private SerializedProperty m_AnimatorProperty;
|
|
||||||
private GUIContent m_AnimatorLabel;
|
|
||||||
|
|
||||||
private void Init()
|
|
||||||
{
|
|
||||||
if (m_Initialized)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Initialized = true;
|
|
||||||
m_AnimSync = target as NetworkAnimator;
|
|
||||||
|
|
||||||
m_AnimatorProperty = serializedObject.FindProperty("m_Animator");
|
|
||||||
m_AnimatorLabel = TextUtility.TextContent("Animator", "The Animator component to synchronize.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnInspectorGUI()
|
public override void OnInspectorGUI()
|
||||||
{
|
{
|
||||||
Init();
|
|
||||||
serializedObject.Update();
|
serializedObject.Update();
|
||||||
DrawControls();
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawControls()
|
|
||||||
{
|
|
||||||
EditorGUI.BeginChangeCheck();
|
EditorGUI.BeginChangeCheck();
|
||||||
EditorGUILayout.PropertyField(m_AnimatorProperty, m_AnimatorLabel);
|
var label = new GUIContent("Animator", "The Animator component to synchronize");
|
||||||
if (EditorGUI.EndChangeCheck())
|
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Animator"), label);
|
||||||
{
|
EditorGUI.EndChangeCheck();
|
||||||
m_AnimSync.ResetParameterOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_AnimSync.Animator == null)
|
serializedObject.ApplyModifiedProperties();
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var controller = m_AnimSync.Animator.runtimeAnimatorController as AnimatorController;
|
|
||||||
if (controller != null)
|
|
||||||
{
|
|
||||||
var showWarning = false;
|
|
||||||
EditorGUI.indentLevel += 1;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
foreach (var p in controller.parameters)
|
|
||||||
{
|
|
||||||
if (i >= NetworkAnimator.K_MaxAnimationParams)
|
|
||||||
{
|
|
||||||
showWarning = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool oldSend = m_AnimSync.GetParameterAutoSend(i);
|
|
||||||
bool send = EditorGUILayout.Toggle(p.name, oldSend);
|
|
||||||
if (send != oldSend)
|
|
||||||
{
|
|
||||||
m_AnimSync.SetParameterAutoSend(i, send);
|
|
||||||
EditorUtility.SetDirty(target);
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showWarning)
|
|
||||||
{
|
|
||||||
EditorGUILayout.HelpBox($"NetworkAnimator can only select between the first {NetworkAnimator.K_MaxAnimationParams} parameters in a mecanim controller", MessageType.Warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUI.indentLevel -= 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ namespace Unity.Netcode.Editor
|
|||||||
private static GUIStyle s_HelpBoxStyle;
|
private static GUIStyle s_HelpBoxStyle;
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
private SerializedProperty m_DontDestroyOnLoadProperty;
|
|
||||||
private SerializedProperty m_RunInBackgroundProperty;
|
private SerializedProperty m_RunInBackgroundProperty;
|
||||||
private SerializedProperty m_LogLevelProperty;
|
private SerializedProperty m_LogLevelProperty;
|
||||||
|
|
||||||
@@ -58,7 +57,7 @@ namespace Unity.Netcode.Editor
|
|||||||
|
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
if (type.IsSubclassOf(typeof(NetworkTransport)) && type.GetCustomAttributes(typeof(DontShowInTransportDropdownAttribute), true).Length == 0)
|
if (type.IsSubclassOf(typeof(NetworkTransport)) && !type.IsSubclassOf(typeof(TestingNetworkTransport)) && type != typeof(TestingNetworkTransport))
|
||||||
{
|
{
|
||||||
m_TransportTypes.Add(type);
|
m_TransportTypes.Add(type);
|
||||||
}
|
}
|
||||||
@@ -85,7 +84,6 @@ namespace Unity.Netcode.Editor
|
|||||||
m_NetworkManager = (NetworkManager)target;
|
m_NetworkManager = (NetworkManager)target;
|
||||||
|
|
||||||
// Base properties
|
// Base properties
|
||||||
m_DontDestroyOnLoadProperty = serializedObject.FindProperty(nameof(NetworkManager.DontDestroy));
|
|
||||||
m_RunInBackgroundProperty = serializedObject.FindProperty(nameof(NetworkManager.RunInBackground));
|
m_RunInBackgroundProperty = serializedObject.FindProperty(nameof(NetworkManager.RunInBackground));
|
||||||
m_LogLevelProperty = serializedObject.FindProperty(nameof(NetworkManager.LogLevel));
|
m_LogLevelProperty = serializedObject.FindProperty(nameof(NetworkManager.LogLevel));
|
||||||
m_NetworkConfigProperty = serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig));
|
m_NetworkConfigProperty = serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig));
|
||||||
@@ -112,7 +110,6 @@ namespace Unity.Netcode.Editor
|
|||||||
private void CheckNullProperties()
|
private void CheckNullProperties()
|
||||||
{
|
{
|
||||||
// Base properties
|
// Base properties
|
||||||
m_DontDestroyOnLoadProperty = serializedObject.FindProperty(nameof(NetworkManager.DontDestroy));
|
|
||||||
m_RunInBackgroundProperty = serializedObject.FindProperty(nameof(NetworkManager.RunInBackground));
|
m_RunInBackgroundProperty = serializedObject.FindProperty(nameof(NetworkManager.RunInBackground));
|
||||||
m_LogLevelProperty = serializedObject.FindProperty(nameof(NetworkManager.LogLevel));
|
m_LogLevelProperty = serializedObject.FindProperty(nameof(NetworkManager.LogLevel));
|
||||||
m_NetworkConfigProperty = serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig));
|
m_NetworkConfigProperty = serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig));
|
||||||
@@ -223,7 +220,6 @@ namespace Unity.Netcode.Editor
|
|||||||
if (!m_NetworkManager.IsServer && !m_NetworkManager.IsClient)
|
if (!m_NetworkManager.IsServer && !m_NetworkManager.IsClient)
|
||||||
{
|
{
|
||||||
serializedObject.Update();
|
serializedObject.Update();
|
||||||
EditorGUILayout.PropertyField(m_DontDestroyOnLoadProperty);
|
|
||||||
EditorGUILayout.PropertyField(m_RunInBackgroundProperty);
|
EditorGUILayout.PropertyField(m_RunInBackgroundProperty);
|
||||||
EditorGUILayout.PropertyField(m_LogLevelProperty);
|
EditorGUILayout.PropertyField(m_LogLevelProperty);
|
||||||
EditorGUILayout.Space();
|
EditorGUILayout.Space();
|
||||||
@@ -363,7 +359,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/docs/tutorials/goldenpath_series/goldenpath_foundation_module";
|
const string targetUrl = "https://docs-multiplayer.unity3d.com/docs/tools/install-tools";
|
||||||
const string infoIconName = "console.infoicon";
|
const string infoIconName = "console.infoicon";
|
||||||
|
|
||||||
if (PlayerPrefs.GetInt(InstallMultiplayerToolsTipDismissedPlayerPrefKey, 0) != 0)
|
if (PlayerPrefs.GetInt(InstallMultiplayerToolsTipDismissedPlayerPrefKey, 0) != 0)
|
||||||
|
|||||||
108
Editor/NetworkManagerHelper.cs
Normal file
108
Editor/NetworkManagerHelper.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Editor
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Specialized editor specific NetworkManager code
|
||||||
|
/// </summary>
|
||||||
|
public class NetworkManagerHelper : NetworkManager.INetworkManagerHelper
|
||||||
|
{
|
||||||
|
internal static NetworkManagerHelper Singleton;
|
||||||
|
|
||||||
|
// This is primarily to handle IntegrationTest scenarios where more than 1 NetworkManager could exist
|
||||||
|
private static Dictionary<NetworkManager, Transform> s_LastKnownNetworkManagerParents = new Dictionary<NetworkManager, Transform>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the singleton instance and registers for:
|
||||||
|
/// Hierarchy changed notification: to notify the user when they nest a NetworkManager
|
||||||
|
/// Play mode state change notification: to capture when entering or exiting play mode (currently only exiting)
|
||||||
|
/// </summary>
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
private static void InitializeOnload()
|
||||||
|
{
|
||||||
|
Singleton = new NetworkManagerHelper();
|
||||||
|
NetworkManager.NetworkManagerHelper = Singleton;
|
||||||
|
|
||||||
|
EditorApplication.playModeStateChanged -= EditorApplication_playModeStateChanged;
|
||||||
|
EditorApplication.hierarchyChanged -= EditorApplication_hierarchyChanged;
|
||||||
|
|
||||||
|
EditorApplication.playModeStateChanged += EditorApplication_playModeStateChanged;
|
||||||
|
EditorApplication.hierarchyChanged += EditorApplication_hierarchyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EditorApplication_playModeStateChanged(PlayModeStateChange playModeStateChange)
|
||||||
|
{
|
||||||
|
switch (playModeStateChange)
|
||||||
|
{
|
||||||
|
case PlayModeStateChange.ExitingEditMode:
|
||||||
|
{
|
||||||
|
s_LastKnownNetworkManagerParents.Clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EditorApplication_hierarchyChanged()
|
||||||
|
{
|
||||||
|
var allNetworkManagers = Resources.FindObjectsOfTypeAll<NetworkManager>();
|
||||||
|
foreach (var networkManager in allNetworkManagers)
|
||||||
|
{
|
||||||
|
networkManager.NetworkManagerCheckForParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles notifying the user, via display dialog window, that they have nested a NetworkManager.
|
||||||
|
/// When in edit mode it provides the option to automatically fix the issue
|
||||||
|
/// When in play mode it just notifies the user when entering play mode as well as when the user
|
||||||
|
/// tries to start a network session while a NetworkManager is still nested.
|
||||||
|
/// </summary>
|
||||||
|
public bool NotifyUserOfNestedNetworkManager(NetworkManager networkManager, bool ignoreNetworkManagerCache = false, bool editorTest = false)
|
||||||
|
{
|
||||||
|
var gameObject = networkManager.gameObject;
|
||||||
|
var transform = networkManager.transform;
|
||||||
|
var isParented = transform.root != transform;
|
||||||
|
|
||||||
|
var message = NetworkManager.GenerateNestedNetworkManagerMessage(transform);
|
||||||
|
if (s_LastKnownNetworkManagerParents.ContainsKey(networkManager) && !ignoreNetworkManagerCache)
|
||||||
|
{
|
||||||
|
// If we have already notified the user, then don't notify them again
|
||||||
|
if (s_LastKnownNetworkManagerParents[networkManager] == transform.root)
|
||||||
|
{
|
||||||
|
return isParented;
|
||||||
|
}
|
||||||
|
else // If we are no longer a child, then we can remove ourself from this list
|
||||||
|
if (transform.root == gameObject.transform)
|
||||||
|
{
|
||||||
|
s_LastKnownNetworkManagerParents.Remove(networkManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!EditorApplication.isUpdating && isParented)
|
||||||
|
{
|
||||||
|
if (!EditorApplication.isPlaying && !editorTest)
|
||||||
|
{
|
||||||
|
message += $"Click 'Auto-Fix' to automatically remove it from {transform.root.gameObject.name} or 'Manual-Fix' to fix it yourself in the hierarchy view.";
|
||||||
|
if (EditorUtility.DisplayDialog("Invalid Nested NetworkManager", message, "Auto-Fix", "Manual-Fix"))
|
||||||
|
{
|
||||||
|
transform.parent = null;
|
||||||
|
isParented = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s_LastKnownNetworkManagerParents.ContainsKey(networkManager) && isParented)
|
||||||
|
{
|
||||||
|
s_LastKnownNetworkManagerParents.Add(networkManager, networkManager.transform.root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isParented;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a8514b4eca0c7044d9b92faf9407ec93
|
guid: b26b53dc28ae1b5488bbbecc3e499bbc
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -8,18 +8,11 @@
|
|||||||
"includePlatforms": [
|
"includePlatforms": [
|
||||||
"Editor"
|
"Editor"
|
||||||
],
|
],
|
||||||
"excludePlatforms": [],
|
|
||||||
"allowUnsafeCode": false,
|
|
||||||
"overrideReferences": false,
|
|
||||||
"precompiledReferences": [],
|
|
||||||
"autoReferenced": true,
|
|
||||||
"defineConstraints": [],
|
|
||||||
"versionDefines": [
|
"versionDefines": [
|
||||||
{
|
{
|
||||||
"name": "com.unity.multiplayer.tools",
|
"name": "com.unity.multiplayer.tools",
|
||||||
"expression": "",
|
"expression": "",
|
||||||
"define": "MULTIPLAYER_TOOLS"
|
"define": "MULTIPLAYER_TOOLS"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"noEngineReferences": false
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
# Netcode for GameObjects
|
||||||
|
|
||||||
[](https://forum.unity.com/forums/multiplayer.26/) [](https://discord.gg/FM8SE9E)
|
[](https://forum.unity.com/forums/multiplayer.26/) [](https://discord.gg/FM8SE9E)
|
||||||
[](https://docs-multiplayer.unity3d.com/) [](https://docs-multiplayer.unity3d.com/docs/mlapi-api/introduction)
|
[](https://docs-multiplayer.unity3d.com/) [](https://docs-multiplayer.unity3d.com/docs/mlapi-api/introduction)
|
||||||
|
|
||||||
Netcode for GameObjects provides networking capabilities to GameObject & MonoBehaviour Unity workflows. The framework is interoperable with many low-level transports, including the official [Unity Transport Package](https://docs.unity3d.com/Packages/com.unity.transport@1.0/manual/index.html).
|
Netcode for GameObjects provides networking capabilities to GameObject & MonoBehaviour Unity workflows. The framework is interoperable with many low-level transports, including the official [Unity Transport Package](https://docs-multiplayer.unity3d.com/transport/1.0.0/introduction).
|
||||||
|
|
||||||
### Getting Started
|
### Getting Started
|
||||||
Visit the [Multiplayer Docs Site](https://docs-multiplayer.unity3d.com/) for package & API documentation, as well as information about several samples which leverage the Netcode for GameObjects package.
|
Visit the [Multiplayer Docs Site](https://docs-multiplayer.unity3d.com/) for package & API documentation, as well as information about several samples which leverage the Netcode for GameObjects package.
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ using System.Runtime.CompilerServices;
|
|||||||
[assembly: InternalsVisibleTo("Unity.Netcode.Editor.CodeGen")]
|
[assembly: InternalsVisibleTo("Unity.Netcode.Editor.CodeGen")]
|
||||||
[assembly: InternalsVisibleTo("Unity.Netcode.Editor")]
|
[assembly: InternalsVisibleTo("Unity.Netcode.Editor")]
|
||||||
[assembly: InternalsVisibleTo("TestProject.EditorTests")]
|
[assembly: InternalsVisibleTo("TestProject.EditorTests")]
|
||||||
[assembly: InternalsVisibleTo("TestProject.RuntimeTests")]
|
|
||||||
[assembly: InternalsVisibleTo("TestProject.ToolsIntegration.RuntimeTests")]
|
|
||||||
#endif
|
#endif
|
||||||
|
[assembly: InternalsVisibleTo("TestProject.ToolsIntegration.RuntimeTests")]
|
||||||
|
[assembly: InternalsVisibleTo("TestProject.RuntimeTests")]
|
||||||
[assembly: InternalsVisibleTo("Unity.Netcode.RuntimeTests")]
|
[assembly: InternalsVisibleTo("Unity.Netcode.RuntimeTests")]
|
||||||
|
[assembly: InternalsVisibleTo("Unity.Netcode.TestHelpers.Runtime")]
|
||||||
|
[assembly: InternalsVisibleTo("Unity.Netcode.Adapter.UTP")]
|
||||||
|
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Queue with a fixed size
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the queue</typeparam>
|
|
||||||
public sealed class FixedQueue<T>
|
|
||||||
{
|
|
||||||
private readonly T[] m_Queue;
|
|
||||||
private int m_QueueCount = 0;
|
|
||||||
private int m_QueueStart;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of enqueued objects
|
|
||||||
/// </summary>
|
|
||||||
public int Count => m_QueueCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the element at a given virtual index
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The virtual index to get the item from</param>
|
|
||||||
/// <returns>The element at the virtual index</returns>
|
|
||||||
public T this[int index] => m_Queue[(m_QueueStart + index) % m_Queue.Length];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new FixedQueue with a given size
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="maxSize">The size of the queue</param>
|
|
||||||
public FixedQueue(int maxSize)
|
|
||||||
{
|
|
||||||
m_Queue = new T[maxSize];
|
|
||||||
m_QueueStart = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enqueues an object
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="t"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool Enqueue(T t)
|
|
||||||
{
|
|
||||||
m_Queue[(m_QueueStart + m_QueueCount) % m_Queue.Length] = t;
|
|
||||||
if (++m_QueueCount > m_Queue.Length)
|
|
||||||
{
|
|
||||||
--m_QueueCount;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Dequeues an object
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public T Dequeue()
|
|
||||||
{
|
|
||||||
if (--m_QueueCount == -1)
|
|
||||||
{
|
|
||||||
throw new IndexOutOfRangeException("Cannot dequeue empty queue!");
|
|
||||||
}
|
|
||||||
|
|
||||||
T res = m_Queue[m_QueueStart];
|
|
||||||
m_QueueStart = (m_QueueStart + 1) % m_Queue.Length;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the element at a given virtual index
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The virtual index to get the item from</param>
|
|
||||||
/// <returns>The element at the virtual index</returns>
|
|
||||||
public T ElementAt(int index) => m_Queue[(m_QueueStart + index) % m_Queue.Length];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -141,15 +141,15 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not to enable Snapshot System for variable updates. Not supported in this version.
|
/// Whether or not to enable Snapshot System for variable updates. Not supported in this version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseSnapshotDelta { get; } = false;
|
public bool UseSnapshotDelta { get; internal set; } = false;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether or not to enable Snapshot System for spawn and despawn commands. Not supported in this version.
|
/// Whether or not to enable Snapshot System for spawn and despawn commands. Not supported in this version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseSnapshotSpawn { get; } = false;
|
public bool UseSnapshotSpawn { get; internal set; } = false;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When Snapshot System spawn is enabled: max size of Snapshot Messages. Meant to fit MTU.
|
/// When Snapshot System spawn is enabled: max size of Snapshot Messages. Meant to fit MTU.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int SnapshotMaxSpawnUsage { get; } = 1200;
|
public int SnapshotMaxSpawnUsage { get; } = 1000;
|
||||||
|
|
||||||
public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one)
|
public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one)
|
||||||
public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets)
|
public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets)
|
||||||
@@ -224,7 +224,7 @@ namespace Unity.Netcode
|
|||||||
return m_ConfigHash.Value;
|
return m_ConfigHash.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var writer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp);
|
var writer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, int.MaxValue);
|
||||||
using (writer)
|
using (writer)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(ProtocolVersion);
|
writer.WriteValueSafe(ProtocolVersion);
|
||||||
@@ -239,6 +239,8 @@ namespace Unity.Netcode
|
|||||||
writer.WriteValueSafe(sortedEntry.Key);
|
writer.WriteValueSafe(sortedEntry.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writer.WriteValueSafe(TickRate);
|
||||||
writer.WriteValueSafe(ConnectionApproval);
|
writer.WriteValueSafe(ConnectionApproval);
|
||||||
writer.WriteValueSafe(ForceSamePrefabs);
|
writer.WriteValueSafe(ForceSamePrefabs);
|
||||||
writer.WriteValueSafe(EnableSceneManagement);
|
writer.WriteValueSafe(EnableSceneManagement);
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class NetworkConstants
|
internal static class NetworkConstants
|
||||||
{
|
{
|
||||||
internal const string PROTOCOL_VERSION = "14.0.0";
|
internal const string PROTOCOL_VERSION = "15.0.0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,41 +15,56 @@ namespace Unity.Netcode
|
|||||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
// RuntimeAccessModifiersILPP will make this `protected`
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
internal enum __RpcExecStage
|
internal enum __RpcExecStage
|
||||||
#pragma warning restore IDE1006 // restore naming rule violation check
|
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Server = 1,
|
Server = 1,
|
||||||
Client = 2
|
Client = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
|
||||||
// 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);
|
||||||
#pragma warning restore IDE1006 // restore naming rule violation check
|
|
||||||
|
|
||||||
#pragma warning disable 414 // disable assigned but its value is never used
|
|
||||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
// RuntimeAccessModifiersILPP will make this `protected`
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
internal __RpcExecStage __rpc_exec_stage = __RpcExecStage.None;
|
internal __RpcExecStage __rpc_exec_stage = __RpcExecStage.None;
|
||||||
#pragma warning restore 414 // restore assigned but its value is never used
|
|
||||||
#pragma warning restore IDE1006 // restore naming rule violation check
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
|
|
||||||
#pragma warning disable 414 // disable assigned but its value is never used
|
private const int k_RpcMessageDefaultSize = 1024; // 1k
|
||||||
|
private const int k_RpcMessageMaximumSize = 1024 * 64; // 64k
|
||||||
|
|
||||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
// RuntimeAccessModifiersILPP will make this `protected`
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
internal void __sendServerRpc(FastBufferWriter writer, uint rpcMethodId, ServerRpcParams rpcParams, RpcDelivery delivery)
|
internal FastBufferWriter __beginSendServerRpc(uint rpcMethodId, ServerRpcParams serverRpcParams, RpcDelivery rpcDelivery)
|
||||||
#pragma warning restore 414 // restore assigned but its value is never used
|
|
||||||
#pragma warning restore IDE1006 // restore naming rule violation check
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
{
|
{
|
||||||
NetworkDelivery networkDelivery = NetworkDelivery.Reliable;
|
return new FastBufferWriter(k_RpcMessageDefaultSize, Allocator.Temp, k_RpcMessageMaximumSize);
|
||||||
switch (delivery)
|
}
|
||||||
|
|
||||||
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
|
internal void __endSendServerRpc(ref FastBufferWriter bufferWriter, uint rpcMethodId, ServerRpcParams serverRpcParams, RpcDelivery rpcDelivery)
|
||||||
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
|
{
|
||||||
|
var serverRpcMessage = new ServerRpcMessage
|
||||||
{
|
{
|
||||||
|
Metadata = new RpcMetadata
|
||||||
|
{
|
||||||
|
NetworkObjectId = NetworkObjectId,
|
||||||
|
NetworkBehaviourId = NetworkBehaviourId,
|
||||||
|
NetworkRpcMethodId = rpcMethodId,
|
||||||
|
},
|
||||||
|
WriteBuffer = bufferWriter
|
||||||
|
};
|
||||||
|
|
||||||
|
NetworkDelivery networkDelivery;
|
||||||
|
switch (rpcDelivery)
|
||||||
|
{
|
||||||
|
default:
|
||||||
case RpcDelivery.Reliable:
|
case RpcDelivery.Reliable:
|
||||||
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
||||||
break;
|
break;
|
||||||
case RpcDelivery.Unreliable:
|
case RpcDelivery.Unreliable:
|
||||||
if (writer.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort))
|
if (bufferWriter.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE)
|
||||||
{
|
{
|
||||||
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
||||||
}
|
}
|
||||||
@@ -57,41 +72,33 @@ namespace Unity.Netcode
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = new RpcMessage
|
var rpcWriteSize = 0;
|
||||||
{
|
|
||||||
Header = new RpcMessage.HeaderData
|
|
||||||
{
|
|
||||||
Type = RpcMessage.RpcType.Server,
|
|
||||||
NetworkObjectId = NetworkObjectId,
|
|
||||||
NetworkBehaviourId = NetworkBehaviourId,
|
|
||||||
NetworkMethodId = rpcMethodId
|
|
||||||
},
|
|
||||||
RpcData = writer
|
|
||||||
};
|
|
||||||
|
|
||||||
var rpcMessageSize = 0;
|
|
||||||
|
|
||||||
// If we are a server/host then we just no op and send to ourself
|
// If we are a server/host then we just no op and send to ourself
|
||||||
if (IsHost || IsServer)
|
if (IsHost || IsServer)
|
||||||
{
|
{
|
||||||
using var tempBuffer = new FastBufferReader(writer, Allocator.Temp);
|
using var tempBuffer = new FastBufferReader(bufferWriter, Allocator.Temp);
|
||||||
var context = new NetworkContext
|
var context = new NetworkContext
|
||||||
{
|
{
|
||||||
SenderId = NetworkManager.ServerClientId,
|
SenderId = NetworkManager.ServerClientId,
|
||||||
Timestamp = Time.realtimeSinceStartup,
|
Timestamp = Time.realtimeSinceStartup,
|
||||||
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.
|
||||||
// Passing false to canDefer prevents it being accessed.
|
// RpcMessage doesn't access this stuff so it's just left empty.
|
||||||
Header = new MessageHeader()
|
Header = new MessageHeader(),
|
||||||
|
SerializedHeaderSize = 0,
|
||||||
|
MessageSize = 0
|
||||||
};
|
};
|
||||||
message.Handle(tempBuffer, context, NetworkManager, NetworkManager.ServerClientId, false);
|
serverRpcMessage.ReadBuffer = tempBuffer;
|
||||||
rpcMessageSize = tempBuffer.Length;
|
serverRpcMessage.Handle(ref context);
|
||||||
|
rpcWriteSize = tempBuffer.Length;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rpcMessageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ServerClientId);
|
rpcWriteSize = NetworkManager.SendMessage(ref serverRpcMessage, networkDelivery, NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bufferWriter.Dispose();
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
||||||
@@ -101,26 +108,44 @@ namespace Unity.Netcode
|
|||||||
NetworkObject,
|
NetworkObject,
|
||||||
rpcMethodName,
|
rpcMethodName,
|
||||||
__getTypeName(),
|
__getTypeName(),
|
||||||
rpcMessageSize);
|
rpcWriteSize);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable 414 // disable assigned but its value is never used
|
|
||||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
// RuntimeAccessModifiersILPP will make this `protected`
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
internal unsafe void __sendClientRpc(FastBufferWriter writer, uint rpcMethodId, ClientRpcParams rpcParams, RpcDelivery delivery)
|
internal FastBufferWriter __beginSendClientRpc(uint rpcMethodId, ClientRpcParams clientRpcParams, RpcDelivery rpcDelivery)
|
||||||
#pragma warning disable 414 // disable assigned but its value is never used
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
|
||||||
{
|
{
|
||||||
NetworkDelivery networkDelivery = NetworkDelivery.Reliable;
|
return new FastBufferWriter(k_RpcMessageDefaultSize, Allocator.Temp, k_RpcMessageMaximumSize);
|
||||||
switch (delivery)
|
}
|
||||||
|
|
||||||
|
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||||
|
// RuntimeAccessModifiersILPP will make this `protected`
|
||||||
|
internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMethodId, ClientRpcParams clientRpcParams, RpcDelivery rpcDelivery)
|
||||||
|
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||||
|
{
|
||||||
|
var clientRpcMessage = new ClientRpcMessage
|
||||||
{
|
{
|
||||||
|
Metadata = new RpcMetadata
|
||||||
|
{
|
||||||
|
NetworkObjectId = NetworkObjectId,
|
||||||
|
NetworkBehaviourId = NetworkBehaviourId,
|
||||||
|
NetworkRpcMethodId = rpcMethodId,
|
||||||
|
},
|
||||||
|
WriteBuffer = bufferWriter
|
||||||
|
};
|
||||||
|
|
||||||
|
NetworkDelivery networkDelivery;
|
||||||
|
switch (rpcDelivery)
|
||||||
|
{
|
||||||
|
default:
|
||||||
case RpcDelivery.Reliable:
|
case RpcDelivery.Reliable:
|
||||||
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
||||||
break;
|
break;
|
||||||
case RpcDelivery.Unreliable:
|
case RpcDelivery.Unreliable:
|
||||||
if (writer.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort))
|
if (bufferWriter.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE)
|
||||||
{
|
{
|
||||||
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
||||||
}
|
}
|
||||||
@@ -128,26 +153,15 @@ namespace Unity.Netcode
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = new RpcMessage
|
var rpcWriteSize = 0;
|
||||||
{
|
|
||||||
Header = new RpcMessage.HeaderData
|
|
||||||
{
|
|
||||||
Type = RpcMessage.RpcType.Client,
|
|
||||||
NetworkObjectId = NetworkObjectId,
|
|
||||||
NetworkBehaviourId = NetworkBehaviourId,
|
|
||||||
NetworkMethodId = rpcMethodId
|
|
||||||
},
|
|
||||||
RpcData = writer
|
|
||||||
};
|
|
||||||
int messageSize;
|
|
||||||
|
|
||||||
// We check to see if we need to shortcut for the case where we are the host/server and we can send a clientRPC
|
// We check to see if we need to shortcut for the case where we are the host/server and we can send a clientRPC
|
||||||
// to ourself. Sadly we have to figure that out from the list of clientIds :(
|
// to ourself. Sadly we have to figure that out from the list of clientIds :(
|
||||||
bool shouldSendToHost = false;
|
bool shouldSendToHost = false;
|
||||||
|
|
||||||
if (rpcParams.Send.TargetClientIds != null)
|
if (clientRpcParams.Send.TargetClientIds != null)
|
||||||
{
|
{
|
||||||
foreach (var clientId in rpcParams.Send.TargetClientIds)
|
foreach (var clientId in clientRpcParams.Send.TargetClientIds)
|
||||||
{
|
{
|
||||||
if (clientId == NetworkManager.ServerClientId)
|
if (clientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
@@ -156,11 +170,11 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageSize = NetworkManager.SendMessage(message, networkDelivery, in rpcParams.Send.TargetClientIds);
|
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, in clientRpcParams.Send.TargetClientIds);
|
||||||
}
|
}
|
||||||
else if (rpcParams.Send.TargetClientIdsNativeArray != null)
|
else if (clientRpcParams.Send.TargetClientIdsNativeArray != null)
|
||||||
{
|
{
|
||||||
foreach (var clientId in rpcParams.Send.TargetClientIdsNativeArray)
|
foreach (var clientId in clientRpcParams.Send.TargetClientIdsNativeArray)
|
||||||
{
|
{
|
||||||
if (clientId == NetworkManager.ServerClientId)
|
if (clientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
@@ -169,31 +183,35 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageSize = NetworkManager.SendMessage(message, networkDelivery, rpcParams.Send.TargetClientIdsNativeArray.Value);
|
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
shouldSendToHost = IsHost;
|
shouldSendToHost = IsHost;
|
||||||
messageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ConnectedClientsIds);
|
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, NetworkManager.ConnectedClientsIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are a server/host then we just no op and send to ourself
|
// If we are a server/host then we just no op and send to ourself
|
||||||
if (shouldSendToHost)
|
if (shouldSendToHost)
|
||||||
{
|
{
|
||||||
using var tempBuffer = new FastBufferReader(writer, Allocator.Temp);
|
using var tempBuffer = new FastBufferReader(bufferWriter, Allocator.Temp);
|
||||||
var context = new NetworkContext
|
var context = new NetworkContext
|
||||||
{
|
{
|
||||||
SenderId = NetworkManager.ServerClientId,
|
SenderId = NetworkManager.ServerClientId,
|
||||||
Timestamp = Time.realtimeSinceStartup,
|
Timestamp = Time.realtimeSinceStartup,
|
||||||
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.
|
||||||
// Passing false to canDefer prevents it being accessed.
|
// RpcMessage doesn't access this stuff so it's just left empty.
|
||||||
Header = new MessageHeader()
|
Header = new MessageHeader(),
|
||||||
|
SerializedHeaderSize = 0,
|
||||||
|
MessageSize = 0
|
||||||
};
|
};
|
||||||
message.Handle(tempBuffer, context, NetworkManager, NetworkManager.ServerClientId, false);
|
clientRpcMessage.ReadBuffer = tempBuffer;
|
||||||
messageSize = tempBuffer.Length;
|
clientRpcMessage.Handle(ref context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bufferWriter.Dispose();
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
||||||
{
|
{
|
||||||
@@ -204,7 +222,7 @@ namespace Unity.Netcode
|
|||||||
NetworkObject,
|
NetworkObject,
|
||||||
rpcMethodName,
|
rpcMethodName,
|
||||||
__getTypeName(),
|
__getTypeName(),
|
||||||
messageSize);
|
rpcWriteSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -258,9 +276,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
// Only server can MODIFY. So allow modification if network is either not running or we are server
|
// Only server can MODIFY. So allow modification if network is either not running or we are server
|
||||||
return !m_NetworkObject ||
|
return !m_NetworkObject ||
|
||||||
(m_NetworkObject.NetworkManager == null ||
|
m_NetworkObject.NetworkManager == null ||
|
||||||
!m_NetworkObject.NetworkManager.IsListening ||
|
m_NetworkObject.NetworkManager.IsListening == false ||
|
||||||
m_NetworkObject.NetworkManager.IsServer);
|
m_NetworkObject.NetworkManager.IsServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -282,9 +300,17 @@ namespace Unity.Netcode
|
|||||||
m_NetworkObject = GetComponentInParent<NetworkObject>();
|
m_NetworkObject = GetComponentInParent<NetworkObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_NetworkObject == null && NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
// ShutdownInProgress check:
|
||||||
|
// This prevents an edge case scenario where the NetworkManager is shutting down but user code
|
||||||
|
// in Update and/or in FixedUpdate could still be checking NetworkBehaviour.NetworkObject directly (i.e. does it exist?)
|
||||||
|
// or NetworkBehaviour.IsSpawned (i.e. to early exit if not spawned) which, in turn, could generate several Warning messages
|
||||||
|
// per spawned NetworkObject. Checking for ShutdownInProgress prevents these unnecessary LogWarning messages.
|
||||||
|
if (m_NetworkObject == null && (NetworkManager.Singleton == null || !NetworkManager.Singleton.ShutdownInProgress))
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"Could not get {nameof(NetworkObject)} for the {nameof(NetworkBehaviour)}. Are you missing a {nameof(NetworkObject)} component?");
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"Could not get {nameof(NetworkObject)} for the {nameof(NetworkBehaviour)}. Are you missing a {nameof(NetworkObject)} component?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_NetworkObject;
|
return m_NetworkObject;
|
||||||
@@ -343,10 +369,7 @@ namespace Unity.Netcode
|
|||||||
InitializeVariables();
|
InitializeVariables();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InternalOnNetworkDespawn()
|
internal void InternalOnNetworkDespawn() { }
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when the local client gains ownership of this object
|
/// Gets called when the local client gains ownership of this object
|
||||||
@@ -422,8 +445,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (instance == null)
|
if (instance == null)
|
||||||
{
|
{
|
||||||
instance = (NetworkVariableBase)Activator.CreateInstance(fieldType, true);
|
throw new Exception($"{GetType().FullName}.{sortedFields[i].Name} cannot be null. All {nameof(NetworkVariableBase)} instances must be initialized.");
|
||||||
sortedFields[i].SetValue(this, instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.Initialize(this);
|
instance.Initialize(this);
|
||||||
@@ -536,7 +558,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 && clientId == NetworkManager.ServerClientId)
|
if (IsServer && clientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp);
|
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
||||||
using (tmpWriter)
|
using (tmpWriter)
|
||||||
{
|
{
|
||||||
message.Serialize(tmpWriter);
|
message.Serialize(tmpWriter);
|
||||||
@@ -544,7 +566,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NetworkManager.SendMessage(message, m_DeliveryTypesForNetworkVariableGroups[j], clientId);
|
NetworkManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool m_ShuttingDown;
|
||||||
|
private bool m_StopProcessingMessages;
|
||||||
|
|
||||||
private class NetworkManagerHooks : INetworkHooks
|
private class NetworkManagerHooks : INetworkHooks
|
||||||
{
|
{
|
||||||
private NetworkManager m_NetworkManager;
|
private NetworkManager m_NetworkManager;
|
||||||
@@ -82,11 +85,11 @@ namespace Unity.Netcode
|
|||||||
m_NetworkManager = manager;
|
m_NetworkManager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery)
|
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery, int messageSizeBytes)
|
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +119,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
|
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
|
||||||
{
|
{
|
||||||
return true;
|
return !m_NetworkManager.m_StopProcessingMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
|
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
|
||||||
@@ -134,7 +137,15 @@ namespace Unity.Netcode
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return !m_NetworkManager.m_StopProcessingMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,11 +205,6 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public NetworkTime ServerTime => NetworkTickSystem?.ServerTime ?? default;
|
public NetworkTime ServerTime => NetworkTickSystem?.ServerTime ?? default;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets if the NetworkManager should be marked as DontDestroyOnLoad
|
|
||||||
/// </summary>
|
|
||||||
[HideInInspector] public bool DontDestroy = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets if the application should be set to run in background
|
/// Gets or sets if the application should be set to run in background
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -333,6 +339,9 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsConnectedClient { get; internal set; }
|
public bool IsConnectedClient { get; internal set; }
|
||||||
|
|
||||||
|
|
||||||
|
public bool ShutdownInProgress { get { return m_ShuttingDown; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The callback to invoke once a client connects. This callback is only ran on the server and on the local client that connects.
|
/// The callback to invoke once a client connects. This callback is only ran on the server and on the local client that connects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -345,8 +354,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<ulong> OnClientDisconnectCallback = null;
|
public event Action<ulong> OnClientDisconnectCallback = null;
|
||||||
|
|
||||||
internal void InvokeOnClientDisconnectCallback(ulong clientId) => OnClientDisconnectCallback?.Invoke(clientId);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The callback to invoke once the server is ready
|
/// The callback to invoke once the server is ready
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -486,6 +493,13 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private void Initialize(bool server)
|
private void Initialize(bool server)
|
||||||
{
|
{
|
||||||
|
// Don't allow the user to start a network session if the NetworkManager is
|
||||||
|
// still parented under another GameObject
|
||||||
|
if (NetworkManagerCheckForParent(true))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogInfo(nameof(Initialize));
|
NetworkLog.LogInfo(nameof(Initialize));
|
||||||
@@ -549,6 +563,8 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkConfig.NetworkTransport.NetworkMetrics = NetworkMetrics;
|
||||||
|
|
||||||
//This 'if' should never enter
|
//This 'if' should never enter
|
||||||
if (SnapshotSystem != null)
|
if (SnapshotSystem != null)
|
||||||
{
|
{
|
||||||
@@ -556,8 +572,6 @@ namespace Unity.Netcode
|
|||||||
SnapshotSystem = null;
|
SnapshotSystem = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
SnapshotSystem = new SnapshotSystem(this);
|
|
||||||
|
|
||||||
if (server)
|
if (server)
|
||||||
{
|
{
|
||||||
NetworkTimeSystem = NetworkTimeSystem.ServerTimeSystem();
|
NetworkTimeSystem = NetworkTimeSystem.ServerTimeSystem();
|
||||||
@@ -570,6 +584,8 @@ namespace Unity.Netcode
|
|||||||
NetworkTickSystem = new NetworkTickSystem(NetworkConfig.TickRate, 0, 0);
|
NetworkTickSystem = new NetworkTickSystem(NetworkConfig.TickRate, 0, 0);
|
||||||
NetworkTickSystem.Tick += OnNetworkManagerTick;
|
NetworkTickSystem.Tick += OnNetworkManagerTick;
|
||||||
|
|
||||||
|
SnapshotSystem = new SnapshotSystem(this, NetworkConfig, NetworkTickSystem);
|
||||||
|
|
||||||
this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate);
|
this.RegisterNetworkUpdate(NetworkUpdateStage.PreUpdate);
|
||||||
|
|
||||||
// This is used to remove entries not needed or invalid
|
// This is used to remove entries not needed or invalid
|
||||||
@@ -577,6 +593,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
// Always clear our prefab override links before building
|
// Always clear our prefab override links before building
|
||||||
NetworkConfig.NetworkPrefabOverrideLinks.Clear();
|
NetworkConfig.NetworkPrefabOverrideLinks.Clear();
|
||||||
|
NetworkConfig.OverrideToNetworkPrefab.Clear();
|
||||||
|
|
||||||
// Build the NetworkPrefabOverrideLinks dictionary
|
// Build the NetworkPrefabOverrideLinks dictionary
|
||||||
for (int i = 0; i < NetworkConfig.NetworkPrefabs.Count; i++)
|
for (int i = 0; i < NetworkConfig.NetworkPrefabs.Count; i++)
|
||||||
@@ -773,7 +790,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
NetworkConfig.NetworkTransport.OnTransportEvent += HandleRawTransportPoll;
|
NetworkConfig.NetworkTransport.OnTransportEvent += HandleRawTransportPoll;
|
||||||
|
|
||||||
NetworkConfig.NetworkTransport.Initialize();
|
NetworkConfig.NetworkTransport.Initialize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -937,11 +954,6 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private void OnEnable()
|
private void OnEnable()
|
||||||
{
|
{
|
||||||
if (DontDestroy)
|
|
||||||
{
|
|
||||||
DontDestroyOnLoad(gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RunInBackground)
|
if (RunInBackground)
|
||||||
{
|
{
|
||||||
Application.runInBackground = true;
|
Application.runInBackground = true;
|
||||||
@@ -951,6 +963,11 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
SetSingleton();
|
SetSingleton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!NetworkManagerCheckForParent())
|
||||||
|
{
|
||||||
|
DontDestroyOnLoad(gameObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
@@ -958,6 +975,48 @@ namespace Unity.Netcode
|
|||||||
UnityEngine.SceneManagement.SceneManager.sceneUnloaded += OnSceneUnloaded;
|
UnityEngine.SceneManagement.SceneManager.sceneUnloaded += OnSceneUnloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle runtime detection for parenting the NetworkManager's GameObject under another GameObject
|
||||||
|
/// </summary>
|
||||||
|
private void OnTransformParentChanged()
|
||||||
|
{
|
||||||
|
NetworkManagerCheckForParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the NetworkManager's GameObject is parented under another GameObject and
|
||||||
|
/// notifies the user that this is not allowed for the NetworkManager.
|
||||||
|
/// </summary>
|
||||||
|
internal bool NetworkManagerCheckForParent(bool ignoreNetworkManagerCache = false)
|
||||||
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
var isParented = NetworkManagerHelper.NotifyUserOfNestedNetworkManager(this, ignoreNetworkManagerCache);
|
||||||
|
#else
|
||||||
|
var isParented = transform.root != transform;
|
||||||
|
if (isParented)
|
||||||
|
{
|
||||||
|
throw new Exception(GenerateNestedNetworkManagerMessage(transform));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return isParented;
|
||||||
|
}
|
||||||
|
|
||||||
|
static internal string GenerateNestedNetworkManagerMessage(Transform transform)
|
||||||
|
{
|
||||||
|
return $"{transform.name} is nested under {transform.root.name}. NetworkManager cannot be nested.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
static internal INetworkManagerHelper NetworkManagerHelper;
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for NetworkManagerHelper
|
||||||
|
/// </summary>
|
||||||
|
internal interface INetworkManagerHelper
|
||||||
|
{
|
||||||
|
bool NotifyUserOfNestedNetworkManager(NetworkManager networkManager, bool ignoreNetworkManagerCache = false, bool editorTest = false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Ensures that the NetworkManager is cleaned up before OnDestroy is run on NetworkObjects and NetworkBehaviours when unloading a scene with a NetworkManager
|
// Ensures that the NetworkManager is cleaned up before OnDestroy is run on NetworkObjects and NetworkBehaviours when unloading a scene with a NetworkManager
|
||||||
private void OnSceneUnloaded(Scene scene)
|
private void OnSceneUnloaded(Scene scene)
|
||||||
{
|
{
|
||||||
@@ -976,7 +1035,7 @@ namespace Unity.Netcode
|
|||||||
// Note that this gets also called manually by OnSceneUnloaded and OnApplicationQuit
|
// Note that this gets also called manually by OnSceneUnloaded and OnApplicationQuit
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
Shutdown();
|
ShutdownInternal();
|
||||||
|
|
||||||
UnityEngine.SceneManagement.SceneManager.sceneUnloaded -= OnSceneUnloaded;
|
UnityEngine.SceneManagement.SceneManager.sceneUnloaded -= OnSceneUnloaded;
|
||||||
|
|
||||||
@@ -996,13 +1055,30 @@ namespace Unity.Netcode
|
|||||||
/// Globally shuts down the library.
|
/// Globally shuts down the library.
|
||||||
/// Disconnects clients if connected and stops server if running.
|
/// Disconnects clients if connected and stops server if running.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Shutdown()
|
/// <param name="discardMessageQueue">
|
||||||
|
/// If false, any messages that are currently in the incoming queue will be handled,
|
||||||
|
/// and any messages in the outgoing queue will be sent, before the shutdown is processed.
|
||||||
|
/// If true, NetworkManager will shut down immediately, and any unprocessed or unsent messages
|
||||||
|
/// will be discarded.
|
||||||
|
/// </param>
|
||||||
|
public void Shutdown(bool discardMessageQueue = false)
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogInfo(nameof(Shutdown));
|
NetworkLog.LogInfo(nameof(Shutdown));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_ShuttingDown = true;
|
||||||
|
m_StopProcessingMessages = discardMessageQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ShutdownInternal()
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
|
{
|
||||||
|
NetworkLog.LogInfo(nameof(ShutdownInternal));
|
||||||
|
}
|
||||||
|
|
||||||
if (IsServer)
|
if (IsServer)
|
||||||
{
|
{
|
||||||
// make sure all messages are flushed before transport disconnect clients
|
// make sure all messages are flushed before transport disconnect clients
|
||||||
@@ -1075,11 +1151,15 @@ namespace Unity.Netcode
|
|||||||
MessagingSystem = null;
|
MessagingSystem = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkConfig.NetworkTransport.OnTransportEvent -= HandleRawTransportPoll;
|
if (NetworkConfig?.NetworkTransport != null)
|
||||||
|
{
|
||||||
|
NetworkConfig.NetworkTransport.OnTransportEvent -= HandleRawTransportPoll;
|
||||||
|
}
|
||||||
|
|
||||||
if (SpawnManager != null)
|
if (SpawnManager != null)
|
||||||
{
|
{
|
||||||
SpawnManager.DestroyNonSceneObjects();
|
SpawnManager.CleanupAllTriggers();
|
||||||
|
SpawnManager.DespawnAndDestroyNetworkObjects();
|
||||||
SpawnManager.ServerResetShudownStateForSceneObjects();
|
SpawnManager.ServerResetShudownStateForSceneObjects();
|
||||||
|
|
||||||
SpawnManager = null;
|
SpawnManager = null;
|
||||||
@@ -1114,6 +1194,8 @@ namespace Unity.Netcode
|
|||||||
m_TransportIdToClientIdMap.Clear();
|
m_TransportIdToClientIdMap.Clear();
|
||||||
|
|
||||||
IsListening = false;
|
IsListening = false;
|
||||||
|
m_ShuttingDown = false;
|
||||||
|
m_StopProcessingMessages = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// INetworkUpdateSystem
|
// INetworkUpdateSystem
|
||||||
@@ -1167,6 +1249,11 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_ShuttingDown && m_StopProcessingMessages)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Only update RTT here, server time is updated by time sync messages
|
// Only update RTT here, server time is updated by time sync messages
|
||||||
var reset = NetworkTimeSystem.Advance(Time.deltaTime);
|
var reset = NetworkTimeSystem.Advance(Time.deltaTime);
|
||||||
if (reset)
|
if (reset)
|
||||||
@@ -1183,9 +1270,18 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private void OnNetworkPostLateUpdate()
|
private void OnNetworkPostLateUpdate()
|
||||||
{
|
{
|
||||||
MessagingSystem.ProcessSendQueues();
|
|
||||||
NetworkMetrics.DispatchFrame();
|
if (!m_ShuttingDown || !m_StopProcessingMessages)
|
||||||
|
{
|
||||||
|
MessagingSystem.ProcessSendQueues();
|
||||||
|
NetworkMetrics.DispatchFrame();
|
||||||
|
}
|
||||||
SpawnManager.CleanupStaleTriggers();
|
SpawnManager.CleanupStaleTriggers();
|
||||||
|
|
||||||
|
if (m_ShuttingDown)
|
||||||
|
{
|
||||||
|
ShutdownInternal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1214,7 +1310,7 @@ namespace Unity.Netcode
|
|||||||
ShouldSendConnectionData = NetworkConfig.ConnectionApproval,
|
ShouldSendConnectionData = NetworkConfig.ConnectionApproval,
|
||||||
ConnectionData = NetworkConfig.ConnectionData
|
ConnectionData = NetworkConfig.ConnectionData
|
||||||
};
|
};
|
||||||
SendMessage(message, NetworkDelivery.ReliableSequenced, ServerClientId);
|
SendMessage(ref message, NetworkDelivery.ReliableSequenced, ServerClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerator ApprovalTimeout(ulong clientId)
|
private IEnumerator ApprovalTimeout(ulong clientId)
|
||||||
@@ -1239,12 +1335,12 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong TransportIdToClientId(ulong transportId)
|
internal ulong TransportIdToClientId(ulong transportId)
|
||||||
{
|
{
|
||||||
return transportId == m_ServerTransportId ? ServerClientId : m_TransportIdToClientIdMap[transportId];
|
return transportId == m_ServerTransportId ? ServerClientId : m_TransportIdToClientIdMap[transportId];
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong ClientIdToTransportId(ulong clientId)
|
internal ulong ClientIdToTransportId(ulong clientId)
|
||||||
{
|
{
|
||||||
return clientId == ServerClientId ? m_ServerTransportId : m_ClientIdToTransportIdMap[clientId];
|
return clientId == ServerClientId ? m_ServerTransportId : m_ClientIdToTransportIdMap[clientId];
|
||||||
}
|
}
|
||||||
@@ -1259,7 +1355,20 @@ namespace Unity.Netcode
|
|||||||
s_TransportConnect.Begin();
|
s_TransportConnect.Begin();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
clientId = m_NextClientId++;
|
// Assumptions:
|
||||||
|
// - When server receives a connection, it *must be* a client
|
||||||
|
// - When client receives one, it *must be* the server
|
||||||
|
// Client's can't connect to or talk to other clients.
|
||||||
|
// Server is a sentinel so only one exists, if we are server, we can't be
|
||||||
|
// connecting to it.
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
clientId = m_NextClientId++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clientId = ServerClientId;
|
||||||
|
}
|
||||||
m_ClientIdToTransportIdMap[clientId] = transportId;
|
m_ClientIdToTransportIdMap[clientId] = transportId;
|
||||||
m_TransportIdToClientIdMap[transportId] = clientId;
|
m_TransportIdToClientIdMap[transportId] = clientId;
|
||||||
|
|
||||||
@@ -1312,6 +1421,8 @@ namespace Unity.Netcode
|
|||||||
#endif
|
#endif
|
||||||
clientId = TransportIdToClientId(clientId);
|
clientId = TransportIdToClientId(clientId);
|
||||||
|
|
||||||
|
OnClientDisconnectCallback?.Invoke(clientId);
|
||||||
|
|
||||||
m_TransportIdToClientIdMap.Remove(transportId);
|
m_TransportIdToClientIdMap.Remove(transportId);
|
||||||
m_ClientIdToTransportIdMap.Remove(clientId);
|
m_ClientIdToTransportIdMap.Remove(clientId);
|
||||||
|
|
||||||
@@ -1328,9 +1439,6 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
OnClientDisconnectCallback?.Invoke(clientId);
|
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
s_TransportDisconnect.End();
|
s_TransportDisconnect.End();
|
||||||
#endif
|
#endif
|
||||||
@@ -1338,7 +1446,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<TMessageType, TClientIdListType>(in TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds)
|
internal unsafe int SendMessage<TMessageType, TClientIdListType>(ref TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds)
|
||||||
where TMessageType : INetworkMessage
|
where TMessageType : INetworkMessage
|
||||||
where TClientIdListType : IReadOnlyList<ulong>
|
where TClientIdListType : IReadOnlyList<ulong>
|
||||||
{
|
{
|
||||||
@@ -1361,12 +1469,18 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return MessagingSystem.SendMessage(message, delivery, nonServerIds, newIdx);
|
return MessagingSystem.SendMessage(ref message, delivery, nonServerIds, newIdx);
|
||||||
}
|
}
|
||||||
return MessagingSystem.SendMessage(message, delivery, clientIds);
|
// else
|
||||||
|
if (clientIds.Count != 1 || clientIds[0] != ServerClientId)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Clients may only send messages to {nameof(ServerClientId)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return MessagingSystem.SendMessage(ref message, delivery, clientIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<T>(in T message, NetworkDelivery delivery,
|
internal unsafe int SendMessage<T>(ref T message, NetworkDelivery delivery,
|
||||||
ulong* clientIds, int numClientIds)
|
ulong* clientIds, int numClientIds)
|
||||||
where T : INetworkMessage
|
where T : INetworkMessage
|
||||||
{
|
{
|
||||||
@@ -1389,19 +1503,24 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return MessagingSystem.SendMessage(message, delivery, nonServerIds, newIdx);
|
return MessagingSystem.SendMessage(ref message, delivery, nonServerIds, newIdx);
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
if (numClientIds != 1 || clientIds[0] != ServerClientId)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Clients may only send messages to {nameof(ServerClientId)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return MessagingSystem.SendMessage(message, delivery, clientIds, numClientIds);
|
return MessagingSystem.SendMessage(ref message, delivery, clientIds, numClientIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<T>(in T message, NetworkDelivery delivery, in NativeArray<ulong> clientIds)
|
internal unsafe int SendMessage<T>(ref T message, NetworkDelivery delivery, in NativeArray<ulong> clientIds)
|
||||||
where T : INetworkMessage
|
where T : INetworkMessage
|
||||||
{
|
{
|
||||||
return SendMessage(message, delivery, (ulong*)clientIds.GetUnsafePtr(), clientIds.Length);
|
return SendMessage(ref message, delivery, (ulong*)clientIds.GetUnsafePtr(), clientIds.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal int SendMessage<T>(in T message, NetworkDelivery delivery, ulong clientId)
|
internal int SendMessage<T>(ref T message, NetworkDelivery delivery, ulong clientId)
|
||||||
where T : INetworkMessage
|
where T : INetworkMessage
|
||||||
{
|
{
|
||||||
// Prevent server sending to itself
|
// Prevent server sending to itself
|
||||||
@@ -1409,7 +1528,18 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return MessagingSystem.SendMessage(message, delivery, clientId);
|
|
||||||
|
if (!IsServer && clientId != ServerClientId)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Clients may only send messages to {nameof(ServerClientId)}");
|
||||||
|
}
|
||||||
|
return MessagingSystem.SendMessage(ref message, delivery, clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int SendPreSerializedMessage<T>(in FastBufferWriter writer, int maxSize, ref T message, NetworkDelivery delivery, ulong clientId)
|
||||||
|
where T : INetworkMessage
|
||||||
|
{
|
||||||
|
return MessagingSystem.SendPreSerializedMessage(writer, maxSize, ref message, delivery, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void HandleIncomingData(ulong clientId, ArraySegment<byte> payload, float receiveTime)
|
internal void HandleIncomingData(ulong clientId, ArraySegment<byte> payload, float receiveTime)
|
||||||
@@ -1433,7 +1563,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (!IsServer)
|
if (!IsServer)
|
||||||
{
|
{
|
||||||
throw new NotServerException("Only server can disconnect remote clients. Use StopClient instead.");
|
throw new NotServerException($"Only server can disconnect remote clients. Please use `{nameof(Shutdown)}()` instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
OnClientDisconnectFromServer(clientId);
|
OnClientDisconnectFromServer(clientId);
|
||||||
@@ -1530,7 +1660,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
Tick = NetworkTickSystem.ServerTime.Tick
|
Tick = NetworkTickSystem.ServerTime.Tick
|
||||||
};
|
};
|
||||||
SendMessage(message, NetworkDelivery.Unreliable, ConnectedClientsIds);
|
SendMessage(ref message, NetworkDelivery.Unreliable, ConnectedClientsIds);
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
s_SyncTime.End();
|
s_SyncTime.End();
|
||||||
#endif
|
#endif
|
||||||
@@ -1577,12 +1707,11 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (SpawnManager.SpawnedObjectsList.Count != 0)
|
if (SpawnManager.SpawnedObjectsList.Count != 0)
|
||||||
{
|
{
|
||||||
message.SceneObjectCount = SpawnManager.SpawnedObjectsList.Count;
|
|
||||||
message.SpawnedObjectsList = SpawnManager.SpawnedObjectsList;
|
message.SpawnedObjectsList = SpawnManager.SpawnedObjectsList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
|
SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, ownerClientId);
|
||||||
|
|
||||||
// If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization
|
// If scene management is enabled, then let NetworkSceneManager handle the initial scene and NetworkObject synchronization
|
||||||
if (!NetworkConfig.EnableSceneManagement)
|
if (!NetworkConfig.EnableSceneManagement)
|
||||||
@@ -1596,6 +1725,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else // Server just adds itself as an observer to all spawned NetworkObjects
|
else // Server just adds itself as an observer to all spawned NetworkObjects
|
||||||
{
|
{
|
||||||
|
LocalClient = client;
|
||||||
SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
|
SpawnManager.UpdateObservedNetworkObjects(ownerClientId);
|
||||||
InvokeOnClientConnectedCallback(ownerClientId);
|
InvokeOnClientConnectedCallback(ownerClientId);
|
||||||
}
|
}
|
||||||
@@ -1641,7 +1771,7 @@ namespace Unity.Netcode
|
|||||||
message.ObjectInfo.Header.HasParent = false;
|
message.ObjectInfo.Header.HasParent = false;
|
||||||
message.ObjectInfo.Header.IsPlayerObject = true;
|
message.ObjectInfo.Header.IsPlayerObject = true;
|
||||||
message.ObjectInfo.Header.OwnerClientId = clientId;
|
message.ObjectInfo.Header.OwnerClientId = clientId;
|
||||||
var size = SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, clientPair.Key);
|
var size = SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientPair.Key);
|
||||||
NetworkMetrics.TrackObjectSpawnSent(clientPair.Key, ConnectedClients[clientId].PlayerObject, size);
|
NetworkMetrics.TrackObjectSpawnSent(clientPair.Key, ConnectedClients[clientId].PlayerObject, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace Unity.Netcode
|
|||||||
var globalObjectIdString = UnityEditor.GlobalObjectId.GetGlobalObjectIdSlow(this).ToString();
|
var globalObjectIdString = UnityEditor.GlobalObjectId.GetGlobalObjectIdSlow(this).ToString();
|
||||||
GlobalObjectIdHash = XXHash.Hash32(globalObjectIdString);
|
GlobalObjectIdHash = XXHash.Hash32(globalObjectIdString);
|
||||||
}
|
}
|
||||||
#endif
|
#endif // UNITY_EDITOR
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the NetworkManager that owns this NetworkObject instance
|
/// Gets the NetworkManager that owns this NetworkObject instance
|
||||||
@@ -328,7 +328,7 @@ namespace Unity.Netcode
|
|||||||
NetworkObjectId = NetworkObjectId
|
NetworkObjectId = NetworkObjectId
|
||||||
};
|
};
|
||||||
// Send destroy call
|
// Send destroy call
|
||||||
var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, clientId);
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
|
||||||
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -714,7 +714,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, clientIds, idx);
|
NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientIds, idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1142,8 +1142,7 @@ namespace Unity.Netcode
|
|||||||
var globalObjectIdHash = NetworkManager.PrefabHandler.GetSourceGlobalObjectIdHash(GlobalObjectIdHash);
|
var globalObjectIdHash = NetworkManager.PrefabHandler.GetSourceGlobalObjectIdHash(GlobalObjectIdHash);
|
||||||
return globalObjectIdHash == 0 ? GlobalObjectIdHash : globalObjectIdHash;
|
return globalObjectIdHash == 0 ? GlobalObjectIdHash : globalObjectIdHash;
|
||||||
}
|
}
|
||||||
else
|
else if (NetworkManager.NetworkConfig.OverrideToNetworkPrefab.ContainsKey(GlobalObjectIdHash))
|
||||||
if (NetworkManager.NetworkConfig.OverrideToNetworkPrefab.ContainsKey(GlobalObjectIdHash))
|
|
||||||
{
|
{
|
||||||
return NetworkManager.NetworkConfig.OverrideToNetworkPrefab[GlobalObjectIdHash];
|
return NetworkManager.NetworkConfig.OverrideToNetworkPrefab[GlobalObjectIdHash];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,20 +62,63 @@ namespace Unity.Netcode
|
|||||||
internal int TimesWritten;
|
internal int TimesWritten;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class ClientData
|
||||||
|
{
|
||||||
|
internal struct SentSpawn // this struct also stores Despawns, not just Spawns
|
||||||
|
{
|
||||||
|
internal ulong SequenceNumber;
|
||||||
|
internal ulong ObjectId;
|
||||||
|
internal int Tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ushort SequenceNumber = 0; // the next sequence number to use for this client
|
||||||
|
internal ushort LastReceivedSequence = 0; // the last sequence number received by this client
|
||||||
|
internal ushort ReceivedSequenceMask = 0; // bitmask of the messages before the last one that we received.
|
||||||
|
|
||||||
|
internal int NextSpawnIndex = 0; // index of the last spawn sent. Used to cycle through spawns (LRU scheme)
|
||||||
|
internal int NextDespawnIndex = 0; // same as above, but for despawns.
|
||||||
|
|
||||||
|
// by objectId
|
||||||
|
// which spawns and despawns did this connection ack'ed ?
|
||||||
|
internal Dictionary<ulong, int> SpawnAck = new Dictionary<ulong, int>();
|
||||||
|
|
||||||
|
// list of spawn and despawns commands we sent, with sequence number
|
||||||
|
// need to manage acknowledgements
|
||||||
|
internal List<SentSpawn> SentSpawns = new List<SentSpawn>();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal delegate int MockSendMessage(ref SnapshotDataMessage message, NetworkDelivery delivery, ulong clientId);
|
||||||
|
internal delegate int MockSpawnObject(SnapshotSpawnCommand spawnCommand);
|
||||||
|
internal delegate int MockDespawnObject(SnapshotDespawnCommand despawnCommand);
|
||||||
|
|
||||||
|
|
||||||
// A table of NetworkVariables that constitutes a Snapshot.
|
// A table of NetworkVariables that constitutes a Snapshot.
|
||||||
// Stores serialized NetworkVariables
|
// Stores serialized NetworkVariables
|
||||||
// todo --M1--
|
// todo --M1--
|
||||||
// The Snapshot will change for M1b with memory management, instead of just FreeMemoryPosition, there will be data structure
|
// The Snapshot will change for M1b with memory management, instead of just FreeMemoryPosition, there will be data structure
|
||||||
// around available buffer, etc.
|
// around available buffer, etc.
|
||||||
internal class Snapshot
|
internal class SnapshotSystem : INetworkUpdateSystem, IDisposable
|
||||||
{
|
{
|
||||||
// todo --M1-- functionality to grow these will be needed in a later milestone
|
// todo --M1-- functionality to grow these will be needed in a later milestone
|
||||||
private const int k_MaxVariables = 2000;
|
private const int k_MaxVariables = 2000;
|
||||||
private int m_MaxSpawns = 100;
|
internal int SpawnsBufferCount { get; private set; } = 100;
|
||||||
private int m_MaxDespawns = 100;
|
internal int DespawnsBufferCount { get; private set; } = 100;
|
||||||
|
|
||||||
private const int k_BufferSize = 30000;
|
private const int k_BufferSize = 30000;
|
||||||
|
|
||||||
|
private NetworkManager m_NetworkManager = default;
|
||||||
|
|
||||||
|
// by clientId
|
||||||
|
private Dictionary<ulong, ClientData> m_ClientData = new Dictionary<ulong, ClientData>();
|
||||||
|
private Dictionary<ulong, ConnectionRtt> m_ConnectionRtts = new Dictionary<ulong, ConnectionRtt>();
|
||||||
|
|
||||||
|
private bool m_UseSnapshotDelta;
|
||||||
|
private bool m_UseSnapshotSpawn;
|
||||||
|
private int m_SnapshotMaxSpawnUsage;
|
||||||
|
private NetworkTickSystem m_NetworkTickSystem;
|
||||||
|
|
||||||
|
private int m_CurrentTick = NetworkTickSystem.NoTick;
|
||||||
|
|
||||||
internal byte[] MainBuffer = new byte[k_BufferSize]; // buffer holding a snapshot in memory
|
internal byte[] MainBuffer = new byte[k_BufferSize]; // buffer holding a snapshot in memory
|
||||||
internal byte[] RecvBuffer = new byte[k_BufferSize]; // buffer holding the received snapshot message
|
internal byte[] RecvBuffer = new byte[k_BufferSize]; // buffer holding the received snapshot message
|
||||||
|
|
||||||
@@ -90,23 +133,17 @@ namespace Unity.Netcode
|
|||||||
internal SnapshotDespawnCommand[] Despawns;
|
internal SnapshotDespawnCommand[] Despawns;
|
||||||
internal int NumDespawns = 0;
|
internal int NumDespawns = 0;
|
||||||
|
|
||||||
internal NetworkManager NetworkManager;
|
|
||||||
|
|
||||||
// indexed by ObjectId
|
// indexed by ObjectId
|
||||||
internal Dictionary<ulong, int> TickAppliedSpawn = new Dictionary<ulong, int>();
|
internal Dictionary<ulong, int> TickAppliedSpawn = new Dictionary<ulong, int>();
|
||||||
internal Dictionary<ulong, int> TickAppliedDespawn = new Dictionary<ulong, int>();
|
internal Dictionary<ulong, int> TickAppliedDespawn = new Dictionary<ulong, int>();
|
||||||
|
|
||||||
/// <summary>
|
internal bool IsServer { get; set; }
|
||||||
/// Constructor
|
internal bool IsConnectedClient { get; set; }
|
||||||
/// Allocated a MemoryStream to be reused for this Snapshot
|
internal ulong ServerClientId { get; set; }
|
||||||
/// </summary>
|
internal List<ulong> ConnectedClientsId { get; } = new List<ulong>();
|
||||||
internal Snapshot()
|
internal MockSendMessage MockSendMessage { get; set; }
|
||||||
{
|
internal MockSpawnObject MockSpawnObject { get; set; }
|
||||||
// we ask for twice as many slots because there could end up being one free spot between each pair of slot used
|
internal MockDespawnObject MockDespawnObject { get; set; }
|
||||||
Allocator = new IndexAllocator(k_BufferSize, k_MaxVariables * 2);
|
|
||||||
Spawns = new SnapshotSpawnCommand[m_MaxSpawns];
|
|
||||||
Despawns = new SnapshotDespawnCommand[m_MaxDespawns];
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Clear()
|
internal void Clear()
|
||||||
{
|
{
|
||||||
@@ -156,15 +193,15 @@ namespace Unity.Netcode
|
|||||||
List<ulong> clientList;
|
List<ulong> clientList;
|
||||||
clientList = new List<ulong>();
|
clientList = new List<ulong>();
|
||||||
|
|
||||||
if (!NetworkManager.IsServer)
|
if (!IsServer)
|
||||||
{
|
{
|
||||||
clientList.Add(NetworkManager.ServerClientId);
|
clientList.Add(m_NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var clientId in NetworkManager.ConnectedClientsIds)
|
foreach (var clientId in ConnectedClientsId)
|
||||||
{
|
{
|
||||||
if (clientId != NetworkManager.ServerClientId)
|
if (clientId != m_NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
clientList.Add(clientId);
|
clientList.Add(clientId);
|
||||||
}
|
}
|
||||||
@@ -176,14 +213,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void AddSpawn(SnapshotSpawnCommand command)
|
internal void AddSpawn(SnapshotSpawnCommand command)
|
||||||
{
|
{
|
||||||
if (NumSpawns >= m_MaxSpawns)
|
if (NumSpawns >= SpawnsBufferCount)
|
||||||
{
|
{
|
||||||
Array.Resize(ref Spawns, 2 * m_MaxSpawns);
|
Array.Resize(ref Spawns, 2 * SpawnsBufferCount);
|
||||||
m_MaxSpawns = m_MaxSpawns * 2;
|
SpawnsBufferCount = SpawnsBufferCount * 2;
|
||||||
// Debug.Log($"[JEFF] spawn size is now {m_MaxSpawns}");
|
// Debug.Log($"[JEFF] spawn size is now {m_MaxSpawns}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NumSpawns < m_MaxSpawns)
|
if (NumSpawns < SpawnsBufferCount)
|
||||||
{
|
{
|
||||||
if (command.TargetClientIds == default)
|
if (command.TargetClientIds == default)
|
||||||
{
|
{
|
||||||
@@ -208,14 +245,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void AddDespawn(SnapshotDespawnCommand command)
|
internal void AddDespawn(SnapshotDespawnCommand command)
|
||||||
{
|
{
|
||||||
if (NumDespawns >= m_MaxDespawns)
|
if (NumDespawns >= DespawnsBufferCount)
|
||||||
{
|
{
|
||||||
Array.Resize(ref Despawns, 2 * m_MaxDespawns);
|
Array.Resize(ref Despawns, 2 * DespawnsBufferCount);
|
||||||
m_MaxDespawns = m_MaxDespawns * 2;
|
DespawnsBufferCount = DespawnsBufferCount * 2;
|
||||||
// Debug.Log($"[JEFF] despawn size is now {m_MaxDespawns}");
|
// Debug.Log($"[JEFF] despawn size is now {m_MaxDespawns}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NumDespawns < m_MaxDespawns)
|
if (NumDespawns < DespawnsBufferCount)
|
||||||
{
|
{
|
||||||
if (command.TargetClientIds == default)
|
if (command.TargetClientIds == default)
|
||||||
{
|
{
|
||||||
@@ -229,6 +266,17 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ReduceBufferUsage()
|
||||||
|
{
|
||||||
|
var count = Math.Max(1, NumDespawns);
|
||||||
|
Array.Resize(ref Despawns, count);
|
||||||
|
DespawnsBufferCount = count;
|
||||||
|
|
||||||
|
count = Math.Max(1, NumSpawns);
|
||||||
|
Array.Resize(ref Spawns, count);
|
||||||
|
SpawnsBufferCount = count;
|
||||||
|
}
|
||||||
|
|
||||||
internal ClientData.SentSpawn GetSpawnData(in ClientData clientData, in SnapshotSpawnCommand spawn, out SnapshotDataMessage.SpawnData data)
|
internal ClientData.SentSpawn GetSpawnData(in ClientData clientData, in SnapshotSpawnCommand spawn, out SnapshotDataMessage.SpawnData data)
|
||||||
{
|
{
|
||||||
// remember which spawn we sent this connection with which sequence number
|
// remember which spawn we sent this connection with which sequence number
|
||||||
@@ -417,7 +465,54 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ReadSpawns(in SnapshotDataMessage message)
|
internal void SpawnObject(SnapshotSpawnCommand spawnCommand, ulong srcClientId)
|
||||||
|
{
|
||||||
|
if (m_NetworkManager)
|
||||||
|
{
|
||||||
|
NetworkObject networkObject;
|
||||||
|
if (spawnCommand.ParentNetworkId == spawnCommand.NetworkObjectId)
|
||||||
|
{
|
||||||
|
networkObject = m_NetworkManager.SpawnManager.CreateLocalNetworkObject(false,
|
||||||
|
spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, null, spawnCommand.ObjectPosition,
|
||||||
|
spawnCommand.ObjectRotation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
networkObject = m_NetworkManager.SpawnManager.CreateLocalNetworkObject(false,
|
||||||
|
spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, spawnCommand.ParentNetworkId, spawnCommand.ObjectPosition,
|
||||||
|
spawnCommand.ObjectRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId,
|
||||||
|
true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, false);
|
||||||
|
//todo: discuss with tools how to report shared bytes
|
||||||
|
m_NetworkManager.NetworkMetrics.TrackObjectSpawnReceived(srcClientId, networkObject, 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MockSpawnObject(spawnCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DespawnObject(SnapshotDespawnCommand despawnCommand, ulong srcClientId)
|
||||||
|
{
|
||||||
|
if (m_NetworkManager)
|
||||||
|
{
|
||||||
|
m_NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(despawnCommand.NetworkObjectId,
|
||||||
|
out NetworkObject networkObject);
|
||||||
|
|
||||||
|
m_NetworkManager.SpawnManager.OnDespawnObject(networkObject, true);
|
||||||
|
//todo: discuss with tools how to report shared bytes
|
||||||
|
m_NetworkManager.NetworkMetrics.TrackObjectDestroyReceived(srcClientId, networkObject, 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MockDespawnObject(despawnCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal void ReadSpawns(in SnapshotDataMessage message, ulong srcClientId)
|
||||||
{
|
{
|
||||||
SnapshotSpawnCommand spawnCommand;
|
SnapshotSpawnCommand spawnCommand;
|
||||||
SnapshotDespawnCommand despawnCommand;
|
SnapshotDespawnCommand despawnCommand;
|
||||||
@@ -436,16 +531,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
// Debug.Log($"[Spawn] {spawnCommand.NetworkObjectId} {spawnCommand.TickWritten}");
|
// Debug.Log($"[Spawn] {spawnCommand.NetworkObjectId} {spawnCommand.TickWritten}");
|
||||||
|
|
||||||
if (spawnCommand.ParentNetworkId == spawnCommand.NetworkObjectId)
|
SpawnObject(spawnCommand, srcClientId);
|
||||||
{
|
|
||||||
var networkObject = NetworkManager.SpawnManager.CreateLocalNetworkObject(false, spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, null, spawnCommand.ObjectPosition, spawnCommand.ObjectRotation);
|
|
||||||
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId, true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var networkObject = NetworkManager.SpawnManager.CreateLocalNetworkObject(false, spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, spawnCommand.ParentNetworkId, spawnCommand.ObjectPosition, spawnCommand.ObjectRotation);
|
|
||||||
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId, true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (var i = 0; i < message.Despawns.Length; i++)
|
for (var i = 0; i < message.Despawns.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -461,10 +547,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
// Debug.Log($"[DeSpawn] {despawnCommand.NetworkObjectId} {despawnCommand.TickWritten}");
|
// Debug.Log($"[DeSpawn] {despawnCommand.NetworkObjectId} {despawnCommand.TickWritten}");
|
||||||
|
|
||||||
NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(despawnCommand.NetworkObjectId,
|
DespawnObject(despawnCommand, srcClientId);
|
||||||
out NetworkObject networkObject);
|
|
||||||
|
|
||||||
NetworkManager.SpawnManager.OnDespawnObject(networkObject, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +652,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="key">The key to search for</param>
|
/// <param name="key">The key to search for</param>
|
||||||
private NetworkVariableBase FindNetworkVar(VariableKey key)
|
private NetworkVariableBase FindNetworkVar(VariableKey key)
|
||||||
{
|
{
|
||||||
var spawnedObjects = NetworkManager.SpawnManager.SpawnedObjects;
|
var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects;
|
||||||
|
|
||||||
if (spawnedObjects.ContainsKey(key.NetworkObjectId))
|
if (spawnedObjects.ContainsKey(key.NetworkObjectId))
|
||||||
{
|
{
|
||||||
@@ -580,61 +663,49 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal class ClientData
|
|
||||||
{
|
|
||||||
internal struct SentSpawn // this struct also stores Despawns, not just Spawns
|
|
||||||
{
|
|
||||||
internal ulong SequenceNumber;
|
|
||||||
internal ulong ObjectId;
|
|
||||||
internal int Tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ushort SequenceNumber = 0; // the next sequence number to use for this client
|
|
||||||
internal ushort LastReceivedSequence = 0; // the last sequence number received by this client
|
|
||||||
internal ushort ReceivedSequenceMask = 0; // bitmask of the messages before the last one that we received.
|
|
||||||
|
|
||||||
internal int NextSpawnIndex = 0; // index of the last spawn sent. Used to cycle through spawns (LRU scheme)
|
|
||||||
internal int NextDespawnIndex = 0; // same as above, but for despawns.
|
|
||||||
|
|
||||||
// by objectId
|
|
||||||
// which spawns and despawns did this connection ack'ed ?
|
|
||||||
internal Dictionary<ulong, int> SpawnAck = new Dictionary<ulong, int>();
|
|
||||||
|
|
||||||
// list of spawn and despawns commands we sent, with sequence number
|
|
||||||
// need to manage acknowledgements
|
|
||||||
internal List<SentSpawn> SentSpawns = new List<SentSpawn>();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class SnapshotSystem : INetworkUpdateSystem, IDisposable
|
|
||||||
{
|
|
||||||
// temporary, debugging sentinels
|
|
||||||
internal const ushort SentinelBefore = 0x4246;
|
|
||||||
internal const ushort SentinelAfter = 0x89CE;
|
|
||||||
|
|
||||||
private NetworkManager m_NetworkManager = default;
|
|
||||||
private Snapshot m_Snapshot = default;
|
|
||||||
|
|
||||||
// by clientId
|
|
||||||
private Dictionary<ulong, ClientData> m_ClientData = new Dictionary<ulong, ClientData>();
|
|
||||||
private Dictionary<ulong, ConnectionRtt> m_ConnectionRtts = new Dictionary<ulong, ConnectionRtt>();
|
|
||||||
|
|
||||||
private int m_CurrentTick = NetworkTickSystem.NoTick;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// Registers the snapshot system for early updates, keeps reference to the NetworkManager
|
/// Registers the snapshot system for early updates, keeps reference to the NetworkManager
|
||||||
internal SnapshotSystem(NetworkManager networkManager)
|
internal SnapshotSystem(NetworkManager networkManager, NetworkConfig config, NetworkTickSystem networkTickSystem)
|
||||||
{
|
{
|
||||||
m_Snapshot = new Snapshot();
|
|
||||||
|
|
||||||
m_NetworkManager = networkManager;
|
m_NetworkManager = networkManager;
|
||||||
m_Snapshot.NetworkManager = networkManager;
|
m_NetworkTickSystem = networkTickSystem;
|
||||||
|
|
||||||
|
m_UseSnapshotDelta = config.UseSnapshotDelta;
|
||||||
|
m_UseSnapshotSpawn = config.UseSnapshotSpawn;
|
||||||
|
m_SnapshotMaxSpawnUsage = config.SnapshotMaxSpawnUsage;
|
||||||
|
|
||||||
|
UpdateClientServerData();
|
||||||
|
|
||||||
this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate);
|
this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate);
|
||||||
|
|
||||||
|
// we ask for twice as many slots because there could end up being one free spot between each pair of slot used
|
||||||
|
Allocator = new IndexAllocator(k_BufferSize, k_MaxVariables * 2);
|
||||||
|
Spawns = new SnapshotSpawnCommand[SpawnsBufferCount];
|
||||||
|
Despawns = new SnapshotDespawnCommand[DespawnsBufferCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
// since we don't want to access the NetworkManager directly, we refresh those values on Update
|
||||||
|
internal void UpdateClientServerData()
|
||||||
|
{
|
||||||
|
if (m_NetworkManager)
|
||||||
|
{
|
||||||
|
IsServer = m_NetworkManager.IsServer;
|
||||||
|
IsConnectedClient = m_NetworkManager.IsConnectedClient;
|
||||||
|
ServerClientId = m_NetworkManager.ServerClientId;
|
||||||
|
|
||||||
|
// todo: This is extremely inefficient. What is the efficient and idiomatic way ?
|
||||||
|
ConnectedClientsId.Clear();
|
||||||
|
if (IsServer)
|
||||||
|
{
|
||||||
|
foreach (var id in m_NetworkManager.ConnectedClientsIds)
|
||||||
|
{
|
||||||
|
ConnectedClientsId.Add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ConnectionRtt GetConnectionRtt(ulong clientId)
|
internal ConnectionRtt GetConnectionRtt(ulong clientId)
|
||||||
@@ -658,34 +729,36 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public void NetworkUpdate(NetworkUpdateStage updateStage)
|
public void NetworkUpdate(NetworkUpdateStage updateStage)
|
||||||
{
|
{
|
||||||
if (!m_NetworkManager.NetworkConfig.UseSnapshotDelta && !m_NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
if (!m_UseSnapshotDelta && !m_UseSnapshotSpawn)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateStage == NetworkUpdateStage.EarlyUpdate)
|
if (updateStage == NetworkUpdateStage.EarlyUpdate)
|
||||||
{
|
{
|
||||||
var tick = m_NetworkManager.NetworkTickSystem.LocalTime.Tick;
|
UpdateClientServerData();
|
||||||
|
|
||||||
|
var tick = m_NetworkTickSystem.LocalTime.Tick;
|
||||||
|
|
||||||
if (tick != m_CurrentTick)
|
if (tick != m_CurrentTick)
|
||||||
{
|
{
|
||||||
m_CurrentTick = tick;
|
m_CurrentTick = tick;
|
||||||
if (m_NetworkManager.IsServer)
|
if (IsServer)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < m_NetworkManager.ConnectedClientsList.Count; i++)
|
for (int i = 0; i < ConnectedClientsId.Count; i++)
|
||||||
{
|
{
|
||||||
var clientId = m_NetworkManager.ConnectedClientsList[i].ClientId;
|
var clientId = ConnectedClientsId[i];
|
||||||
|
|
||||||
// don't send to ourselves
|
// don't send to ourselves
|
||||||
if (clientId != m_NetworkManager.ServerClientId)
|
if (clientId != ServerClientId)
|
||||||
{
|
{
|
||||||
SendSnapshot(clientId);
|
SendSnapshot(clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_NetworkManager.IsConnectedClient)
|
else if (IsConnectedClient)
|
||||||
{
|
{
|
||||||
SendSnapshot(m_NetworkManager.ServerClientId);
|
SendSnapshot(ServerClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,12 +794,12 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
CurrentTick = m_CurrentTick,
|
CurrentTick = m_CurrentTick,
|
||||||
Sequence = sequence,
|
Sequence = sequence,
|
||||||
Range = (ushort)m_Snapshot.Allocator.Range,
|
Range = (ushort)Allocator.Range,
|
||||||
|
|
||||||
// todo --M1--
|
// todo --M1--
|
||||||
// this sends the whole buffer
|
// this sends the whole buffer
|
||||||
// we'll need to build a per-client list
|
// we'll need to build a per-client list
|
||||||
SendMainBuffer = m_Snapshot.MainBuffer,
|
SendMainBuffer = MainBuffer,
|
||||||
|
|
||||||
Ack = new SnapshotDataMessage.AckData
|
Ack = new SnapshotDataMessage.AckData
|
||||||
{
|
{
|
||||||
@@ -740,7 +813,23 @@ namespace Unity.Netcode
|
|||||||
WriteIndex(ref message);
|
WriteIndex(ref message);
|
||||||
WriteSpawns(ref message, clientId);
|
WriteSpawns(ref message, clientId);
|
||||||
|
|
||||||
m_NetworkManager.SendMessage(message, NetworkDelivery.Unreliable, clientId);
|
try
|
||||||
|
{
|
||||||
|
if (m_NetworkManager)
|
||||||
|
{
|
||||||
|
m_NetworkManager.SendMessage(ref message, NetworkDelivery.Unreliable, clientId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MockSendMessage(ref message, NetworkDelivery.Unreliable, clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
message.Entries.Dispose();
|
||||||
|
message.Spawns.Dispose();
|
||||||
|
message.Despawns.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
m_ClientData[clientId].LastReceivedSequence = 0;
|
m_ClientData[clientId].LastReceivedSequence = 0;
|
||||||
|
|
||||||
@@ -791,51 +880,51 @@ namespace Unity.Netcode
|
|||||||
ClientData clientData = m_ClientData[clientId];
|
ClientData clientData = m_ClientData[clientId];
|
||||||
|
|
||||||
// this is needed because spawns being removed may have reduce the size below LRU position
|
// this is needed because spawns being removed may have reduce the size below LRU position
|
||||||
if (m_Snapshot.NumSpawns > 0)
|
if (NumSpawns > 0)
|
||||||
{
|
{
|
||||||
clientData.NextSpawnIndex %= m_Snapshot.NumSpawns;
|
clientData.NextSpawnIndex %= NumSpawns;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
clientData.NextSpawnIndex = 0;
|
clientData.NextSpawnIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Snapshot.NumDespawns > 0)
|
if (NumDespawns > 0)
|
||||||
{
|
{
|
||||||
clientData.NextDespawnIndex %= m_Snapshot.NumDespawns;
|
clientData.NextDespawnIndex %= NumDespawns;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
clientData.NextDespawnIndex = 0;
|
clientData.NextDespawnIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
message.Spawns = new NativeList<SnapshotDataMessage.SpawnData>(m_Snapshot.NumSpawns, Allocator.TempJob);
|
message.Spawns = new NativeList<SnapshotDataMessage.SpawnData>(NumSpawns, Collections.Allocator.TempJob);
|
||||||
message.Despawns = new NativeList<SnapshotDataMessage.DespawnData>(m_Snapshot.NumDespawns, Allocator.TempJob);
|
message.Despawns = new NativeList<SnapshotDataMessage.DespawnData>(NumDespawns, Collections.Allocator.TempJob);
|
||||||
var spawnUsage = 0;
|
var spawnUsage = 0;
|
||||||
|
|
||||||
for (var j = 0; j < m_Snapshot.NumSpawns && !overSize; j++)
|
for (var j = 0; j < NumSpawns && !overSize; j++)
|
||||||
{
|
{
|
||||||
var index = clientData.NextSpawnIndex;
|
var index = clientData.NextSpawnIndex;
|
||||||
|
|
||||||
// todo: re-enable ShouldWriteSpawn, once we have a mechanism to not let despawn pass in front of spawns
|
// todo: re-enable ShouldWriteSpawn, once we have a mechanism to not let despawn pass in front of spawns
|
||||||
if (m_Snapshot.Spawns[index].TargetClientIds.Contains(clientId) /*&& ShouldWriteSpawn(m_Snapshot.Spawns[index])*/)
|
if (Spawns[index].TargetClientIds.Contains(clientId) /*&& ShouldWriteSpawn(Spawns[index])*/)
|
||||||
{
|
{
|
||||||
spawnUsage += FastBufferWriter.GetWriteSize<SnapshotDataMessage.SpawnData>();
|
spawnUsage += FastBufferWriter.GetWriteSize<SnapshotDataMessage.SpawnData>();
|
||||||
|
|
||||||
// limit spawn sizes, compare current pos to very first position we wrote to
|
// limit spawn sizes, compare current pos to very first position we wrote to
|
||||||
if (spawnUsage > m_NetworkManager.NetworkConfig.SnapshotMaxSpawnUsage)
|
if (spawnUsage > m_SnapshotMaxSpawnUsage)
|
||||||
{
|
{
|
||||||
overSize = true;
|
overSize = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var sentSpawn = m_Snapshot.GetSpawnData(clientData, in m_Snapshot.Spawns[index], out var spawn);
|
var sentSpawn = GetSpawnData(clientData, in Spawns[index], out var spawn);
|
||||||
message.Spawns.Add(spawn);
|
message.Spawns.Add(spawn);
|
||||||
|
|
||||||
m_Snapshot.Spawns[index].TimesWritten++;
|
Spawns[index].TimesWritten++;
|
||||||
clientData.SentSpawns.Add(sentSpawn);
|
clientData.SentSpawns.Add(sentSpawn);
|
||||||
spawnWritten++;
|
spawnWritten++;
|
||||||
}
|
}
|
||||||
clientData.NextSpawnIndex = (clientData.NextSpawnIndex + 1) % m_Snapshot.NumSpawns;
|
clientData.NextSpawnIndex = (clientData.NextSpawnIndex + 1) % NumSpawns;
|
||||||
}
|
}
|
||||||
|
|
||||||
// even though we might have a spawn we could not fit, it's possible despawns will fit (they're smaller)
|
// even though we might have a spawn we could not fit, it's possible despawns will fit (they're smaller)
|
||||||
@@ -846,28 +935,28 @@ namespace Unity.Netcode
|
|||||||
// As-is it is overly restrictive but allows us to go forward without the spawn/despawn dependency check
|
// As-is it is overly restrictive but allows us to go forward without the spawn/despawn dependency check
|
||||||
// overSize = false;
|
// overSize = false;
|
||||||
|
|
||||||
for (var j = 0; j < m_Snapshot.NumDespawns && !overSize; j++)
|
for (var j = 0; j < NumDespawns && !overSize; j++)
|
||||||
{
|
{
|
||||||
var index = clientData.NextDespawnIndex;
|
var index = clientData.NextDespawnIndex;
|
||||||
|
|
||||||
// todo: re-enable ShouldWriteSpawn, once we have a mechanism to not let despawn pass in front of spawns
|
// todo: re-enable ShouldWriteSpawn, once we have a mechanism to not let despawn pass in front of spawns
|
||||||
if (m_Snapshot.Despawns[index].TargetClientIds.Contains(clientId) /*&& ShouldWriteDespawn(m_Snapshot.Despawns[index])*/)
|
if (Despawns[index].TargetClientIds.Contains(clientId) /*&& ShouldWriteDespawn(Despawns[index])*/)
|
||||||
{
|
{
|
||||||
spawnUsage += FastBufferWriter.GetWriteSize<SnapshotDataMessage.DespawnData>();
|
spawnUsage += FastBufferWriter.GetWriteSize<SnapshotDataMessage.DespawnData>();
|
||||||
|
|
||||||
// limit spawn sizes, compare current pos to very first position we wrote to
|
// limit spawn sizes, compare current pos to very first position we wrote to
|
||||||
if (spawnUsage > m_NetworkManager.NetworkConfig.SnapshotMaxSpawnUsage)
|
if (spawnUsage > m_SnapshotMaxSpawnUsage)
|
||||||
{
|
{
|
||||||
overSize = true;
|
overSize = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var sentDespawn = m_Snapshot.GetDespawnData(clientData, in m_Snapshot.Despawns[index], out var despawn);
|
var sentDespawn = GetDespawnData(clientData, in Despawns[index], out var despawn);
|
||||||
message.Despawns.Add(despawn);
|
message.Despawns.Add(despawn);
|
||||||
m_Snapshot.Despawns[index].TimesWritten++;
|
Despawns[index].TimesWritten++;
|
||||||
clientData.SentSpawns.Add(sentDespawn);
|
clientData.SentSpawns.Add(sentDespawn);
|
||||||
despawnWritten++;
|
despawnWritten++;
|
||||||
}
|
}
|
||||||
clientData.NextDespawnIndex = (clientData.NextDespawnIndex + 1) % m_Snapshot.NumDespawns;
|
clientData.NextDespawnIndex = (clientData.NextDespawnIndex + 1) % NumDespawns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,10 +966,10 @@ namespace Unity.Netcode
|
|||||||
/// <param name="message">The message to write the index to</param>
|
/// <param name="message">The message to write the index to</param>
|
||||||
private void WriteIndex(ref SnapshotDataMessage message)
|
private void WriteIndex(ref SnapshotDataMessage message)
|
||||||
{
|
{
|
||||||
message.Entries = new NativeList<SnapshotDataMessage.EntryData>(m_Snapshot.LastEntry, Allocator.TempJob);
|
message.Entries = new NativeList<SnapshotDataMessage.EntryData>(LastEntry, Collections.Allocator.TempJob);
|
||||||
for (var i = 0; i < m_Snapshot.LastEntry; i++)
|
for (var i = 0; i < LastEntry; i++)
|
||||||
{
|
{
|
||||||
var entryMeta = m_Snapshot.Entries[i];
|
var entryMeta = Entries[i];
|
||||||
var entry = entryMeta.Key;
|
var entry = entryMeta.Key;
|
||||||
message.Entries.Add(new SnapshotDataMessage.EntryData
|
message.Entries.Add(new SnapshotDataMessage.EntryData
|
||||||
{
|
{
|
||||||
@@ -897,7 +986,7 @@ namespace Unity.Netcode
|
|||||||
internal void Spawn(SnapshotSpawnCommand command)
|
internal void Spawn(SnapshotSpawnCommand command)
|
||||||
{
|
{
|
||||||
command.TickWritten = m_CurrentTick;
|
command.TickWritten = m_CurrentTick;
|
||||||
m_Snapshot.AddSpawn(command);
|
AddSpawn(command);
|
||||||
|
|
||||||
// Debug.Log($"[Spawn] {command.NetworkObjectId} {command.TickWritten}");
|
// Debug.Log($"[Spawn] {command.NetworkObjectId} {command.TickWritten}");
|
||||||
}
|
}
|
||||||
@@ -905,7 +994,7 @@ namespace Unity.Netcode
|
|||||||
internal void Despawn(SnapshotDespawnCommand command)
|
internal void Despawn(SnapshotDespawnCommand command)
|
||||||
{
|
{
|
||||||
command.TickWritten = m_CurrentTick;
|
command.TickWritten = m_CurrentTick;
|
||||||
m_Snapshot.AddDespawn(command);
|
AddDespawn(command);
|
||||||
|
|
||||||
// Debug.Log($"[DeSpawn] {command.NetworkObjectId} {command.TickWritten}");
|
// Debug.Log($"[DeSpawn] {command.NetworkObjectId} {command.TickWritten}");
|
||||||
}
|
}
|
||||||
@@ -922,35 +1011,35 @@ namespace Unity.Netcode
|
|||||||
k.NetworkObjectId = networkObjectId;
|
k.NetworkObjectId = networkObjectId;
|
||||||
k.BehaviourIndex = (ushort)behaviourIndex;
|
k.BehaviourIndex = (ushort)behaviourIndex;
|
||||||
k.VariableIndex = (ushort)variableIndex;
|
k.VariableIndex = (ushort)variableIndex;
|
||||||
k.TickWritten = m_NetworkManager.NetworkTickSystem.LocalTime.Tick;
|
k.TickWritten = m_NetworkTickSystem.LocalTime.Tick;
|
||||||
|
|
||||||
int pos = m_Snapshot.Find(k);
|
int pos = Find(k);
|
||||||
if (pos == Entry.NotFound)
|
if (pos == Entry.NotFound)
|
||||||
{
|
{
|
||||||
pos = m_Snapshot.AddEntry(k);
|
pos = AddEntry(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Snapshot.Entries[pos].Key.TickWritten = k.TickWritten;
|
Entries[pos].Key.TickWritten = k.TickWritten;
|
||||||
|
|
||||||
WriteVariableToSnapshot(m_Snapshot, networkVariable, pos);
|
WriteVariable(networkVariable, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void WriteVariableToSnapshot(Snapshot snapshot, NetworkVariableBase networkVariable, int index)
|
private unsafe void WriteVariable(NetworkVariableBase networkVariable, int index)
|
||||||
{
|
{
|
||||||
// write var into buffer, possibly adjusting entry's position and Length
|
// write var into buffer, possibly adjusting entry's position and Length
|
||||||
var varBuffer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp);
|
var varBuffer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Collections.Allocator.Temp);
|
||||||
using (varBuffer)
|
using (varBuffer)
|
||||||
{
|
{
|
||||||
networkVariable.WriteDelta(varBuffer);
|
networkVariable.WriteDelta(varBuffer);
|
||||||
if (varBuffer.Length > snapshot.Entries[index].Length)
|
if (varBuffer.Length > Entries[index].Length)
|
||||||
{
|
{
|
||||||
// allocate this Entry's buffer
|
// allocate this Entry's buffer
|
||||||
snapshot.AllocateEntry(ref snapshot.Entries[index], index, (int)varBuffer.Length);
|
AllocateEntry(ref Entries[index], index, (int)varBuffer.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (byte* buffer = snapshot.MainBuffer)
|
fixed (byte* buffer = MainBuffer)
|
||||||
{
|
{
|
||||||
UnsafeUtility.MemCpy(buffer + snapshot.Entries[index].Position, varBuffer.GetUnsafePtr(), varBuffer.Length);
|
UnsafeUtility.MemCpy(buffer + Entries[index].Position, varBuffer.GetUnsafePtr(), varBuffer.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1009,10 +1098,10 @@ namespace Unity.Netcode
|
|||||||
// without this, we incur extra retransmit, not a catastrophic failure
|
// without this, we incur extra retransmit, not a catastrophic failure
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Snapshot.ReadBuffer(message);
|
ReadBuffer(message);
|
||||||
m_Snapshot.ReadIndex(message);
|
ReadIndex(message);
|
||||||
m_Snapshot.ReadAcks(clientId, m_ClientData[clientId], message, GetConnectionRtt(clientId));
|
ReadAcks(clientId, m_ClientData[clientId], message, GetConnectionRtt(clientId));
|
||||||
m_Snapshot.ReadSpawns(message);
|
ReadSpawns(message, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo --M1--
|
// todo --M1--
|
||||||
@@ -1024,14 +1113,14 @@ namespace Unity.Netcode
|
|||||||
table += $"We're clientId {m_NetworkManager.LocalClientId}\n";
|
table += $"We're clientId {m_NetworkManager.LocalClientId}\n";
|
||||||
|
|
||||||
table += "=== Variables ===\n";
|
table += "=== Variables ===\n";
|
||||||
for (int i = 0; i < m_Snapshot.LastEntry; i++)
|
for (int i = 0; i < LastEntry; i++)
|
||||||
{
|
{
|
||||||
table += string.Format("NetworkVariable {0}:{1}:{2} written {5}, range [{3}, {4}] ", m_Snapshot.Entries[i].Key.NetworkObjectId, m_Snapshot.Entries[i].Key.BehaviourIndex,
|
table += string.Format("NetworkVariable {0}:{1}:{2} written {5}, range [{3}, {4}] ", Entries[i].Key.NetworkObjectId, Entries[i].Key.BehaviourIndex,
|
||||||
m_Snapshot.Entries[i].Key.VariableIndex, m_Snapshot.Entries[i].Position, m_Snapshot.Entries[i].Position + m_Snapshot.Entries[i].Length, m_Snapshot.Entries[i].Key.TickWritten);
|
Entries[i].Key.VariableIndex, Entries[i].Position, Entries[i].Position + Entries[i].Length, Entries[i].Key.TickWritten);
|
||||||
|
|
||||||
for (int j = 0; j < m_Snapshot.Entries[i].Length && j < 4; j++)
|
for (int j = 0; j < Entries[i].Length && j < 4; j++)
|
||||||
{
|
{
|
||||||
table += m_Snapshot.MainBuffer[m_Snapshot.Entries[i].Position + j].ToString("X2") + " ";
|
table += MainBuffer[Entries[i].Position + j].ToString("X2") + " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
table += "\n";
|
table += "\n";
|
||||||
@@ -1039,14 +1128,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
table += "=== Spawns ===\n";
|
table += "=== Spawns ===\n";
|
||||||
|
|
||||||
for (int i = 0; i < m_Snapshot.NumSpawns; i++)
|
for (int i = 0; i < NumSpawns; i++)
|
||||||
{
|
{
|
||||||
string targets = "";
|
string targets = "";
|
||||||
foreach (var target in m_Snapshot.Spawns[i].TargetClientIds)
|
foreach (var target in Spawns[i].TargetClientIds)
|
||||||
{
|
{
|
||||||
targets += target.ToString() + ", ";
|
targets += target.ToString() + ", ";
|
||||||
}
|
}
|
||||||
table += $"Spawn Object Id {m_Snapshot.Spawns[i].NetworkObjectId}, Tick {m_Snapshot.Spawns[i].TickWritten}, Target {targets}\n";
|
table += $"Spawn Object Id {Spawns[i].NetworkObjectId}, Tick {Spawns[i].TickWritten}, Target {targets}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
table += $"=== RTTs ===\n";
|
table += $"=== RTTs ===\n";
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ namespace Unity.Netcode
|
|||||||
LogType = logType,
|
LogType = logType,
|
||||||
Message = message
|
Message = message
|
||||||
};
|
};
|
||||||
var size = NetworkManager.Singleton.SendMessage(networkMessage, NetworkDelivery.ReliableFragmentedSequenced,
|
var size = NetworkManager.Singleton.SendMessage(ref networkMessage, NetworkDelivery.ReliableFragmentedSequenced, NetworkManager.Singleton.ServerClientId);
|
||||||
NetworkManager.Singleton.ServerClientId);
|
|
||||||
|
|
||||||
NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.Singleton.ServerClientId, (uint)logType, size);
|
NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.Singleton.ServerClientId, (uint)logType, size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event UnnamedMessageDelegate OnUnnamedMessage;
|
public event UnnamedMessageDelegate OnUnnamedMessage;
|
||||||
|
|
||||||
internal void InvokeUnnamedMessage(ulong clientId, FastBufferReader reader)
|
internal void InvokeUnnamedMessage(ulong clientId, FastBufferReader reader, int serializedHeaderSize)
|
||||||
{
|
{
|
||||||
if (OnUnnamedMessage != null)
|
if (OnUnnamedMessage != null)
|
||||||
{
|
{
|
||||||
@@ -40,7 +40,7 @@ namespace Unity.Netcode
|
|||||||
((UnnamedMessageDelegate)handler).Invoke(clientId, reader);
|
((UnnamedMessageDelegate)handler).Invoke(clientId, reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_NetworkManager.NetworkMetrics.TrackUnnamedMessageReceived(clientId, reader.Length + FastBufferWriter.GetWriteSize<MessageHeader>());
|
m_NetworkManager.NetworkMetrics.TrackUnnamedMessageReceived(clientId, reader.Length + serializedHeaderSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -73,9 +73,9 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
var message = new UnnamedMessage
|
var message = new UnnamedMessage
|
||||||
{
|
{
|
||||||
Data = messageBuffer
|
SendData = messageBuffer
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, networkDelivery, clientIds);
|
var size = m_NetworkManager.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)
|
||||||
@@ -94,9 +94,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
var message = new UnnamedMessage
|
var message = new UnnamedMessage
|
||||||
{
|
{
|
||||||
Data = messageBuffer
|
SendData = messageBuffer
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, networkDelivery, clientId);
|
var size = m_NetworkManager.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)
|
||||||
{
|
{
|
||||||
@@ -115,9 +115,9 @@ namespace Unity.Netcode
|
|||||||
private Dictionary<ulong, string> m_MessageHandlerNameLookup32 = new Dictionary<ulong, string>();
|
private Dictionary<ulong, string> m_MessageHandlerNameLookup32 = new Dictionary<ulong, string>();
|
||||||
private Dictionary<ulong, string> m_MessageHandlerNameLookup64 = new Dictionary<ulong, string>();
|
private Dictionary<ulong, string> m_MessageHandlerNameLookup64 = new Dictionary<ulong, string>();
|
||||||
|
|
||||||
internal void InvokeNamedMessage(ulong hash, ulong sender, FastBufferReader reader)
|
internal void InvokeNamedMessage(ulong hash, ulong sender, FastBufferReader reader, int serializedHeaderSize)
|
||||||
{
|
{
|
||||||
var bytesCount = reader.Length + FastBufferWriter.GetWriteSize<MessageHeader>();
|
var bytesCount = reader.Length + serializedHeaderSize;
|
||||||
|
|
||||||
if (m_NetworkManager == null)
|
if (m_NetworkManager == null)
|
||||||
{
|
{
|
||||||
@@ -223,9 +223,9 @@ namespace Unity.Netcode
|
|||||||
var message = new NamedMessage
|
var message = new NamedMessage
|
||||||
{
|
{
|
||||||
Hash = hash,
|
Hash = hash,
|
||||||
Data = messageStream
|
SendData = messageStream,
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, networkDelivery, clientId);
|
var size = m_NetworkManager.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)
|
||||||
@@ -266,9 +266,9 @@ namespace Unity.Netcode
|
|||||||
var message = new NamedMessage
|
var message = new NamedMessage
|
||||||
{
|
{
|
||||||
Hash = hash,
|
Hash = hash,
|
||||||
Data = messageStream
|
SendData = messageStream
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, networkDelivery, clientIds);
|
var size = m_NetworkManager.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)
|
||||||
|
|||||||
@@ -13,18 +13,18 @@ namespace Unity.Netcode
|
|||||||
/// Called before an individual message is sent.
|
/// Called before an individual message is sent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="clientId">The destination clientId</param>
|
/// <param name="clientId">The destination clientId</param>
|
||||||
/// <param name="messageType">The type of the message being sent</param>
|
/// <param name="message">The message being sent</param>
|
||||||
/// <param name="delivery"></param>
|
/// <param name="delivery"></param>
|
||||||
void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery);
|
void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called after an individual message is sent.
|
/// Called after an individual message is sent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="clientId">The destination clientId</param>
|
/// <param name="clientId">The destination clientId</param>
|
||||||
/// <param name="messageType">The type of the message being sent</param>
|
/// <param name="message">The message being sent</param>
|
||||||
/// <param name="delivery"></param>
|
/// <param name="delivery"></param>
|
||||||
/// <param name="messageSizeBytes">Number of bytes in the message, not including the message header</param>
|
/// <param name="messageSizeBytes">Number of bytes in the message, not including the message header</param>
|
||||||
void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery, int messageSizeBytes);
|
void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called before an individual message is received.
|
/// Called before an individual message is received.
|
||||||
@@ -93,5 +93,23 @@ namespace Unity.Netcode
|
|||||||
/// <param name="messageType">The type of the message</param>
|
/// <param name="messageType">The type of the message</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
bool OnVerifyCanReceive(ulong senderId, Type messageType);
|
bool OnVerifyCanReceive(ulong senderId, Type messageType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after a message is serialized, but before it's handled.
|
||||||
|
/// Differs from OnBeforeReceiveMessage in that the actual message object is passed and can be inspected.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The message object</param>
|
||||||
|
/// <param name="context">The network context the message is being ahandled in</param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called after a message is serialized and handled.
|
||||||
|
/// Differs from OnAfterReceiveMessage in that the actual message object is passed and can be inspected.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The message object</param>
|
||||||
|
/// <param name="context">The network context the message is being ahandled in</param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Unity.Netcode
|
|||||||
/// static message handler for receiving messages of the following name and signature:
|
/// static message handler for receiving messages of the following name and signature:
|
||||||
///
|
///
|
||||||
/// <code>
|
/// <code>
|
||||||
/// public static void Receive(FastBufferReader reader, in NetworkContext context)
|
/// public static void Receive(FastBufferReader reader, ref NetworkContext context)
|
||||||
/// </code>
|
/// </code>
|
||||||
///
|
///
|
||||||
/// It is the responsibility of the Serialize and Receive methods to ensure there is enough buffer space
|
/// It is the responsibility of the Serialize and Receive methods to ensure there is enough buffer space
|
||||||
@@ -40,10 +40,8 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal interface INetworkMessage
|
internal interface INetworkMessage
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Used to serialize the message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer"></param>
|
|
||||||
void Serialize(FastBufferWriter writer);
|
void Serialize(FastBufferWriter writer);
|
||||||
|
bool Deserialize(FastBufferReader reader, ref NetworkContext context);
|
||||||
|
void Handle(ref NetworkContext context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ namespace Unity.Netcode
|
|||||||
/// unchanged - if new messages are added or messages are removed, MessageType assignments may be
|
/// unchanged - if new messages are added or messages are removed, MessageType assignments may be
|
||||||
/// calculated differently.
|
/// calculated differently.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte MessageType;
|
public uint MessageType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The total size of the message, NOT including the header.
|
/// The total size of the message, NOT including the header.
|
||||||
|
/// Stored as a uint to avoid zig-zag encoding, but capped at int.MaxValue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort MessageSize;
|
public uint MessageSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,24 +10,28 @@ namespace Unity.Netcode
|
|||||||
writer.WriteValueSafe(this);
|
writer.WriteValueSafe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
reader.ReadValueSafe(out ChangeOwnershipMessage message);
|
reader.ReadValueSafe(out this);
|
||||||
message.Handle(reader, context, context.SenderId, networkManager, reader.Length);
|
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
|
||||||
|
{
|
||||||
|
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, ref context);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(FastBufferReader reader, in NetworkContext context, ulong senderId, NetworkManager networkManager, int messageSize)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
|
||||||
{
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, context);
|
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (networkObject.OwnerClientId == networkManager.LocalClientId)
|
if (networkObject.OwnerClientId == networkManager.LocalClientId)
|
||||||
{
|
{
|
||||||
@@ -43,7 +47,7 @@ namespace Unity.Netcode
|
|||||||
networkObject.InvokeBehaviourOnGainedOwnership();
|
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||||
}
|
}
|
||||||
|
|
||||||
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(senderId, networkObject, messageSize);
|
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(context.SenderId, networkObject, context.MessageSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,24 +7,27 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
public ulong OwnerClientId;
|
public ulong OwnerClientId;
|
||||||
public int NetworkTick;
|
public int NetworkTick;
|
||||||
public int SceneObjectCount;
|
|
||||||
|
|
||||||
// Not serialized, held as references to serialize NetworkVariable data
|
// Not serialized, held as references to serialize NetworkVariable data
|
||||||
public HashSet<NetworkObject> SpawnedObjectsList;
|
public HashSet<NetworkObject> SpawnedObjectsList;
|
||||||
|
|
||||||
|
private FastBufferReader m_ReceivedSceneObjectData;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
if (!writer.TryBeginWrite(sizeof(ulong) + sizeof(int) + sizeof(int)))
|
if (!writer.TryBeginWrite(sizeof(ulong) + sizeof(int) + sizeof(int)))
|
||||||
{
|
{
|
||||||
throw new OverflowException(
|
throw new OverflowException($"Not enough space in the write buffer to serialize {nameof(ConnectionApprovedMessage)}");
|
||||||
$"Not enough space in the write buffer to serialize {nameof(ConnectionApprovedMessage)}");
|
|
||||||
}
|
}
|
||||||
writer.WriteValue(OwnerClientId);
|
writer.WriteValue(OwnerClientId);
|
||||||
writer.WriteValue(NetworkTick);
|
writer.WriteValue(NetworkTick);
|
||||||
writer.WriteValue(SceneObjectCount);
|
|
||||||
|
|
||||||
if (SceneObjectCount != 0)
|
uint sceneObjectCount = 0;
|
||||||
|
if (SpawnedObjectsList != null)
|
||||||
{
|
{
|
||||||
|
var pos = writer.Position;
|
||||||
|
writer.Seek(writer.Position + FastBufferWriter.GetWriteSize(sceneObjectCount));
|
||||||
|
|
||||||
// Serialize NetworkVariable data
|
// Serialize NetworkVariable data
|
||||||
foreach (var sobj in SpawnedObjectsList)
|
foreach (var sobj in SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
@@ -33,34 +36,41 @@ namespace Unity.Netcode
|
|||||||
sobj.Observers.Add(OwnerClientId);
|
sobj.Observers.Add(OwnerClientId);
|
||||||
var sceneObject = sobj.GetMessageSceneObject(OwnerClientId);
|
var sceneObject = sobj.GetMessageSceneObject(OwnerClientId);
|
||||||
sceneObject.Serialize(writer);
|
sceneObject.Serialize(writer);
|
||||||
|
++sceneObjectCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
writer.Seek(pos);
|
||||||
|
writer.WriteValue(sceneObjectCount);
|
||||||
|
writer.Seek(writer.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteValue(sceneObjectCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reader.TryBeginRead(sizeof(ulong) + sizeof(int) + sizeof(int)))
|
if (!reader.TryBeginRead(sizeof(ulong) + sizeof(int) + sizeof(int)))
|
||||||
{
|
{
|
||||||
throw new OverflowException(
|
throw new OverflowException($"Not enough space in the buffer to read {nameof(ConnectionApprovedMessage)}");
|
||||||
$"Not enough space in the buffer to read {nameof(ConnectionApprovedMessage)}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = new ConnectionApprovedMessage();
|
reader.ReadValue(out OwnerClientId);
|
||||||
reader.ReadValue(out message.OwnerClientId);
|
reader.ReadValue(out NetworkTick);
|
||||||
reader.ReadValue(out message.NetworkTick);
|
m_ReceivedSceneObjectData = reader;
|
||||||
reader.ReadValue(out message.SceneObjectCount);
|
return true;
|
||||||
message.Handle(reader, context.SenderId, networkManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(FastBufferReader reader, ulong clientId, NetworkManager networkManager)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
networkManager.LocalClientId = OwnerClientId;
|
networkManager.LocalClientId = OwnerClientId;
|
||||||
networkManager.NetworkMetrics.SetConnectionId(networkManager.LocalClientId);
|
networkManager.NetworkMetrics.SetConnectionId(networkManager.LocalClientId);
|
||||||
|
|
||||||
@@ -74,20 +84,21 @@ namespace Unity.Netcode
|
|||||||
if (!networkManager.NetworkConfig.EnableSceneManagement)
|
if (!networkManager.NetworkConfig.EnableSceneManagement)
|
||||||
{
|
{
|
||||||
networkManager.SpawnManager.DestroySceneObjects();
|
networkManager.SpawnManager.DestroySceneObjects();
|
||||||
|
m_ReceivedSceneObjectData.ReadValue(out uint sceneObjectCount);
|
||||||
|
|
||||||
// Deserializing NetworkVariable data is deferred from Receive() to Handle to avoid needing
|
// Deserializing NetworkVariable data is deferred from Receive() to Handle to avoid needing
|
||||||
// to create a list to hold the data. This is a breach of convention for performance reasons.
|
// to create a list to hold the data. This is a breach of convention for performance reasons.
|
||||||
for (ushort i = 0; i < SceneObjectCount; i++)
|
for (ushort i = 0; i < sceneObjectCount; i++)
|
||||||
{
|
{
|
||||||
var sceneObject = new NetworkObject.SceneObject();
|
var sceneObject = new NetworkObject.SceneObject();
|
||||||
sceneObject.Deserialize(reader);
|
sceneObject.Deserialize(m_ReceivedSceneObjectData);
|
||||||
NetworkObject.AddSceneObject(sceneObject, reader, networkManager);
|
NetworkObject.AddSceneObject(sceneObject, m_ReceivedSceneObjectData, networkManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(clientId);
|
networkManager.InvokeOnClientConnectedCallback(context.SenderId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,19 +21,17 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsServer)
|
if (!networkManager.IsServer)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = new ConnectionRequestMessage();
|
|
||||||
if (networkManager.NetworkConfig.ConnectionApproval)
|
if (networkManager.NetworkConfig.ConnectionApproval)
|
||||||
{
|
{
|
||||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.ConfigHash) +
|
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(ConfigHash) + FastBufferWriter.GetWriteSize<int>()))
|
||||||
FastBufferWriter.GetWriteSize<int>()))
|
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
@@ -41,11 +39,12 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
networkManager.DisconnectClient(context.SenderId);
|
networkManager.DisconnectClient(context.SenderId);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
reader.ReadValue(out message.ConfigHash);
|
|
||||||
|
|
||||||
if (!networkManager.NetworkConfig.CompareConfig(message.ConfigHash))
|
reader.ReadValue(out ConfigHash);
|
||||||
|
|
||||||
|
if (!networkManager.NetworkConfig.CompareConfig(ConfigHash))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
@@ -53,14 +52,14 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
networkManager.DisconnectClient(context.SenderId);
|
networkManager.DisconnectClient(context.SenderId);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.ReadValueSafe(out message.ConnectionData);
|
reader.ReadValueSafe(out ConnectionData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.ConfigHash)))
|
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(ConfigHash)))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
@@ -68,11 +67,11 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
networkManager.DisconnectClient(context.SenderId);
|
networkManager.DisconnectClient(context.SenderId);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
reader.ReadValue(out message.ConfigHash);
|
reader.ReadValue(out ConfigHash);
|
||||||
|
|
||||||
if (!networkManager.NetworkConfig.CompareConfig(message.ConfigHash))
|
if (!networkManager.NetworkConfig.CompareConfig(ConfigHash))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
@@ -80,14 +79,18 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
networkManager.DisconnectClient(context.SenderId);
|
networkManager.DisconnectClient(context.SenderId);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
message.Handle(networkManager, context.SenderId);
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(NetworkManager networkManager, ulong senderId)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
|
var senderId = context.SenderId;
|
||||||
|
|
||||||
if (networkManager.PendingClients.TryGetValue(senderId, out PendingClient client))
|
if (networkManager.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
|
||||||
@@ -102,8 +105,7 @@ namespace Unity.Netcode
|
|||||||
(createPlayerObject, playerPrefabHash, approved, position, rotation) =>
|
(createPlayerObject, playerPrefabHash, approved, position, rotation) =>
|
||||||
{
|
{
|
||||||
var localCreatePlayerObject = createPlayerObject;
|
var localCreatePlayerObject = createPlayerObject;
|
||||||
networkManager.HandleApproval(senderId, localCreatePlayerObject, playerPrefabHash, approved,
|
networkManager.HandleApproval(senderId, localCreatePlayerObject, playerPrefabHash, approved, position, rotation);
|
||||||
position, rotation);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -3,28 +3,33 @@ namespace Unity.Netcode
|
|||||||
internal struct CreateObjectMessage : INetworkMessage
|
internal struct CreateObjectMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
public NetworkObject.SceneObject ObjectInfo;
|
public NetworkObject.SceneObject ObjectInfo;
|
||||||
|
private FastBufferReader m_ReceivedNetworkVariableData;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
ObjectInfo.Serialize(writer);
|
ObjectInfo.Serialize(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
var message = new CreateObjectMessage();
|
|
||||||
message.ObjectInfo.Deserialize(reader);
|
ObjectInfo.Deserialize(reader);
|
||||||
message.Handle(context.SenderId, reader, networkManager);
|
m_ReceivedNetworkVariableData = reader;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ulong senderId, FastBufferReader reader, NetworkManager networkManager)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkObject = NetworkObject.AddSceneObject(ObjectInfo, reader, networkManager);
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
networkManager.NetworkMetrics.TrackObjectSpawnReceived(senderId, networkObject, reader.Length);
|
var networkObject = NetworkObject.AddSceneObject(ObjectInfo, m_ReceivedNetworkVariableData, networkManager);
|
||||||
|
|
||||||
|
networkManager.NetworkMetrics.TrackObjectSpawnReceived(context.SenderId, networkObject, context.MessageSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,32 +9,27 @@ namespace Unity.Netcode
|
|||||||
writer.WriteValueSafe(this);
|
writer.WriteValueSafe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
reader.ReadValueSafe(out DestroyObjectMessage message);
|
reader.ReadValueSafe(out this);
|
||||||
message.Handle(context.SenderId, networkManager, reader.Length);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ulong senderId, NetworkManager networkManager, int messageSize)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
||||||
{
|
{
|
||||||
// This is the same check and log message that happens inside OnDespawnObject, but we have to do it here
|
// This is the same check and log message that happens inside OnDespawnObject, but we have to do it here
|
||||||
// while we still have access to the network ID, otherwise the log message will be less useful.
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning($"Trying to destroy {nameof(NetworkObject)} #{NetworkObjectId} but it does not exist in {nameof(NetworkSpawnManager.SpawnedObjects)} anymore!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
networkManager.NetworkMetrics.TrackObjectDestroyReceived(senderId, networkObject, messageSize);
|
networkManager.NetworkMetrics.TrackObjectDestroyReceived(context.SenderId, networkObject, context.MessageSize);
|
||||||
networkManager.SpawnManager.OnDespawnObject(networkObject, true);
|
networkManager.SpawnManager.OnDespawnObject(networkObject, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,26 @@ namespace Unity.Netcode
|
|||||||
internal struct NamedMessage : INetworkMessage
|
internal struct NamedMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
public ulong Hash;
|
public ulong Hash;
|
||||||
public FastBufferWriter Data;
|
public FastBufferWriter SendData;
|
||||||
|
|
||||||
|
private FastBufferReader m_ReceiveData;
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
public unsafe void Serialize(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(Hash);
|
writer.WriteValueSafe(Hash);
|
||||||
writer.WriteBytesSafe(Data.GetUnsafePtr(), Data.Length);
|
writer.WriteBytesSafe(SendData.GetUnsafePtr(), SendData.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var message = new NamedMessage();
|
reader.ReadValueSafe(out Hash);
|
||||||
reader.ReadValueSafe(out message.Hash);
|
m_ReceiveData = reader;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(message.Hash, context.SenderId, reader);
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(Hash, context.SenderId, m_ReceiveData, context.SerializedHeaderSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,16 +19,18 @@ namespace Unity.Netcode
|
|||||||
public ulong ClientId;
|
public ulong ClientId;
|
||||||
public NetworkBehaviour NetworkBehaviour;
|
public NetworkBehaviour NetworkBehaviour;
|
||||||
|
|
||||||
|
private FastBufferReader m_ReceivedNetworkVariableData;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) +
|
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
|
||||||
FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
|
|
||||||
{
|
{
|
||||||
throw new OverflowException(
|
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
||||||
$"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteValue(NetworkObjectId);
|
writer.WriteValue(NetworkObjectId);
|
||||||
writer.WriteValue(NetworkBehaviourIndex);
|
writer.WriteValue(NetworkBehaviourIndex);
|
||||||
|
|
||||||
for (int k = 0; k < NetworkBehaviour.NetworkVariableFields.Count; k++)
|
for (int k = 0; k < NetworkBehaviour.NetworkVariableFields.Count; k++)
|
||||||
{
|
{
|
||||||
if (!DeliveryMappedNetworkVariableIndex.Contains(k))
|
if (!DeliveryMappedNetworkVariableIndex.Contains(k))
|
||||||
@@ -36,7 +38,7 @@ namespace Unity.Netcode
|
|||||||
// This var does not belong to the currently iterating delivery group.
|
// This var does not belong to the currently iterating delivery group.
|
||||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe((short)0);
|
writer.WriteValueSafe((ushort)0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -69,7 +71,12 @@ namespace Unity.Netcode
|
|||||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, short.MaxValue);
|
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, short.MaxValue);
|
||||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(tmpWriter);
|
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(tmpWriter);
|
||||||
|
|
||||||
writer.WriteValueSafe((ushort)tmpWriter.Length);
|
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize<ushort>() + tmpWriter.Length))
|
||||||
|
{
|
||||||
|
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteValue((ushort)tmpWriter.Length);
|
||||||
tmpWriter.CopyTo(writer);
|
tmpWriter.CopyTo(writer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -93,24 +100,25 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
|
{
|
||||||
|
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
|
||||||
|
{
|
||||||
|
throw new OverflowException($"Not enough data in the buffer to read {nameof(NetworkVariableDeltaMessage)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.ReadValue(out NetworkObjectId);
|
||||||
|
reader.ReadValue(out NetworkBehaviourIndex);
|
||||||
|
|
||||||
|
m_ReceivedNetworkVariableData = reader;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
|
|
||||||
var message = new NetworkVariableDeltaMessage();
|
|
||||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.NetworkObjectId) +
|
|
||||||
FastBufferWriter.GetWriteSize(message.NetworkBehaviourIndex)))
|
|
||||||
{
|
|
||||||
throw new OverflowException(
|
|
||||||
$"Not enough data in the buffer to read {nameof(NetworkVariableDeltaMessage)}");
|
|
||||||
}
|
|
||||||
reader.ReadValue(out message.NetworkObjectId);
|
|
||||||
reader.ReadValue(out message.NetworkBehaviourIndex);
|
|
||||||
message.Handle(context.SenderId, reader, context, networkManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(ulong senderId, FastBufferReader reader, in NetworkContext context, NetworkManager networkManager)
|
|
||||||
{
|
|
||||||
if (networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out NetworkObject networkObject))
|
if (networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out NetworkObject networkObject))
|
||||||
{
|
{
|
||||||
NetworkBehaviour behaviour = networkObject.GetNetworkBehaviourAtOrderIndex(NetworkBehaviourIndex);
|
NetworkBehaviour behaviour = networkObject.GetNetworkBehaviourAtOrderIndex(NetworkBehaviourIndex);
|
||||||
@@ -130,7 +138,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out varSize);
|
m_ReceivedNetworkVariableData.ReadValueSafe(out varSize);
|
||||||
|
|
||||||
if (varSize == 0)
|
if (varSize == 0)
|
||||||
{
|
{
|
||||||
@@ -139,7 +147,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out bool deltaExists);
|
m_ReceivedNetworkVariableData.ReadValueSafe(out bool deltaExists);
|
||||||
if (!deltaExists)
|
if (!deltaExists)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -157,7 +165,7 @@ namespace Unity.Netcode
|
|||||||
NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Seek(reader.Position + varSize);
|
m_ReceivedNetworkVariableData.Seek(m_ReceivedNetworkVariableData.Position + varSize);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,39 +184,37 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int readStartPos = reader.Position;
|
int readStartPos = m_ReceivedNetworkVariableData.Position;
|
||||||
|
|
||||||
behaviour.NetworkVariableFields[i].ReadDelta(reader, networkManager.IsServer);
|
behaviour.NetworkVariableFields[i].ReadDelta(m_ReceivedNetworkVariableData, networkManager.IsServer);
|
||||||
|
|
||||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
||||||
senderId,
|
context.SenderId,
|
||||||
networkObject,
|
networkObject,
|
||||||
behaviour.NetworkVariableFields[i].Name,
|
behaviour.NetworkVariableFields[i].Name,
|
||||||
behaviour.__getTypeName(),
|
behaviour.__getTypeName(),
|
||||||
reader.Length);
|
context.MessageSize);
|
||||||
|
|
||||||
|
|
||||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
if (reader.Position > (readStartPos + varSize))
|
if (m_ReceivedNetworkVariableData.Position > (readStartPos + varSize))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning(
|
NetworkLog.LogWarning($"Var delta read too far. {m_ReceivedNetworkVariableData.Position - (readStartPos + varSize)} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
||||||
$"Var delta read too far. {reader.Position - (readStartPos + varSize)} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Seek(readStartPos + varSize);
|
m_ReceivedNetworkVariableData.Seek(readStartPos + varSize);
|
||||||
}
|
}
|
||||||
else if (reader.Position < (readStartPos + varSize))
|
else if (m_ReceivedNetworkVariableData.Position < (readStartPos + varSize))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning(
|
NetworkLog.LogWarning($"Var delta read too little. {(readStartPos + varSize) - m_ReceivedNetworkVariableData.Position} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
||||||
$"Var delta read too little. {(readStartPos + varSize) - reader.Position} bytes. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Seek(readStartPos + varSize);
|
m_ReceivedNetworkVariableData.Seek(readStartPos + varSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,7 +222,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, context);
|
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, m_ReceivedNetworkVariableData, ref context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,42 +26,41 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = new ParentSyncMessage();
|
reader.ReadValueSafe(out NetworkObjectId);
|
||||||
reader.ReadValueSafe(out message.NetworkObjectId);
|
reader.ReadValueSafe(out IsReparented);
|
||||||
reader.ReadValueSafe(out message.IsReparented);
|
if (IsReparented)
|
||||||
if (message.IsReparented)
|
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out message.IsLatestParentSet);
|
reader.ReadValueSafe(out IsLatestParentSet);
|
||||||
if (message.IsLatestParentSet)
|
if (IsLatestParentSet)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out ulong latestParent);
|
reader.ReadValueSafe(out ulong latestParent);
|
||||||
message.LatestParent = latestParent;
|
LatestParent = latestParent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message.Handle(reader, context, networkManager);
|
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
|
||||||
|
{
|
||||||
|
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, ref context);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(FastBufferReader reader, in NetworkContext context, NetworkManager networkManager)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
if (networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
{
|
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
||||||
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
networkObject.SetNetworkParenting(IsReparented, LatestParent);
|
||||||
networkObject.SetNetworkParenting(IsReparented, LatestParent);
|
networkObject.ApplyNetworkParenting();
|
||||||
networkObject.ApplyNetworkParenting();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal struct RpcMessage : INetworkMessage
|
|
||||||
{
|
|
||||||
public enum RpcType : byte
|
|
||||||
{
|
|
||||||
Server,
|
|
||||||
Client
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct HeaderData
|
|
||||||
{
|
|
||||||
public RpcType Type;
|
|
||||||
public ulong NetworkObjectId;
|
|
||||||
public ushort NetworkBehaviourId;
|
|
||||||
public uint NetworkMethodId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HeaderData Header;
|
|
||||||
public FastBufferWriter RpcData;
|
|
||||||
|
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
|
||||||
{
|
|
||||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(Header) + RpcData.Length))
|
|
||||||
{
|
|
||||||
throw new OverflowException("Not enough space in the buffer to store RPC data.");
|
|
||||||
}
|
|
||||||
writer.WriteValue(Header);
|
|
||||||
writer.WriteBytes(RpcData.GetUnsafePtr(), RpcData.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
|
||||||
{
|
|
||||||
var message = new RpcMessage();
|
|
||||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.Header)))
|
|
||||||
{
|
|
||||||
throw new OverflowException("Not enough space in the buffer to read RPC data.");
|
|
||||||
}
|
|
||||||
reader.ReadValue(out message.Header);
|
|
||||||
message.Handle(reader, context, (NetworkManager)context.SystemOwner, context.SenderId, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(FastBufferReader reader, in NetworkContext context, NetworkManager networkManager, ulong senderId, bool canDefer)
|
|
||||||
{
|
|
||||||
if (NetworkManager.__rpc_func_table.ContainsKey(Header.NetworkMethodId))
|
|
||||||
{
|
|
||||||
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(Header.NetworkObjectId))
|
|
||||||
{
|
|
||||||
if (canDefer)
|
|
||||||
{
|
|
||||||
networkManager.SpawnManager.TriggerOnSpawn(Header.NetworkObjectId, reader, context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NetworkLog.LogError($"Tried to invoke an RPC on a non-existent {nameof(NetworkObject)} with {nameof(canDefer)}=false");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var networkObject = networkManager.SpawnManager.SpawnedObjects[Header.NetworkObjectId];
|
|
||||||
|
|
||||||
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(Header.NetworkBehaviourId);
|
|
||||||
if (networkBehaviour == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rpcParams = new __RpcParams();
|
|
||||||
switch (Header.Type)
|
|
||||||
{
|
|
||||||
case RpcType.Server:
|
|
||||||
rpcParams.Server = new ServerRpcParams
|
|
||||||
{
|
|
||||||
Receive = new ServerRpcReceiveParams
|
|
||||||
{
|
|
||||||
SenderClientId = senderId
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case RpcType.Client:
|
|
||||||
rpcParams.Client = new ClientRpcParams
|
|
||||||
{
|
|
||||||
Receive = new ClientRpcReceiveParams
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkManager.__rpc_func_table[Header.NetworkMethodId](networkBehaviour, reader, rpcParams);
|
|
||||||
|
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
|
||||||
if (NetworkManager.__rpc_name_table.TryGetValue(Header.NetworkMethodId, out var rpcMethodName))
|
|
||||||
{
|
|
||||||
networkManager.NetworkMetrics.TrackRpcReceived(
|
|
||||||
senderId,
|
|
||||||
networkObject,
|
|
||||||
rpcMethodName,
|
|
||||||
networkBehaviour.__getTypeName(),
|
|
||||||
reader.Length);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
157
Runtime/Messaging/Messages/RpcMessages.cs
Normal file
157
Runtime/Messaging/Messages/RpcMessages.cs
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using Unity.Collections;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
internal static class RpcMessageHelpers
|
||||||
|
{
|
||||||
|
public static unsafe void Serialize(ref FastBufferWriter writer, ref RpcMetadata metadata, ref FastBufferWriter payload)
|
||||||
|
{
|
||||||
|
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize<RpcMetadata>() + payload.Length))
|
||||||
|
{
|
||||||
|
throw new OverflowException("Not enough space in the buffer to store RPC data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteValue(metadata);
|
||||||
|
writer.WriteBytes(payload.GetUnsafePtr(), payload.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe bool Deserialize(ref FastBufferReader reader, ref NetworkContext context, ref RpcMetadata metadata, ref FastBufferReader payload)
|
||||||
|
{
|
||||||
|
int metadataSize = FastBufferWriter.GetWriteSize<RpcMetadata>();
|
||||||
|
if (!reader.TryBeginRead(metadataSize))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Not enough data in the buffer to read RPC meta.");
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.ReadValue(out metadata);
|
||||||
|
|
||||||
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
|
if (!networkManager.SpawnManager.SpawnedObjects.ContainsKey(metadata.NetworkObjectId))
|
||||||
|
{
|
||||||
|
networkManager.SpawnManager.TriggerOnSpawn(metadata.NetworkObjectId, reader, ref context);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var networkObject = networkManager.SpawnManager.SpawnedObjects[metadata.NetworkObjectId];
|
||||||
|
var networkBehaviour = networkManager.SpawnManager.SpawnedObjects[metadata.NetworkObjectId].GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId);
|
||||||
|
if (networkBehaviour == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NetworkManager.__rpc_func_table.ContainsKey(metadata.NetworkRpcMethodId))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = new FastBufferReader(reader.GetUnsafePtr() + metadataSize, Allocator.None, reader.Length - metadataSize);
|
||||||
|
|
||||||
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
|
if (NetworkManager.__rpc_name_table.TryGetValue(metadata.NetworkRpcMethodId, out var rpcMethodName))
|
||||||
|
{
|
||||||
|
networkManager.NetworkMetrics.TrackRpcReceived(
|
||||||
|
context.SenderId,
|
||||||
|
networkObject,
|
||||||
|
rpcMethodName,
|
||||||
|
networkBehaviour.__getTypeName(),
|
||||||
|
reader.Length);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Handle(ref NetworkContext context, ref RpcMetadata metadata, ref FastBufferReader payload, ref __RpcParams rpcParams)
|
||||||
|
{
|
||||||
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
|
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(metadata.NetworkObjectId, out var networkObject))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"An RPC called on a {nameof(NetworkObject)} that is not in the spawned objects list. Please make sure the {nameof(NetworkObject)} is spawned before calling RPCs.");
|
||||||
|
}
|
||||||
|
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NetworkManager.__rpc_func_table[metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogException(new Exception("Unhandled RPC exception!", ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct RpcMetadata
|
||||||
|
{
|
||||||
|
public ulong NetworkObjectId;
|
||||||
|
public ushort NetworkBehaviourId;
|
||||||
|
public uint NetworkRpcMethodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct ServerRpcMessage : INetworkMessage
|
||||||
|
{
|
||||||
|
public RpcMetadata Metadata;
|
||||||
|
|
||||||
|
public FastBufferWriter WriteBuffer;
|
||||||
|
public FastBufferReader ReadBuffer;
|
||||||
|
|
||||||
|
public unsafe void Serialize(FastBufferWriter writer)
|
||||||
|
{
|
||||||
|
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
|
{
|
||||||
|
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
var rpcParams = new __RpcParams
|
||||||
|
{
|
||||||
|
Server = new ServerRpcParams
|
||||||
|
{
|
||||||
|
Receive = new ServerRpcReceiveParams
|
||||||
|
{
|
||||||
|
SenderClientId = context.SenderId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RpcMessageHelpers.Handle(ref context, ref Metadata, ref ReadBuffer, ref rpcParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct ClientRpcMessage : INetworkMessage
|
||||||
|
{
|
||||||
|
public RpcMetadata Metadata;
|
||||||
|
|
||||||
|
public FastBufferWriter WriteBuffer;
|
||||||
|
public FastBufferReader ReadBuffer;
|
||||||
|
|
||||||
|
public void Serialize(FastBufferWriter writer)
|
||||||
|
{
|
||||||
|
RpcMessageHelpers.Serialize(ref writer, ref Metadata, ref WriteBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
|
{
|
||||||
|
return RpcMessageHelpers.Deserialize(ref reader, ref context, ref Metadata, ref ReadBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
var rpcParams = new __RpcParams
|
||||||
|
{
|
||||||
|
Client = new ClientRpcParams
|
||||||
|
{
|
||||||
|
Receive = new ClientRpcReceiveParams
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RpcMessageHelpers.Handle(ref context, ref Metadata, ref ReadBuffer, ref rpcParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,14 +6,22 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
public SceneEventData EventData;
|
public SceneEventData EventData;
|
||||||
|
|
||||||
|
private FastBufferReader m_ReceivedData;
|
||||||
|
|
||||||
public void Serialize(FastBufferWriter writer)
|
public void Serialize(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
EventData.Serialize(writer);
|
EventData.Serialize(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
((NetworkManager)context.SystemOwner).SceneManager.HandleSceneEvent(context.SenderId, reader);
|
m_ReceivedData = reader;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
((NetworkManager)context.SystemOwner).SceneManager.HandleSceneEvent(context.SenderId, m_ReceivedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,21 +17,25 @@ namespace Unity.Netcode
|
|||||||
BytePacker.WriteValuePacked(writer, Message);
|
BytePacker.WriteValuePacked(writer, Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (networkManager.IsServer && networkManager.NetworkConfig.EnableNetworkLogs)
|
if (networkManager.IsServer && networkManager.NetworkConfig.EnableNetworkLogs)
|
||||||
{
|
{
|
||||||
var message = new ServerLogMessage();
|
reader.ReadValueSafe(out LogType);
|
||||||
reader.ReadValueSafe(out message.LogType);
|
ByteUnpacker.ReadValuePacked(reader, out Message);
|
||||||
ByteUnpacker.ReadValuePacked(reader, out message.Message);
|
return true;
|
||||||
message.Handle(context.SenderId, networkManager, reader.Length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ulong senderId, NetworkManager networkManager, int messageSize)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
networkManager.NetworkMetrics.TrackServerLogReceived(senderId, (uint)LogType, messageSize);
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
|
var senderId = context.SenderId;
|
||||||
|
|
||||||
|
networkManager.NetworkMetrics.TrackServerLogReceived(senderId, (uint)LogType, context.MessageSize);
|
||||||
|
|
||||||
switch (LogType)
|
switch (LogType)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -76,9 +76,6 @@ namespace Unity.Netcode
|
|||||||
Despawns.Length * sizeof(DespawnData)
|
Despawns.Length * sizeof(DespawnData)
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
Entries.Dispose();
|
|
||||||
Spawns.Dispose();
|
|
||||||
Despawns.Dispose();
|
|
||||||
throw new OverflowException($"Not enough space to serialize {nameof(SnapshotDataMessage)}");
|
throw new OverflowException($"Not enough space to serialize {nameof(SnapshotDataMessage)}");
|
||||||
}
|
}
|
||||||
writer.WriteValue(CurrentTick);
|
writer.WriteValue(CurrentTick);
|
||||||
@@ -96,66 +93,68 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
writer.WriteValue((ushort)Despawns.Length);
|
writer.WriteValue((ushort)Despawns.Length);
|
||||||
writer.WriteBytes((byte*)Despawns.GetUnsafePtr(), Despawns.Length * sizeof(DespawnData));
|
writer.WriteBytes((byte*)Despawns.GetUnsafePtr(), Despawns.Length * sizeof(DespawnData));
|
||||||
|
|
||||||
Entries.Dispose();
|
|
||||||
Spawns.Dispose();
|
|
||||||
Despawns.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe void Receive(FastBufferReader reader, in NetworkContext context)
|
public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
|
||||||
var message = new SnapshotDataMessage();
|
|
||||||
if (!reader.TryBeginRead(
|
if (!reader.TryBeginRead(
|
||||||
FastBufferWriter.GetWriteSize(message.CurrentTick) +
|
FastBufferWriter.GetWriteSize(CurrentTick) +
|
||||||
FastBufferWriter.GetWriteSize(message.Sequence) +
|
FastBufferWriter.GetWriteSize(Sequence) +
|
||||||
FastBufferWriter.GetWriteSize(message.Range)
|
FastBufferWriter.GetWriteSize(Range)
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
throw new OverflowException($"Not enough space to deserialize {nameof(SnapshotDataMessage)}");
|
throw new OverflowException($"Not enough space to deserialize {nameof(SnapshotDataMessage)}");
|
||||||
}
|
}
|
||||||
reader.ReadValue(out message.CurrentTick);
|
reader.ReadValue(out CurrentTick);
|
||||||
reader.ReadValue(out message.Sequence);
|
reader.ReadValue(out Sequence);
|
||||||
|
|
||||||
reader.ReadValue(out message.Range);
|
reader.ReadValue(out Range);
|
||||||
message.ReceiveMainBuffer = new NativeArray<byte>(message.Range, Allocator.Temp);
|
ReceiveMainBuffer = new NativeArray<byte>(Range, Allocator.Temp);
|
||||||
reader.ReadBytesSafe((byte*)message.ReceiveMainBuffer.GetUnsafePtr(), message.Range);
|
reader.ReadBytesSafe((byte*)ReceiveMainBuffer.GetUnsafePtr(), Range);
|
||||||
reader.ReadValueSafe(out message.Ack);
|
reader.ReadValueSafe(out Ack);
|
||||||
|
|
||||||
reader.ReadValueSafe(out ushort length);
|
reader.ReadValueSafe(out ushort length);
|
||||||
message.Entries = new NativeList<EntryData>(length, Allocator.Temp);
|
Entries = new NativeList<EntryData>(length, Allocator.Temp) { Length = length };
|
||||||
message.Entries.Length = length;
|
reader.ReadBytesSafe((byte*)Entries.GetUnsafePtr(), Entries.Length * sizeof(EntryData));
|
||||||
reader.ReadBytesSafe((byte*)message.Entries.GetUnsafePtr(), message.Entries.Length * sizeof(EntryData));
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out length);
|
reader.ReadValueSafe(out length);
|
||||||
message.Spawns = new NativeList<SpawnData>(length, Allocator.Temp);
|
Spawns = new NativeList<SpawnData>(length, Allocator.Temp) { Length = length };
|
||||||
message.Spawns.Length = length;
|
reader.ReadBytesSafe((byte*)Spawns.GetUnsafePtr(), Spawns.Length * sizeof(SpawnData));
|
||||||
reader.ReadBytesSafe((byte*)message.Spawns.GetUnsafePtr(), message.Spawns.Length * sizeof(SpawnData));
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out length);
|
reader.ReadValueSafe(out length);
|
||||||
message.Despawns = new NativeList<DespawnData>(length, Allocator.Temp);
|
Despawns = new NativeList<DespawnData>(length, Allocator.Temp) { Length = length };
|
||||||
message.Despawns.Length = length;
|
reader.ReadBytesSafe((byte*)Despawns.GetUnsafePtr(), Despawns.Length * sizeof(DespawnData));
|
||||||
reader.ReadBytesSafe((byte*)message.Despawns.GetUnsafePtr(), message.Despawns.Length * sizeof(DespawnData));
|
|
||||||
|
|
||||||
using (message.ReceiveMainBuffer)
|
return true;
|
||||||
using (message.Entries)
|
|
||||||
using (message.Spawns)
|
|
||||||
using (message.Despawns)
|
|
||||||
{
|
|
||||||
message.Handle(context.SenderId, networkManager);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ulong senderId, NetworkManager networkManager)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
// todo: temporary hack around bug
|
using (ReceiveMainBuffer)
|
||||||
if (!networkManager.IsServer)
|
using (Entries)
|
||||||
|
using (Spawns)
|
||||||
|
using (Despawns)
|
||||||
{
|
{
|
||||||
senderId = networkManager.ServerClientId;
|
var systemOwner = context.SystemOwner;
|
||||||
}
|
var senderId = context.SenderId;
|
||||||
|
if (systemOwner is NetworkManager networkManager)
|
||||||
|
{
|
||||||
|
// todo: temporary hack around bug
|
||||||
|
if (!networkManager.IsServer)
|
||||||
|
{
|
||||||
|
senderId = networkManager.ServerClientId;
|
||||||
|
}
|
||||||
|
|
||||||
var snapshotSystem = networkManager.SnapshotSystem;
|
var snapshotSystem = networkManager.SnapshotSystem;
|
||||||
snapshotSystem.HandleSnapshot(senderId, this);
|
snapshotSystem.HandleSnapshot(senderId, this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var ownerData = (Tuple<SnapshotSystem, ulong>)systemOwner;
|
||||||
|
var snapshotSystem = ownerData.Item1;
|
||||||
|
snapshotSystem.HandleSnapshot(ownerData.Item2, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,21 +9,22 @@ namespace Unity.Netcode
|
|||||||
writer.WriteValueSafe(this);
|
writer.WriteValueSafe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
var networkManager = (NetworkManager)context.SystemOwner;
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
if (!networkManager.IsClient)
|
if (!networkManager.IsClient)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
reader.ReadValueSafe(out TimeSyncMessage message);
|
reader.ReadValueSafe(out this);
|
||||||
message.Handle(context.SenderId, networkManager);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Handle(ulong senderId, NetworkManager networkManager)
|
public void Handle(ref NetworkContext context)
|
||||||
{
|
{
|
||||||
|
var networkManager = (NetworkManager)context.SystemOwner;
|
||||||
var time = new NetworkTime(networkManager.NetworkTickSystem.TickRate, Tick);
|
var time = new NetworkTime(networkManager.NetworkTickSystem.TickRate, Tick);
|
||||||
networkManager.NetworkTimeSystem.Sync(time.Time, networkManager.NetworkConfig.NetworkTransport.GetCurrentRtt(senderId) / 1000d);
|
networkManager.NetworkTimeSystem.Sync(time.Time, networkManager.NetworkConfig.NetworkTransport.GetCurrentRtt(context.SenderId) / 1000d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,23 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
internal struct UnnamedMessage : INetworkMessage
|
internal struct UnnamedMessage : INetworkMessage
|
||||||
{
|
{
|
||||||
public FastBufferWriter Data;
|
public FastBufferWriter SendData;
|
||||||
|
private FastBufferReader m_ReceivedData;
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
public unsafe void Serialize(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
writer.WriteBytesSafe(Data.GetUnsafePtr(), Data.Length);
|
writer.WriteBytesSafe(SendData.GetUnsafePtr(), SendData.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeUnnamedMessage(context.SenderId, reader);
|
m_ReceivedData = reader;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeUnnamedMessage(context.SenderId, m_ReceivedData, context.SerializedHeaderSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace Unity.Netcode
|
|||||||
public MessageHeader Header;
|
public MessageHeader Header;
|
||||||
public ulong SenderId;
|
public ulong SenderId;
|
||||||
public float Timestamp;
|
public float Timestamp;
|
||||||
|
public int MessageHeaderSerializedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct SendQueueItem
|
private struct SendQueueItem
|
||||||
@@ -39,34 +40,34 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal delegate void MessageHandler(FastBufferReader reader, in NetworkContext context);
|
internal delegate void MessageHandler(FastBufferReader reader, ref NetworkContext context, MessagingSystem system);
|
||||||
|
|
||||||
private NativeList<ReceiveQueueItem> m_IncomingMessageQueue = new NativeList<ReceiveQueueItem>(16, Allocator.Persistent);
|
private NativeList<ReceiveQueueItem> m_IncomingMessageQueue = new NativeList<ReceiveQueueItem>(16, Allocator.Persistent);
|
||||||
|
|
||||||
private MessageHandler[] m_MessageHandlers = new MessageHandler[255];
|
private MessageHandler[] m_MessageHandlers = new MessageHandler[255];
|
||||||
private Type[] m_ReverseTypeMap = new Type[255];
|
private Type[] m_ReverseTypeMap = new Type[255];
|
||||||
|
|
||||||
private Dictionary<Type, byte> m_MessageTypes = new Dictionary<Type, byte>();
|
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 List<INetworkHooks> m_Hooks = new List<INetworkHooks>();
|
private List<INetworkHooks> m_Hooks = new List<INetworkHooks>();
|
||||||
|
|
||||||
private byte m_HighMessageType;
|
private uint m_HighMessageType;
|
||||||
private object m_Owner;
|
private object m_Owner;
|
||||||
private IMessageSender m_MessageSender;
|
private IMessageSender m_MessageSender;
|
||||||
private bool m_Disposed;
|
private bool m_Disposed;
|
||||||
|
|
||||||
internal Type[] MessageTypes => m_ReverseTypeMap;
|
internal Type[] MessageTypes => m_ReverseTypeMap;
|
||||||
internal MessageHandler[] MessageHandlers => m_MessageHandlers;
|
internal MessageHandler[] MessageHandlers => m_MessageHandlers;
|
||||||
internal int MessageHandlerCount => m_HighMessageType;
|
internal uint MessageHandlerCount => m_HighMessageType;
|
||||||
|
|
||||||
internal byte GetMessageType(Type t)
|
internal uint GetMessageType(Type t)
|
||||||
{
|
{
|
||||||
return m_MessageTypes[t];
|
return m_MessageTypes[t];
|
||||||
}
|
}
|
||||||
|
|
||||||
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
||||||
public const int FRAGMENTED_MESSAGE_MAX_SIZE = 64000;
|
public const int FRAGMENTED_MESSAGE_MAX_SIZE = int.MaxValue;
|
||||||
|
|
||||||
internal struct MessageWithHandler
|
internal struct MessageWithHandler
|
||||||
{
|
{
|
||||||
@@ -100,7 +101,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public unsafe void Dispose()
|
||||||
{
|
{
|
||||||
if (m_Disposed)
|
if (m_Disposed)
|
||||||
{
|
{
|
||||||
@@ -113,6 +114,14 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
CleanupDisconnectedClient(kvp.Key);
|
CleanupDisconnectedClient(kvp.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var queueIndex = 0; queueIndex < m_IncomingMessageQueue.Length; ++queueIndex)
|
||||||
|
{
|
||||||
|
// Avoid copies...
|
||||||
|
ref var item = ref m_IncomingMessageQueue.ElementAt(queueIndex);
|
||||||
|
item.Reader.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
m_IncomingMessageQueue.Dispose();
|
m_IncomingMessageQueue.Dispose();
|
||||||
m_Disposed = true;
|
m_Disposed = true;
|
||||||
}
|
}
|
||||||
@@ -141,7 +150,7 @@ namespace Unity.Netcode
|
|||||||
fixed (byte* nativeData = data.Array)
|
fixed (byte* nativeData = data.Array)
|
||||||
{
|
{
|
||||||
var batchReader =
|
var batchReader =
|
||||||
new FastBufferReader(nativeData, Allocator.None, data.Count, data.Offset);
|
new FastBufferReader(nativeData + data.Offset, Allocator.None, data.Count);
|
||||||
if (!batchReader.TryBeginRead(sizeof(BatchHeader)))
|
if (!batchReader.TryBeginRead(sizeof(BatchHeader)))
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning("Received a packet too small to contain a BatchHeader. Ignoring it.");
|
NetworkLog.LogWarning("Received a packet too small to contain a BatchHeader. Ignoring it.");
|
||||||
@@ -157,14 +166,23 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (var messageIdx = 0; messageIdx < batchHeader.BatchSize; ++messageIdx)
|
for (var messageIdx = 0; messageIdx < batchHeader.BatchSize; ++messageIdx)
|
||||||
{
|
{
|
||||||
if (!batchReader.TryBeginRead(sizeof(MessageHeader)))
|
|
||||||
|
var messageHeader = new MessageHeader();
|
||||||
|
var position = batchReader.Position;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ByteUnpacker.ReadValueBitPacked(batchReader, out messageHeader.MessageType);
|
||||||
|
ByteUnpacker.ReadValueBitPacked(batchReader, out messageHeader.MessageSize);
|
||||||
|
}
|
||||||
|
catch (OverflowException)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning("Received a batch that didn't have enough data for all of its batches, ending early!");
|
NetworkLog.LogWarning("Received a batch that didn't have enough data for all of its batches, ending early!");
|
||||||
return;
|
throw;
|
||||||
}
|
}
|
||||||
batchReader.ReadValue(out MessageHeader messageHeader);
|
|
||||||
|
|
||||||
if (!batchReader.TryBeginRead(messageHeader.MessageSize))
|
var receivedHeaderSize = batchReader.Position - position;
|
||||||
|
|
||||||
|
if (!batchReader.TryBeginRead((int)messageHeader.MessageSize))
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning("Received a message that claimed a size larger than the packet, ending early!");
|
NetworkLog.LogWarning("Received a message that claimed a size larger than the packet, ending early!");
|
||||||
return;
|
return;
|
||||||
@@ -177,9 +195,10 @@ namespace Unity.Netcode
|
|||||||
// 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, messageHeader.MessageSize)
|
Reader = new FastBufferReader(batchReader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, (int)messageHeader.MessageSize),
|
||||||
|
MessageHeaderSerializedSize = receivedHeaderSize,
|
||||||
});
|
});
|
||||||
batchReader.Seek(batchReader.Position + 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)
|
||||||
{
|
{
|
||||||
@@ -202,7 +221,7 @@ namespace Unity.Netcode
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleMessage(in MessageHeader header, FastBufferReader reader, ulong senderId, float timestamp)
|
public void HandleMessage(in MessageHeader header, FastBufferReader reader, ulong senderId, float timestamp, int serializedHeaderSize)
|
||||||
{
|
{
|
||||||
if (header.MessageType >= m_HighMessageType)
|
if (header.MessageType >= m_HighMessageType)
|
||||||
{
|
{
|
||||||
@@ -215,8 +234,11 @@ namespace Unity.Netcode
|
|||||||
SystemOwner = m_Owner,
|
SystemOwner = m_Owner,
|
||||||
SenderId = senderId,
|
SenderId = senderId,
|
||||||
Timestamp = timestamp,
|
Timestamp = timestamp,
|
||||||
Header = header
|
Header = header,
|
||||||
|
SerializedHeaderSize = serializedHeaderSize,
|
||||||
|
MessageSize = header.MessageSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
var type = m_ReverseTypeMap[header.MessageType];
|
var type = m_ReverseTypeMap[header.MessageType];
|
||||||
if (!CanReceive(senderId, type))
|
if (!CanReceive(senderId, type))
|
||||||
{
|
{
|
||||||
@@ -228,6 +250,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type, reader.Length + FastBufferWriter.GetWriteSize<MessageHeader>());
|
m_Hooks[hookIdx].OnBeforeReceiveMessage(senderId, type, reader.Length + FastBufferWriter.GetWriteSize<MessageHeader>());
|
||||||
}
|
}
|
||||||
|
|
||||||
var handler = m_MessageHandlers[header.MessageType];
|
var handler = m_MessageHandlers[header.MessageType];
|
||||||
using (reader)
|
using (reader)
|
||||||
{
|
{
|
||||||
@@ -238,7 +261,7 @@ namespace Unity.Netcode
|
|||||||
// for some dynamic-length value.
|
// for some dynamic-length value.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
handler.Invoke(reader, context);
|
handler.Invoke(reader, ref context, this);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -253,11 +276,15 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal unsafe void ProcessIncomingMessageQueue()
|
internal unsafe void ProcessIncomingMessageQueue()
|
||||||
{
|
{
|
||||||
for (var i = 0; i < m_IncomingMessageQueue.Length; ++i)
|
for (var index = 0; index < m_IncomingMessageQueue.Length; ++index)
|
||||||
{
|
{
|
||||||
// Avoid copies...
|
// Avoid copies...
|
||||||
ref var item = ref m_IncomingMessageQueue.GetUnsafeList()->ElementAt(i);
|
ref var item = ref m_IncomingMessageQueue.ElementAt(index);
|
||||||
HandleMessage(item.Header, item.Reader, item.SenderId, item.Timestamp);
|
HandleMessage(item.Header, item.Reader, item.SenderId, item.Timestamp, item.MessageHeaderSerializedSize);
|
||||||
|
if (m_Disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_IncomingMessageQueue.Clear();
|
m_IncomingMessageQueue.Clear();
|
||||||
@@ -287,12 +314,31 @@ namespace Unity.Netcode
|
|||||||
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)
|
||||||
{
|
{
|
||||||
queue.GetUnsafeList()->ElementAt(i).Writer.Dispose();
|
queue.ElementAt(i).Writer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.Dispose();
|
queue.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ReceiveMessage<T>(FastBufferReader reader, ref NetworkContext context, MessagingSystem system) where T : INetworkMessage, new()
|
||||||
|
{
|
||||||
|
var message = new T();
|
||||||
|
if (message.Deserialize(reader, ref context))
|
||||||
|
{
|
||||||
|
for (var hookIdx = 0; hookIdx < system.m_Hooks.Count; ++hookIdx)
|
||||||
|
{
|
||||||
|
system.m_Hooks[hookIdx].OnBeforeHandleMessage(ref message, ref context);
|
||||||
|
}
|
||||||
|
|
||||||
|
message.Handle(ref context);
|
||||||
|
|
||||||
|
for (var hookIdx = 0; hookIdx < system.m_Hooks.Count; ++hookIdx)
|
||||||
|
{
|
||||||
|
system.m_Hooks[hookIdx].OnAfterHandleMessage(ref message, ref context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool CanSend(ulong clientId, Type messageType, NetworkDelivery delivery)
|
private bool CanSend(ulong clientId, Type messageType, NetworkDelivery delivery)
|
||||||
{
|
{
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
@@ -306,7 +352,7 @@ namespace Unity.Netcode
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<TMessageType, TClientIdListType>(in TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds)
|
internal int SendMessage<TMessageType, TClientIdListType>(ref TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds)
|
||||||
where TMessageType : INetworkMessage
|
where TMessageType : INetworkMessage
|
||||||
where TClientIdListType : IReadOnlyList<ulong>
|
where TClientIdListType : IReadOnlyList<ulong>
|
||||||
{
|
{
|
||||||
@@ -316,64 +362,81 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? FRAGMENTED_MESSAGE_MAX_SIZE : NON_FRAGMENTED_MESSAGE_MAX_SIZE;
|
var maxSize = delivery == NetworkDelivery.ReliableFragmentedSequenced ? FRAGMENTED_MESSAGE_MAX_SIZE : NON_FRAGMENTED_MESSAGE_MAX_SIZE;
|
||||||
var tmpSerializer = new FastBufferWriter(NON_FRAGMENTED_MESSAGE_MAX_SIZE - FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp, maxSize - FastBufferWriter.GetWriteSize<MessageHeader>());
|
|
||||||
using (tmpSerializer)
|
using var tmpSerializer = new FastBufferWriter(NON_FRAGMENTED_MESSAGE_MAX_SIZE - FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp, maxSize - FastBufferWriter.GetWriteSize<MessageHeader>());
|
||||||
|
|
||||||
|
message.Serialize(tmpSerializer);
|
||||||
|
|
||||||
|
return SendPreSerializedMessage(tmpSerializer, maxSize, ref message, delivery, clientIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter tmpSerializer, int maxSize, ref TMessageType message, NetworkDelivery delivery, in IReadOnlyList<ulong> clientIds)
|
||||||
|
where TMessageType : INetworkMessage
|
||||||
|
{
|
||||||
|
using var headerSerializer = new FastBufferWriter(FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp);
|
||||||
|
|
||||||
|
var header = new MessageHeader
|
||||||
{
|
{
|
||||||
message.Serialize(tmpSerializer);
|
MessageSize = (uint)tmpSerializer.Length,
|
||||||
|
MessageType = m_MessageTypes[typeof(TMessageType)],
|
||||||
|
};
|
||||||
|
BytePacker.WriteValueBitPacked(headerSerializer, header.MessageType);
|
||||||
|
BytePacker.WriteValueBitPacked(headerSerializer, header.MessageSize);
|
||||||
|
|
||||||
for (var i = 0; i < clientIds.Count; ++i)
|
for (var i = 0; i < clientIds.Count; ++i)
|
||||||
|
{
|
||||||
|
var clientId = clientIds[i];
|
||||||
|
|
||||||
|
if (!CanSend(clientId, typeof(TMessageType), delivery))
|
||||||
{
|
{
|
||||||
var clientId = clientIds[i];
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CanSend(clientId, typeof(TMessageType), delivery))
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
continue;
|
m_Hooks[hookIdx].OnBeforeSendMessage(clientId, ref message, delivery);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
var sendQueueItem = m_SendQueues[clientId];
|
||||||
{
|
if (sendQueueItem.Length == 0)
|
||||||
m_Hooks[hookIdx].OnBeforeSendMessage(clientId, typeof(TMessageType), delivery);
|
{
|
||||||
}
|
sendQueueItem.Add(new SendQueueItem(delivery, NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.TempJob,
|
||||||
|
maxSize));
|
||||||
var sendQueueItem = m_SendQueues[clientId];
|
sendQueueItem.ElementAt(0).Writer.Seek(sizeof(BatchHeader));
|
||||||
if (sendQueueItem.Length == 0)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ref var lastQueueItem = ref sendQueueItem.ElementAt(sendQueueItem.Length - 1);
|
||||||
|
if (lastQueueItem.NetworkDelivery != delivery ||
|
||||||
|
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, NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.TempJob,
|
||||||
maxSize));
|
maxSize));
|
||||||
sendQueueItem.GetUnsafeList()->ElementAt(0).Writer.Seek(sizeof(BatchHeader));
|
sendQueueItem.ElementAt(sendQueueItem.Length - 1).Writer.Seek(sizeof(BatchHeader));
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ref var lastQueueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1);
|
|
||||||
if (lastQueueItem.NetworkDelivery != delivery ||
|
|
||||||
lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position
|
|
||||||
< tmpSerializer.Length + FastBufferWriter.GetWriteSize<MessageHeader>())
|
|
||||||
{
|
|
||||||
sendQueueItem.Add(new SendQueueItem(delivery, NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.TempJob,
|
|
||||||
maxSize));
|
|
||||||
sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1).Writer.Seek(sizeof(BatchHeader));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ref var writeQueueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1);
|
|
||||||
writeQueueItem.Writer.TryBeginWrite(tmpSerializer.Length + FastBufferWriter.GetWriteSize<MessageHeader>());
|
|
||||||
var header = new MessageHeader
|
|
||||||
{
|
|
||||||
MessageSize = (ushort)tmpSerializer.Length,
|
|
||||||
MessageType = m_MessageTypes[typeof(TMessageType)],
|
|
||||||
};
|
|
||||||
|
|
||||||
writeQueueItem.Writer.WriteValue(header);
|
|
||||||
writeQueueItem.Writer.WriteBytes(tmpSerializer.GetUnsafePtr(), tmpSerializer.Length);
|
|
||||||
writeQueueItem.BatchHeader.BatchSize++;
|
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
|
||||||
{
|
|
||||||
m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(TMessageType), delivery, tmpSerializer.Length + FastBufferWriter.GetWriteSize<MessageHeader>());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tmpSerializer.Length + FastBufferWriter.GetWriteSize<MessageHeader>();
|
ref var writeQueueItem = ref sendQueueItem.ElementAt(sendQueueItem.Length - 1);
|
||||||
|
writeQueueItem.Writer.TryBeginWrite(tmpSerializer.Length + headerSerializer.Length);
|
||||||
|
|
||||||
|
writeQueueItem.Writer.WriteBytes(headerSerializer.GetUnsafePtr(), headerSerializer.Length);
|
||||||
|
writeQueueItem.Writer.WriteBytes(tmpSerializer.GetUnsafePtr(), tmpSerializer.Length);
|
||||||
|
writeQueueItem.BatchHeader.BatchSize++;
|
||||||
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
|
{
|
||||||
|
m_Hooks[hookIdx].OnAfterSendMessage(clientId, ref message, delivery, tmpSerializer.Length + headerSerializer.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tmpSerializer.Length + headerSerializer.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter tmpSerializer, int maxSize, ref TMessageType message, NetworkDelivery delivery, ulong clientId)
|
||||||
|
where TMessageType : INetworkMessage
|
||||||
|
{
|
||||||
|
ulong* clientIds = stackalloc ulong[] { clientId };
|
||||||
|
return SendPreSerializedMessage(tmpSerializer, maxSize, ref message, delivery, new PointerListWrapper<ulong>(clientIds, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct PointerListWrapper<T> : IReadOnlyList<T>
|
private struct PointerListWrapper<T> : IReadOnlyList<T>
|
||||||
@@ -411,24 +474,24 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<T>(in T message, NetworkDelivery delivery,
|
internal unsafe int SendMessage<T>(ref T message, NetworkDelivery delivery,
|
||||||
ulong* clientIds, int numClientIds)
|
ulong* clientIds, int numClientIds)
|
||||||
where T : INetworkMessage
|
where T : INetworkMessage
|
||||||
{
|
{
|
||||||
return SendMessage(message, delivery, new PointerListWrapper<ulong>(clientIds, numClientIds));
|
return SendMessage(ref message, delivery, new PointerListWrapper<ulong>(clientIds, numClientIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<T>(in T message, NetworkDelivery delivery, ulong clientId)
|
internal unsafe int SendMessage<T>(ref T message, NetworkDelivery delivery, ulong clientId)
|
||||||
where T : INetworkMessage
|
where T : INetworkMessage
|
||||||
{
|
{
|
||||||
ulong* clientIds = stackalloc ulong[] { clientId };
|
ulong* clientIds = stackalloc ulong[] { clientId };
|
||||||
return SendMessage(message, delivery, new PointerListWrapper<ulong>(clientIds, 1));
|
return SendMessage(ref message, delivery, new PointerListWrapper<ulong>(clientIds, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<T>(in T message, NetworkDelivery delivery, in NativeArray<ulong> clientIds)
|
internal unsafe int SendMessage<T>(ref T message, NetworkDelivery delivery, in NativeArray<ulong> clientIds)
|
||||||
where T : INetworkMessage
|
where T : INetworkMessage
|
||||||
{
|
{
|
||||||
return SendMessage(message, delivery, new PointerListWrapper<ulong>((ulong*)clientIds.GetUnsafePtr(), clientIds.Length));
|
return SendMessage(ref message, delivery, new PointerListWrapper<ulong>((ulong*)clientIds.GetUnsafePtr(), clientIds.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe void ProcessSendQueues()
|
internal unsafe void ProcessSendQueues()
|
||||||
@@ -439,7 +502,7 @@ namespace Unity.Netcode
|
|||||||
var sendQueueItem = kvp.Value;
|
var sendQueueItem = kvp.Value;
|
||||||
for (var i = 0; i < sendQueueItem.Length; ++i)
|
for (var i = 0; i < sendQueueItem.Length; ++i)
|
||||||
{
|
{
|
||||||
ref var queueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(i);
|
ref var queueItem = ref sendQueueItem.ElementAt(i);
|
||||||
if (queueItem.BatchHeader.BatchSize == 0)
|
if (queueItem.BatchHeader.BatchSize == 0)
|
||||||
{
|
{
|
||||||
queueItem.Writer.Dispose();
|
queueItem.Writer.Dispose();
|
||||||
@@ -461,16 +524,16 @@ namespace Unity.Netcode
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_MessageSender.Send(clientId, queueItem.NetworkDelivery, queueItem.Writer);
|
m_MessageSender.Send(clientId, queueItem.NetworkDelivery, queueItem.Writer);
|
||||||
|
|
||||||
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
|
{
|
||||||
|
m_Hooks[hookIdx].OnAfterSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
queueItem.Writer.Dispose();
|
queueItem.Writer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
|
||||||
{
|
|
||||||
m_Hooks[hookIdx].OnAfterSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sendQueueItem.Clear();
|
sendQueueItem.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,5 +25,15 @@ namespace Unity.Netcode
|
|||||||
/// The header data that was sent with the message
|
/// The header data that was sent with the message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MessageHeader Header;
|
public MessageHeader Header;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The actual serialized size of the header when packed into the buffer
|
||||||
|
/// </summary>
|
||||||
|
public int SerializedHeaderSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the message in the buffer, header excluded
|
||||||
|
/// </summary>
|
||||||
|
public uint MessageSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,12 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
void TrackSceneEventReceived(ulong senderClientId, uint sceneEventType, string sceneName, long bytesCount);
|
void TrackSceneEventReceived(ulong senderClientId, uint sceneEventType, string sceneName, long bytesCount);
|
||||||
|
|
||||||
|
void TrackPacketSent(uint packetCount);
|
||||||
|
|
||||||
|
void TrackPacketReceived(uint packetCount);
|
||||||
|
|
||||||
|
void TrackRttToServer(int rtt);
|
||||||
|
|
||||||
void DispatchFrame();
|
void DispatchFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,13 @@ namespace Unity.Netcode
|
|||||||
m_NetworkManager = networkManager;
|
m_NetworkManager = networkManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
|
||||||
public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery, int messageSizeBytes)
|
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
|
||||||
{
|
{
|
||||||
m_NetworkManager.NetworkMetrics.TrackNetworkMessageSent(clientId, messageType.Name, messageSizeBytes);
|
m_NetworkManager.NetworkMetrics.TrackNetworkMessageSent(clientId, typeof(T).Name, messageSizeBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
||||||
@@ -57,5 +56,15 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
// TODO: Per-message metrics recording moved here
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
// TODO: Per-message metrics recording moved here
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using Unity.Multiplayer.Tools;
|
using Unity.Multiplayer.Tools;
|
||||||
using Unity.Multiplayer.Tools.MetricTypes;
|
using Unity.Multiplayer.Tools.MetricTypes;
|
||||||
using Unity.Multiplayer.Tools.NetStats;
|
using Unity.Multiplayer.Tools.NetStats;
|
||||||
|
using Unity.Profiling;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
@@ -14,6 +15,8 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
static Dictionary<uint, string> s_SceneEventTypeNames;
|
static Dictionary<uint, string> s_SceneEventTypeNames;
|
||||||
|
|
||||||
|
static ProfilerMarker s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame");
|
||||||
|
|
||||||
static NetworkMetrics()
|
static NetworkMetrics()
|
||||||
{
|
{
|
||||||
s_SceneEventTypeNames = new Dictionary<uint, string>();
|
s_SceneEventTypeNames = new Dictionary<uint, string>();
|
||||||
@@ -63,6 +66,21 @@ namespace Unity.Netcode
|
|||||||
private readonly EventMetric<SceneEventMetric> m_SceneEventSentEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventSent.Id);
|
private readonly EventMetric<SceneEventMetric> m_SceneEventSentEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventSent.Id);
|
||||||
private readonly EventMetric<SceneEventMetric> m_SceneEventReceivedEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventReceived.Id);
|
private readonly EventMetric<SceneEventMetric> m_SceneEventReceivedEvent = new EventMetric<SceneEventMetric>(NetworkMetricTypes.SceneEventReceived.Id);
|
||||||
|
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
||||||
|
private readonly Counter m_PacketSentCounter = new Counter(NetworkMetricTypes.PacketsSent.Id)
|
||||||
|
{
|
||||||
|
ShouldResetOnDispatch = true,
|
||||||
|
};
|
||||||
|
private readonly Counter m_PacketReceivedCounter = new Counter(NetworkMetricTypes.PacketsReceived.Id)
|
||||||
|
{
|
||||||
|
ShouldResetOnDispatch = true,
|
||||||
|
};
|
||||||
|
private readonly Gauge m_RttToServerGauge = new Gauge(NetworkMetricTypes.RttToServer.Id)
|
||||||
|
{
|
||||||
|
ShouldResetOnDispatch = true,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
private ulong m_NumberOfMetricsThisFrame;
|
private ulong m_NumberOfMetricsThisFrame;
|
||||||
|
|
||||||
public NetworkMetrics()
|
public NetworkMetrics()
|
||||||
@@ -79,6 +97,10 @@ namespace Unity.Netcode
|
|||||||
.WithMetricEvents(m_RpcSentEvent, m_RpcReceivedEvent)
|
.WithMetricEvents(m_RpcSentEvent, m_RpcReceivedEvent)
|
||||||
.WithMetricEvents(m_ServerLogSentEvent, m_ServerLogReceivedEvent)
|
.WithMetricEvents(m_ServerLogSentEvent, m_ServerLogReceivedEvent)
|
||||||
.WithMetricEvents(m_SceneEventSentEvent, m_SceneEventReceivedEvent)
|
.WithMetricEvents(m_SceneEventSentEvent, m_SceneEventReceivedEvent)
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
||||||
|
.WithCounters(m_PacketSentCounter, m_PacketReceivedCounter)
|
||||||
|
.WithGauges(m_RttToServerGauge)
|
||||||
|
#endif
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Dispatcher.RegisterObserver(NetcodeObserver.Observer);
|
Dispatcher.RegisterObserver(NetcodeObserver.Observer);
|
||||||
@@ -404,9 +426,49 @@ namespace Unity.Netcode
|
|||||||
IncrementMetricCount();
|
IncrementMetricCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TrackPacketSent(uint packetCount)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PacketSentCounter.Increment(packetCount);
|
||||||
|
IncrementMetricCount();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TrackPacketReceived(uint packetCount)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PacketReceivedCounter.Increment(packetCount);
|
||||||
|
IncrementMetricCount();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TrackRttToServer(int rtt)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_4
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_RttToServerGauge.Set(rtt);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public void DispatchFrame()
|
public void DispatchFrame()
|
||||||
{
|
{
|
||||||
|
s_FrameDispatch.Begin();
|
||||||
Dispatcher.Dispatch();
|
Dispatcher.Dispatch();
|
||||||
|
s_FrameDispatch.End();
|
||||||
m_NumberOfMetricsThisFrame = 0;
|
m_NumberOfMetricsThisFrame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +483,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class NetcodeObserver
|
internal class NetcodeObserver
|
||||||
{
|
{
|
||||||
public static IMetricObserver Observer { get; } = MetricObserverFactory.Construct();
|
public static IMetricObserver Observer { get; } = MetricObserverFactory.Construct();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,6 +137,18 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TrackPacketSent(uint packetCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TrackPacketReceived(uint packetCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TrackRttToServer(int rtt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void DispatchFrame()
|
public void DispatchFrame()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
public static class StreamExtensions
|
|
||||||
{
|
|
||||||
public static long SafeGetLengthOrDefault(this Stream stream)
|
|
||||||
{
|
|
||||||
return stream.CanSeek ? stream.Length : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -90,18 +90,18 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
case NetworkListEvent<T>.EventType.Add:
|
case NetworkListEvent<T>.EventType.Add:
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(m_DirtyEvents[i].Value);
|
NetworkVariable<T>.Write(writer, m_DirtyEvents[i].Value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NetworkListEvent<T>.EventType.Insert:
|
case NetworkListEvent<T>.EventType.Insert:
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(m_DirtyEvents[i].Index);
|
writer.WriteValueSafe(m_DirtyEvents[i].Index);
|
||||||
writer.WriteValueSafe(m_DirtyEvents[i].Value);
|
NetworkVariable<T>.Write(writer, m_DirtyEvents[i].Value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NetworkListEvent<T>.EventType.Remove:
|
case NetworkListEvent<T>.EventType.Remove:
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(m_DirtyEvents[i].Value);
|
NetworkVariable<T>.Write(writer, m_DirtyEvents[i].Value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NetworkListEvent<T>.EventType.RemoveAt:
|
case NetworkListEvent<T>.EventType.RemoveAt:
|
||||||
@@ -112,7 +112,7 @@ namespace Unity.Netcode
|
|||||||
case NetworkListEvent<T>.EventType.Value:
|
case NetworkListEvent<T>.EventType.Value:
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(m_DirtyEvents[i].Index);
|
writer.WriteValueSafe(m_DirtyEvents[i].Index);
|
||||||
writer.WriteValueSafe(m_DirtyEvents[i].Value);
|
NetworkVariable<T>.Write(writer, m_DirtyEvents[i].Value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NetworkListEvent<T>.EventType.Clear:
|
case NetworkListEvent<T>.EventType.Clear:
|
||||||
@@ -130,7 +130,7 @@ namespace Unity.Netcode
|
|||||||
writer.WriteValueSafe((ushort)m_List.Length);
|
writer.WriteValueSafe((ushort)m_List.Length);
|
||||||
for (int i = 0; i < m_List.Length; i++)
|
for (int i = 0; i < m_List.Length; i++)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(m_List[i]);
|
NetworkVariable<T>.Write(writer, m_List[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ namespace Unity.Netcode
|
|||||||
reader.ReadValueSafe(out ushort count);
|
reader.ReadValueSafe(out ushort count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out T value);
|
NetworkVariable<T>.Read(reader, out T value);
|
||||||
m_List.Add(value);
|
m_List.Add(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
case NetworkListEvent<T>.EventType.Add:
|
case NetworkListEvent<T>.EventType.Add:
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out T value);
|
NetworkVariable<T>.Read(reader, out T value);
|
||||||
m_List.Add(value);
|
m_List.Add(value);
|
||||||
|
|
||||||
if (OnListChanged != null)
|
if (OnListChanged != null)
|
||||||
@@ -184,7 +184,7 @@ namespace Unity.Netcode
|
|||||||
case NetworkListEvent<T>.EventType.Insert:
|
case NetworkListEvent<T>.EventType.Insert:
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out int index);
|
reader.ReadValueSafe(out int index);
|
||||||
reader.ReadValueSafe(out T value);
|
NetworkVariable<T>.Read(reader, out T value);
|
||||||
m_List.InsertRangeWithBeginEnd(index, index + 1);
|
m_List.InsertRangeWithBeginEnd(index, index + 1);
|
||||||
m_List[index] = value;
|
m_List[index] = value;
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ namespace Unity.Netcode
|
|||||||
break;
|
break;
|
||||||
case NetworkListEvent<T>.EventType.Remove:
|
case NetworkListEvent<T>.EventType.Remove:
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out T value);
|
NetworkVariable<T>.Read(reader, out T value);
|
||||||
int index = m_List.IndexOf(value);
|
int index = m_List.IndexOf(value);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
@@ -271,19 +271,23 @@ namespace Unity.Netcode
|
|||||||
case NetworkListEvent<T>.EventType.Value:
|
case NetworkListEvent<T>.EventType.Value:
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out int index);
|
reader.ReadValueSafe(out int index);
|
||||||
reader.ReadValueSafe(out T value);
|
NetworkVariable<T>.Read(reader, out T value);
|
||||||
if (index < m_List.Length)
|
if (index >= m_List.Length)
|
||||||
{
|
{
|
||||||
m_List[index] = value;
|
throw new Exception("Shouldn't be here, index is higher than list length");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var previousValue = m_List[index];
|
||||||
|
m_List[index] = value;
|
||||||
|
|
||||||
if (OnListChanged != null)
|
if (OnListChanged != null)
|
||||||
{
|
{
|
||||||
OnListChanged(new NetworkListEvent<T>
|
OnListChanged(new NetworkListEvent<T>
|
||||||
{
|
{
|
||||||
Type = eventType,
|
Type = eventType,
|
||||||
Index = index,
|
Index = index,
|
||||||
Value = value
|
Value = value,
|
||||||
|
PreviousValue = previousValue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +297,8 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
Type = eventType,
|
Type = eventType,
|
||||||
Index = index,
|
Index = index,
|
||||||
Value = value
|
Value = value,
|
||||||
|
PreviousValue = previousValue
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,7 +373,7 @@ namespace Unity.Netcode
|
|||||||
public bool Contains(T item)
|
public bool Contains(T item)
|
||||||
{
|
{
|
||||||
int index = NativeArrayExtensions.IndexOf(m_List, item);
|
int index = NativeArrayExtensions.IndexOf(m_List, item);
|
||||||
return index == -1;
|
return index != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -528,6 +533,11 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public T Value;
|
public T Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The previous value when "Value" has changed, if available.
|
||||||
|
/// </summary>
|
||||||
|
public T PreviousValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// the index changed, added or removed if available
|
/// the index changed, added or removed if available
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -9,6 +9,55 @@ namespace Unity.Netcode
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class NetworkVariable<T> : NetworkVariableBase where T : unmanaged
|
public class NetworkVariable<T> : NetworkVariableBase where T : unmanaged
|
||||||
{
|
{
|
||||||
|
// Functions that know how to serialize INetworkSerializable
|
||||||
|
internal static void WriteNetworkSerializable<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||||
|
where TForMethod : INetworkSerializable, new()
|
||||||
|
{
|
||||||
|
writer.WriteNetworkSerializable(value);
|
||||||
|
}
|
||||||
|
internal static void ReadNetworkSerializable<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||||
|
where TForMethod : INetworkSerializable, new()
|
||||||
|
{
|
||||||
|
reader.ReadNetworkSerializable(out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions that serialize other types
|
||||||
|
private static void WriteValue<TForMethod>(FastBufferWriter writer, in TForMethod value) where TForMethod : unmanaged
|
||||||
|
{
|
||||||
|
writer.WriteValueSafe(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReadValue<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||||
|
where TForMethod : unmanaged
|
||||||
|
{
|
||||||
|
reader.ReadValueSafe(out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal delegate void WriteDelegate<TForMethod>(FastBufferWriter writer, in TForMethod value);
|
||||||
|
|
||||||
|
internal delegate void ReadDelegate<TForMethod>(FastBufferReader reader, out TForMethod value);
|
||||||
|
|
||||||
|
// These static delegates provide the right implementation for writing and reading a particular network variable
|
||||||
|
// type.
|
||||||
|
//
|
||||||
|
// For most types, these default to WriteValue() and ReadValue(), which perform simple memcpy operations.
|
||||||
|
//
|
||||||
|
// INetworkSerializableILPP will generate startup code that will set it to WriteNetworkSerializable()
|
||||||
|
// and ReadNetworkSerializable() for INetworkSerializable types, which will call NetworkSerialize().
|
||||||
|
//
|
||||||
|
// In the future we may be able to use this to provide packing implementations for floats and integers to
|
||||||
|
// optimize bandwidth usage.
|
||||||
|
//
|
||||||
|
// The reason this is done is to avoid runtime reflection and boxing in NetworkVariable - without this,
|
||||||
|
// NetworkVariable would need to do a `var is INetworkSerializable` check, and then cast to INetworkSerializable,
|
||||||
|
// *both* of which would cause a boxing allocation. Alternatively, NetworkVariable could have been split into
|
||||||
|
// NetworkVariable and NetworkSerializableVariable or something like that, which would have caused a poor
|
||||||
|
// user experience and an API that's easier to get wrong than right. This is a bit ugly on the implementation
|
||||||
|
// side, but it gets the best achievable user experience and performance.
|
||||||
|
internal static WriteDelegate<T> Write = WriteValue;
|
||||||
|
internal static ReadDelegate<T> Read = ReadValue;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate type for value changed event
|
/// Delegate type for value changed event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -106,7 +155,7 @@ namespace Unity.Netcode
|
|||||||
public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)
|
public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)
|
||||||
{
|
{
|
||||||
T previousValue = m_InternalValue;
|
T previousValue = m_InternalValue;
|
||||||
reader.ReadValueSafe(out m_InternalValue);
|
Read(reader, out m_InternalValue);
|
||||||
|
|
||||||
if (keepDirtyDelta)
|
if (keepDirtyDelta)
|
||||||
{
|
{
|
||||||
@@ -119,13 +168,13 @@ namespace Unity.Netcode
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ReadField(FastBufferReader reader)
|
public override void ReadField(FastBufferReader reader)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out m_InternalValue);
|
Read(reader, out m_InternalValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void WriteField(FastBufferWriter writer)
|
public override void WriteField(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(m_InternalValue);
|
Write(writer, m_InternalValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The delivery type (QoS) to send data with
|
/// The delivery type (QoS) to send data with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal const NetworkDelivery Delivery = NetworkDelivery.ReliableSequenced;
|
internal const NetworkDelivery Delivery = NetworkDelivery.ReliableFragmentedSequenced;
|
||||||
|
|
||||||
private protected NetworkBehaviour m_NetworkBehaviour;
|
private protected NetworkBehaviour m_NetworkBehaviour;
|
||||||
|
|
||||||
|
|||||||
22
Runtime/NetworkVariable/NetworkVariableHelper.cs
Normal file
22
Runtime/NetworkVariable/NetworkVariableHelper.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
public class NetworkVariableHelper
|
||||||
|
{
|
||||||
|
// This is called by ILPP during module initialization for all unmanaged INetworkSerializable types
|
||||||
|
// This sets up NetworkVariable so that it properly calls NetworkSerialize() when wrapping an INetworkSerializable value
|
||||||
|
//
|
||||||
|
// The reason this is done is to avoid runtime reflection and boxing in NetworkVariable - without this,
|
||||||
|
// NetworkVariable would need to do a `var is INetworkSerializable` check, and then cast to INetworkSerializable,
|
||||||
|
// *both* of which would cause a boxing allocation. Alternatively, NetworkVariable could have been split into
|
||||||
|
// NetworkVariable and NetworkSerializableVariable or something like that, which would have caused a poor
|
||||||
|
// user experience and an API that's easier to get wrong than right. This is a bit ugly on the implementation
|
||||||
|
// side, but it gets the best achievable user experience and performance.
|
||||||
|
//
|
||||||
|
// RuntimeAccessModifiersILPP will make this `public`
|
||||||
|
internal static void InitializeDelegates<T>() where T : unmanaged, INetworkSerializable
|
||||||
|
{
|
||||||
|
NetworkVariable<T>.Write = NetworkVariable<T>.WriteNetworkSerializable;
|
||||||
|
NetworkVariable<T>.Read = NetworkVariable<T>.ReadNetworkSerializable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 61dd9b1558f6d7c46ad323b2c2c03c29
|
guid: e54b65208bd3bbe4eaf62ca0384ae21f
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -37,14 +37,14 @@ namespace Unity.Netcode
|
|||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery)
|
public void OnBeforeSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery) where T : INetworkMessage
|
||||||
{
|
{
|
||||||
GetSenderProfilerMarker(messageType).Begin();
|
GetSenderProfilerMarker(typeof(T)).Begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnAfterSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery, int messageSizeBytes)
|
public void OnAfterSendMessage<T>(ulong clientId, ref T message, NetworkDelivery delivery, int messageSizeBytes) where T : INetworkMessage
|
||||||
{
|
{
|
||||||
GetSenderProfilerMarker(messageType).End();
|
GetSenderProfilerMarker(typeof(T)).End();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
public void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes)
|
||||||
@@ -86,5 +86,15 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnBeforeHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAfterHandleMessage<T>(ref T message, ref NetworkContext context) where T : INetworkMessage
|
||||||
|
{
|
||||||
|
// nop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal static class TypeExtensions
|
|
||||||
{
|
|
||||||
internal static bool HasInterface(this Type type, Type interfaceType)
|
|
||||||
{
|
|
||||||
var ifaces = type.GetInterfaces();
|
|
||||||
for (int i = 0; i < ifaces.Length; i++)
|
|
||||||
{
|
|
||||||
if (ifaces[i] == interfaceType)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool IsNullable(this Type type)
|
|
||||||
{
|
|
||||||
if (!type.IsValueType)
|
|
||||||
{
|
|
||||||
return true; // ref-type
|
|
||||||
}
|
|
||||||
|
|
||||||
return Nullable.GetUnderlyingType(type) != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
28
Runtime/SceneManagement/ISceneManagerHandler.cs
Normal file
28
Runtime/SceneManagement/ISceneManagerHandler.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace Unity.Netcode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to override the LoadSceneAsync and UnloadSceneAsync methods called
|
||||||
|
/// within the NetworkSceneManager.
|
||||||
|
/// </summary>
|
||||||
|
internal interface ISceneManagerHandler
|
||||||
|
{
|
||||||
|
// Generic action to call when a scene is finished loading/unloading
|
||||||
|
struct SceneEventAction
|
||||||
|
{
|
||||||
|
internal uint SceneEventId;
|
||||||
|
internal Action<uint> EventAction;
|
||||||
|
internal void Invoke()
|
||||||
|
{
|
||||||
|
EventAction.Invoke(SceneEventId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventAction sceneEventAction);
|
||||||
|
|
||||||
|
AsyncOperation UnloadSceneAsync(Scene scene, SceneEventAction sceneEventAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 3e168a2bc1a1e2642af0369780fb560c
|
guid: de907a9fb8151e240800dbcc97f8e745
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -139,13 +139,7 @@ namespace Unity.Netcode
|
|||||||
/// Used to detect if a scene event is underway
|
/// Used to detect if a scene event is underway
|
||||||
/// Only 1 scene event can occur on the server at a time for now.
|
/// Only 1 scene event can occur on the server at a time for now.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static bool s_IsSceneEventActive = false;
|
private bool m_IsSceneEventActive = false;
|
||||||
|
|
||||||
// TODO: Remove `m_IsRunningUnitTest` entirely after we switch to multi-process testing
|
|
||||||
// In MultiInstance tests, we cannot allow clients to load additional scenes as they're sharing the same scene space / Unity instance.
|
|
||||||
#if UNITY_INCLUDE_TESTS
|
|
||||||
private readonly bool m_IsRunningUnitTest = SceneManager.GetActiveScene().name.StartsWith("InitTestScene");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The delegate callback definition for scene event notifications.<br/>
|
/// The delegate callback definition for scene event notifications.<br/>
|
||||||
@@ -324,6 +318,31 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public VerifySceneBeforeLoadingDelegateHandler VerifySceneBeforeLoading;
|
public VerifySceneBeforeLoadingDelegateHandler VerifySceneBeforeLoading;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Proof of concept: Interface version that allows for the decoupling from
|
||||||
|
/// the SceneManager's Load and Unload methods for testing purposes (potentially other future usage)
|
||||||
|
/// </summary>
|
||||||
|
private class DefaultSceneManagerHandler : ISceneManagerHandler
|
||||||
|
{
|
||||||
|
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, ISceneManagerHandler.SceneEventAction sceneEventAction)
|
||||||
|
{
|
||||||
|
var operation = SceneManager.LoadSceneAsync(sceneName, loadSceneMode);
|
||||||
|
operation.completed += new Action<AsyncOperation>(asyncOp2 => { sceneEventAction.Invoke(); });
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncOperation UnloadSceneAsync(Scene scene, ISceneManagerHandler.SceneEventAction sceneEventAction)
|
||||||
|
{
|
||||||
|
var operation = SceneManager.UnloadSceneAsync(scene);
|
||||||
|
operation.completed += new Action<AsyncOperation>(asyncOp2 => { sceneEventAction.Invoke(); });
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ISceneManagerHandler SceneManagerHandler = new DefaultSceneManagerHandler();
|
||||||
|
/// End of Proof of Concept
|
||||||
|
|
||||||
|
|
||||||
internal readonly Dictionary<Guid, SceneEventProgress> SceneEventProgressTracking = new Dictionary<Guid, SceneEventProgress>();
|
internal readonly Dictionary<Guid, SceneEventProgress> SceneEventProgressTracking = new Dictionary<Guid, SceneEventProgress>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -565,20 +584,8 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
GenerateScenesInBuild();
|
GenerateScenesInBuild();
|
||||||
|
|
||||||
// If NetworkManager has this set to true, then we can get the DDOL (DontDestroyOnLoad) from its GaemObject
|
// Since NetworkManager is now always migrated to the DDOL we will use this to get the DDOL scene
|
||||||
if (networkManager.DontDestroy)
|
DontDestroyOnLoadScene = networkManager.gameObject.scene;
|
||||||
{
|
|
||||||
DontDestroyOnLoadScene = networkManager.gameObject.scene;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Otherwise, we have to create a GameObject and move it into the DDOL in order to
|
|
||||||
// register the DDOL scene handle with NetworkSceneManager
|
|
||||||
var myDDOLObject = new GameObject("DDOL-NWSM");
|
|
||||||
UnityEngine.Object.DontDestroyOnLoad(myDDOLObject);
|
|
||||||
DontDestroyOnLoadScene = myDDOLObject.scene;
|
|
||||||
UnityEngine.Object.Destroy(myDDOLObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerSceneHandleToClientSceneHandle.Add(DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene.handle);
|
ServerSceneHandleToClientSceneHandle.Add(DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene.handle);
|
||||||
ScenesLoaded.Add(DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene);
|
ScenesLoaded.Add(DontDestroyOnLoadScene.handle, DontDestroyOnLoadScene);
|
||||||
@@ -735,10 +742,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = SceneEventDataStore[sceneEventId]
|
EventData = SceneEventDataStore[sceneEventId]
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, k_DeliveryType, targetClientIds);
|
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, targetClientIds);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(
|
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(targetClientIds, (uint)SceneEventDataStore[sceneEventId].SceneEventType, SceneNameFromHash(SceneEventDataStore[sceneEventId].SceneHash), size);
|
||||||
targetClientIds, (uint)SceneEventDataStore[sceneEventId].SceneEventType, SceneNameFromHash(SceneEventDataStore[sceneEventId].SceneHash), size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -801,7 +807,7 @@ namespace Unity.Netcode
|
|||||||
private SceneEventProgress ValidateSceneEvent(string sceneName, bool isUnloading = false)
|
private SceneEventProgress ValidateSceneEvent(string sceneName, bool isUnloading = false)
|
||||||
{
|
{
|
||||||
// Return scene event already in progress if one is already in progress
|
// Return scene event already in progress if one is already in progress
|
||||||
if (s_IsSceneEventActive)
|
if (m_IsSceneEventActive)
|
||||||
{
|
{
|
||||||
return new SceneEventProgress(null, SceneEventProgressStatus.SceneEventInProgress);
|
return new SceneEventProgress(null, SceneEventProgressStatus.SceneEventInProgress);
|
||||||
}
|
}
|
||||||
@@ -830,7 +836,7 @@ namespace Unity.Netcode
|
|||||||
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_IsSceneEventActive = true;
|
m_IsSceneEventActive = true;
|
||||||
|
|
||||||
// Set our callback delegate handler for completion
|
// Set our callback delegate handler for completion
|
||||||
sceneEventProgress.OnComplete = OnSceneEventProgressCompleted;
|
sceneEventProgress.OnComplete = OnSceneEventProgressCompleted;
|
||||||
@@ -857,12 +863,12 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = sceneEventData
|
EventData = sceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, k_DeliveryType, m_NetworkManager.ConnectedClientsIds);
|
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, m_NetworkManager.ConnectedClientsIds);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(
|
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(
|
||||||
m_NetworkManager.ConnectedClientsIds,
|
m_NetworkManager.ConnectedClientsIds,
|
||||||
(uint)sceneEventProgress.SceneEventType,
|
(uint)sceneEventProgress.SceneEventType,
|
||||||
SceneNameFromHash(sceneEventProgress.SceneHash),
|
SceneNameFromHash(sceneEventProgress.SceneHash),
|
||||||
size);
|
size);
|
||||||
|
|
||||||
// Send a local notification to the server that all clients are done loading or unloading
|
// Send a local notification to the server that all clients are done loading or unloading
|
||||||
@@ -929,8 +935,9 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
ScenesLoaded.Remove(scene.handle);
|
ScenesLoaded.Remove(scene.handle);
|
||||||
|
|
||||||
AsyncOperation sceneUnload = SceneManager.UnloadSceneAsync(scene);
|
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(scene,
|
||||||
sceneUnload.completed += (AsyncOperation asyncOp2) => { OnSceneUnloaded(sceneEventData.SceneEventId); };
|
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventData.SceneEventId, EventAction = OnSceneUnloaded });
|
||||||
|
|
||||||
sceneEventProgress.SetSceneLoadOperation(sceneUnload);
|
sceneEventProgress.SetSceneLoadOperation(sceneUnload);
|
||||||
|
|
||||||
// Notify local server that a scene is going to be unloaded
|
// Notify local server that a scene is going to be unloaded
|
||||||
@@ -960,8 +967,10 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (!ServerSceneHandleToClientSceneHandle.ContainsKey(sceneEventData.SceneHandle))
|
if (!ServerSceneHandleToClientSceneHandle.ContainsKey(sceneEventData.SceneHandle))
|
||||||
{
|
{
|
||||||
throw new Exception($"Client failed to unload scene {sceneName} " +
|
Debug.Log($"Client failed to unload scene {sceneName} " +
|
||||||
$"because we are missing the client scene handle due to the server scene handle {sceneEventData.SceneHandle} not being found!");
|
$"because we are missing the client scene handle due to the server scene handle {sceneEventData.SceneHandle} not being found.");
|
||||||
|
EndSceneEvent(sceneEventId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sceneHandle = ServerSceneHandleToClientSceneHandle[sceneEventData.SceneHandle];
|
var sceneHandle = ServerSceneHandleToClientSceneHandle[sceneEventData.SceneHandle];
|
||||||
@@ -972,22 +981,11 @@ namespace Unity.Netcode
|
|||||||
throw new Exception($"Client failed to unload scene {sceneName} " +
|
throw new Exception($"Client failed to unload scene {sceneName} " +
|
||||||
$"because the client scene handle {sceneHandle} was not found in ScenesLoaded!");
|
$"because the client scene handle {sceneHandle} was not found in ScenesLoaded!");
|
||||||
}
|
}
|
||||||
s_IsSceneEventActive = true;
|
m_IsSceneEventActive = true;
|
||||||
var sceneUnload = (AsyncOperation)null;
|
|
||||||
#if UNITY_INCLUDE_TESTS
|
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(ScenesLoaded[sceneHandle],
|
||||||
if (m_IsRunningUnitTest)
|
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventData.SceneEventId, EventAction = OnSceneUnloaded });
|
||||||
{
|
|
||||||
sceneUnload = new AsyncOperation();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sceneUnload = SceneManager.UnloadSceneAsync(ScenesLoaded[sceneHandle]);
|
|
||||||
sceneUnload.completed += asyncOp2 => OnSceneUnloaded(sceneEventId);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
sceneUnload = SceneManager.UnloadSceneAsync(ScenesLoaded[sceneHandle]);
|
|
||||||
sceneUnload.completed += asyncOp2 => OnSceneUnloaded(sceneEventId);
|
|
||||||
#endif
|
|
||||||
ScenesLoaded.Remove(sceneHandle);
|
ScenesLoaded.Remove(sceneHandle);
|
||||||
|
|
||||||
// Remove our server to scene handle lookup
|
// Remove our server to scene handle lookup
|
||||||
@@ -1004,13 +1002,6 @@ namespace Unity.Netcode
|
|||||||
});
|
});
|
||||||
|
|
||||||
OnUnload?.Invoke(m_NetworkManager.LocalClientId, sceneName, sceneUnload);
|
OnUnload?.Invoke(m_NetworkManager.LocalClientId, sceneName, sceneUnload);
|
||||||
|
|
||||||
#if UNITY_INCLUDE_TESTS
|
|
||||||
if (m_IsRunningUnitTest)
|
|
||||||
{
|
|
||||||
OnSceneUnloaded(sceneEventId);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1028,8 +1019,8 @@ namespace Unity.Netcode
|
|||||||
// despawned that no longer exists
|
// despawned that no longer exists
|
||||||
SendSceneEventData(sceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray());
|
SendSceneEventData(sceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray());
|
||||||
|
|
||||||
//Second, server sets itself as having finished unloading
|
//Only if we are a host do we want register having loaded for the associated SceneEventProgress
|
||||||
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId))
|
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
@@ -1057,7 +1048,12 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
EndSceneEvent(sceneEventId);
|
EndSceneEvent(sceneEventId);
|
||||||
// This scene event is now considered "complete"
|
// This scene event is now considered "complete"
|
||||||
s_IsSceneEventActive = false;
|
m_IsSceneEventActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmptySceneUnloadedOperation(uint sceneEventId)
|
||||||
|
{
|
||||||
|
// Do nothing (this is a stub call since it is only used to flush all currently loaded scenes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1065,17 +1061,21 @@ namespace Unity.Netcode
|
|||||||
/// Since we assume a single mode loaded scene will be considered the "currently active scene",
|
/// Since we assume a single mode loaded scene will be considered the "currently active scene",
|
||||||
/// we only unload any additively loaded scenes.
|
/// we only unload any additively loaded scenes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal void UnloadAdditivelyLoadedScenes()
|
internal void UnloadAdditivelyLoadedScenes(uint sceneEventId)
|
||||||
{
|
{
|
||||||
// Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ).
|
// Unload all additive scenes while making sure we don't try to unload the base scene ( loaded in single mode ).
|
||||||
var currentActiveScene = SceneManager.GetActiveScene();
|
var currentActiveScene = SceneManager.GetActiveScene();
|
||||||
foreach (var keyHandleEntry in ScenesLoaded)
|
foreach (var keyHandleEntry in ScenesLoaded)
|
||||||
{
|
{
|
||||||
if (currentActiveScene.name != keyHandleEntry.Value.name)
|
// 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)
|
||||||
{
|
{
|
||||||
|
var sceneUnload = SceneManagerHandler.UnloadSceneAsync(keyHandleEntry.Value,
|
||||||
|
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = EmptySceneUnloadedOperation });
|
||||||
|
|
||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
{
|
{
|
||||||
AsyncOperation = SceneManager.UnloadSceneAsync(keyHandleEntry.Value),
|
AsyncOperation = sceneUnload,
|
||||||
SceneEventType = SceneEventType.Unload,
|
SceneEventType = SceneEventType.Unload,
|
||||||
SceneName = keyHandleEntry.Value.name,
|
SceneName = keyHandleEntry.Value.name,
|
||||||
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
|
||||||
@@ -1115,8 +1115,8 @@ namespace Unity.Netcode
|
|||||||
sceneEventData.LoadSceneMode = loadSceneMode;
|
sceneEventData.LoadSceneMode = loadSceneMode;
|
||||||
|
|
||||||
// This both checks to make sure the scene is valid and if not resets the active scene event
|
// This both checks to make sure the scene is valid and if not resets the active scene event
|
||||||
s_IsSceneEventActive = ValidateSceneBeforeLoading(sceneEventData.SceneHash, loadSceneMode);
|
m_IsSceneEventActive = ValidateSceneBeforeLoading(sceneEventData.SceneHash, loadSceneMode);
|
||||||
if (!s_IsSceneEventActive)
|
if (!m_IsSceneEventActive)
|
||||||
{
|
{
|
||||||
EndSceneEvent(sceneEventData.SceneEventId);
|
EndSceneEvent(sceneEventData.SceneEventId);
|
||||||
return SceneEventProgressStatus.SceneFailedVerification;
|
return SceneEventProgressStatus.SceneFailedVerification;
|
||||||
@@ -1131,12 +1131,13 @@ namespace Unity.Netcode
|
|||||||
MoveObjectsToDontDestroyOnLoad();
|
MoveObjectsToDontDestroyOnLoad();
|
||||||
|
|
||||||
// Now Unload all currently additively loaded scenes
|
// Now Unload all currently additively loaded scenes
|
||||||
UnloadAdditivelyLoadedScenes();
|
UnloadAdditivelyLoadedScenes(sceneEventData.SceneEventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now start loading the scene
|
// Now start loading the scene
|
||||||
AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode);
|
var sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, loadSceneMode,
|
||||||
sceneLoad.completed += (AsyncOperation asyncOp2) => { OnSceneLoaded(sceneEventData.SceneEventId, sceneName); };
|
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventData.SceneEventId, EventAction = OnSceneLoaded });
|
||||||
|
|
||||||
sceneEventProgress.SetSceneLoadOperation(sceneLoad);
|
sceneEventProgress.SetSceneLoadOperation(sceneLoad);
|
||||||
|
|
||||||
// Notify the local server that a scene loading event has begun
|
// Notify the local server that a scene loading event has begun
|
||||||
@@ -1172,44 +1173,13 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_INCLUDE_TESTS
|
|
||||||
if (m_IsRunningUnitTest)
|
|
||||||
{
|
|
||||||
// Send the loading message
|
|
||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
|
||||||
{
|
|
||||||
AsyncOperation = new AsyncOperation(),
|
|
||||||
SceneEventType = sceneEventData.SceneEventType,
|
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
|
||||||
SceneName = sceneName,
|
|
||||||
ClientId = m_NetworkManager.LocalClientId
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only for testing
|
|
||||||
OnLoad?.Invoke(m_NetworkManager.ServerClientId, sceneName, sceneEventData.LoadSceneMode, new AsyncOperation());
|
|
||||||
|
|
||||||
// Unit tests must mirror the server's scenes loaded dictionary, otherwise this portion will fail
|
|
||||||
if (ScenesLoaded.ContainsKey(sceneEventData.SceneHandle))
|
|
||||||
{
|
|
||||||
OnClientLoadedScene(sceneEventId, ScenesLoaded[sceneEventData.SceneHandle]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EndSceneEvent(sceneEventId);
|
|
||||||
throw new Exception($"Could not find the scene handle {sceneEventData.SceneHandle} for scene {sceneName} " +
|
|
||||||
$"during unit test. Did you forget to register this in the unit test?");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sceneEventData.LoadSceneMode == LoadSceneMode.Single)
|
if (sceneEventData.LoadSceneMode == LoadSceneMode.Single)
|
||||||
{
|
{
|
||||||
// Move ALL NetworkObjects to the temp scene
|
// Move ALL NetworkObjects to the temp scene
|
||||||
MoveObjectsToDontDestroyOnLoad();
|
MoveObjectsToDontDestroyOnLoad();
|
||||||
|
|
||||||
// Now Unload all currently additively loaded scenes
|
// Now Unload all currently additively loaded scenes
|
||||||
UnloadAdditivelyLoadedScenes();
|
UnloadAdditivelyLoadedScenes(sceneEventData.SceneEventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Condition: While a scene is asynchronously loaded in single loading scene mode, if any new NetworkObjects are spawned
|
// The Condition: While a scene is asynchronously loaded in single loading scene mode, if any new NetworkObjects are spawned
|
||||||
@@ -1217,13 +1187,14 @@ namespace Unity.Netcode
|
|||||||
// When it is set: Just before starting the asynchronous loading call
|
// When it is set: Just before starting the asynchronous loading call
|
||||||
// When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do
|
// When it is unset: After the scene has loaded, the PopulateScenePlacedObjects is called, and all NetworkObjects in the do
|
||||||
// not destroy temporary scene are moved into the active scene
|
// not destroy temporary scene are moved into the active scene
|
||||||
|
// TODO: When Snapshot scene spawning is enabled this needs to be removed.
|
||||||
if (sceneEventData.LoadSceneMode == LoadSceneMode.Single)
|
if (sceneEventData.LoadSceneMode == LoadSceneMode.Single)
|
||||||
{
|
{
|
||||||
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
IsSpawnedObjectsPendingInDontDestroyOnLoad = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sceneLoad = SceneManager.LoadSceneAsync(sceneName, sceneEventData.LoadSceneMode);
|
var sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, sceneEventData.LoadSceneMode,
|
||||||
sceneLoad.completed += asyncOp2 => OnSceneLoaded(sceneEventId, sceneName);
|
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = OnSceneLoaded });
|
||||||
|
|
||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
{
|
{
|
||||||
@@ -1242,10 +1213,10 @@ namespace Unity.Netcode
|
|||||||
/// Client and Server:
|
/// Client and Server:
|
||||||
/// Generic on scene loaded callback method to be called upon a scene loading
|
/// Generic on scene loaded callback method to be called upon a scene loading
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnSceneLoaded(uint sceneEventId, string sceneName)
|
private void OnSceneLoaded(uint sceneEventId)
|
||||||
{
|
{
|
||||||
var sceneEventData = SceneEventDataStore[sceneEventId];
|
var sceneEventData = SceneEventDataStore[sceneEventId];
|
||||||
var nextScene = GetAndAddNewlyLoadedSceneByName(sceneName);
|
var nextScene = GetAndAddNewlyLoadedSceneByName(SceneNameFromHash(sceneEventData.SceneHash));
|
||||||
if (!nextScene.isLoaded || !nextScene.IsValid())
|
if (!nextScene.isLoaded || !nextScene.IsValid())
|
||||||
{
|
{
|
||||||
throw new Exception($"Failed to find valid scene internal Unity.Netcode for {nameof(GameObject)}s error!");
|
throw new Exception($"Failed to find valid scene internal Unity.Netcode for {nameof(GameObject)}s error!");
|
||||||
@@ -1326,12 +1297,12 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = sceneEventData
|
EventData = sceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, k_DeliveryType, clientId);
|
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, clientId);
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)sceneEventData.SceneEventType, scene.name, size);
|
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)sceneEventData.SceneEventType, scene.name, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s_IsSceneEventActive = false;
|
m_IsSceneEventActive = false;
|
||||||
//First, notify local server that the scene was loaded
|
//First, notify local server that the scene was loaded
|
||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
{
|
{
|
||||||
@@ -1344,8 +1315,8 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
OnLoadComplete?.Invoke(m_NetworkManager.ServerClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
OnLoadComplete?.Invoke(m_NetworkManager.ServerClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
||||||
|
|
||||||
//Second, set the server as 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))
|
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
@@ -1363,7 +1334,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
sceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
sceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
||||||
s_IsSceneEventActive = false;
|
m_IsSceneEventActive = false;
|
||||||
|
|
||||||
// Notify local client that the scene was loaded
|
// Notify local client that the scene was loaded
|
||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
@@ -1433,9 +1404,8 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
EventData = sceneEventData
|
EventData = sceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, k_DeliveryType, clientId);
|
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, clientId);
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(
|
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(clientId, (uint)sceneEventData.SceneEventType, "", size);
|
||||||
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()
|
||||||
@@ -1486,6 +1456,10 @@ namespace Unity.Netcode
|
|||||||
ScenePlacedObjects.Clear();
|
ScenePlacedObjects.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store the sceneHandle and hash
|
||||||
|
sceneEventData.ClientSceneHandle = sceneHandle;
|
||||||
|
sceneEventData.ClientSceneHash = sceneHash;
|
||||||
|
|
||||||
var shouldPassThrough = false;
|
var shouldPassThrough = false;
|
||||||
var sceneLoad = (AsyncOperation)null;
|
var sceneLoad = (AsyncOperation)null;
|
||||||
|
|
||||||
@@ -1497,38 +1471,28 @@ namespace Unity.Netcode
|
|||||||
shouldPassThrough = true;
|
shouldPassThrough = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_INCLUDE_TESTS
|
|
||||||
if (m_IsRunningUnitTest)
|
|
||||||
{
|
|
||||||
// In unit tests, we don't allow clients to load additional scenes since
|
|
||||||
// MultiInstance unit tests share the same scene space.
|
|
||||||
shouldPassThrough = true;
|
|
||||||
sceneLoad = new AsyncOperation();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!shouldPassThrough)
|
if (!shouldPassThrough)
|
||||||
{
|
{
|
||||||
// If not, then load the scene
|
// If not, then load the scene
|
||||||
sceneLoad = SceneManager.LoadSceneAsync(sceneName, loadSceneMode);
|
sceneLoad = SceneManagerHandler.LoadSceneAsync(sceneName, loadSceneMode,
|
||||||
sceneLoad.completed += asyncOp2 => ClientLoadedSynchronization(sceneEventId, sceneHash, sceneHandle);
|
new ISceneManagerHandler.SceneEventAction() { SceneEventId = sceneEventId, EventAction = ClientLoadedSynchronization });
|
||||||
|
|
||||||
|
// Notify local client that a scene load has begun
|
||||||
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
|
{
|
||||||
|
AsyncOperation = sceneLoad,
|
||||||
|
SceneEventType = SceneEventType.Load,
|
||||||
|
LoadSceneMode = loadSceneMode,
|
||||||
|
SceneName = sceneName,
|
||||||
|
ClientId = m_NetworkManager.LocalClientId,
|
||||||
|
});
|
||||||
|
|
||||||
|
OnLoad?.Invoke(m_NetworkManager.LocalClientId, sceneName, loadSceneMode, sceneLoad);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Notify local client that a scene load has begun
|
|
||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
|
||||||
{
|
|
||||||
AsyncOperation = sceneLoad,
|
|
||||||
SceneEventType = SceneEventType.Load,
|
|
||||||
LoadSceneMode = loadSceneMode,
|
|
||||||
SceneName = sceneName,
|
|
||||||
ClientId = m_NetworkManager.LocalClientId,
|
|
||||||
});
|
|
||||||
|
|
||||||
OnLoad?.Invoke(m_NetworkManager.LocalClientId, sceneName, loadSceneMode, sceneLoad);
|
|
||||||
|
|
||||||
if (shouldPassThrough)
|
|
||||||
{
|
{
|
||||||
// If so, then pass through
|
// If so, then pass through
|
||||||
ClientLoadedSynchronization(sceneEventId, sceneHash, sceneHandle);
|
ClientLoadedSynchronization(sceneEventId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1537,10 +1501,10 @@ namespace Unity.Netcode
|
|||||||
/// This handles all of the in-scene and dynamically spawned NetworkObject synchronization
|
/// This handles all of the in-scene and dynamically spawned NetworkObject synchronization
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sceneIndex">Netcode scene index that was loaded</param>
|
/// <param name="sceneIndex">Netcode scene index that was loaded</param>
|
||||||
private void ClientLoadedSynchronization(uint sceneEventId, uint sceneHash, int sceneHandle)
|
private void ClientLoadedSynchronization(uint sceneEventId)
|
||||||
{
|
{
|
||||||
var sceneEventData = SceneEventDataStore[sceneEventId];
|
var sceneEventData = SceneEventDataStore[sceneEventId];
|
||||||
var sceneName = SceneNameFromHash(sceneHash);
|
var sceneName = SceneNameFromHash(sceneEventData.ClientSceneHash);
|
||||||
var nextScene = GetAndAddNewlyLoadedSceneByName(sceneName);
|
var nextScene = GetAndAddNewlyLoadedSceneByName(sceneName);
|
||||||
|
|
||||||
if (!nextScene.isLoaded || !nextScene.IsValid())
|
if (!nextScene.isLoaded || !nextScene.IsValid())
|
||||||
@@ -1548,7 +1512,7 @@ namespace Unity.Netcode
|
|||||||
throw new Exception($"Failed to find valid scene internal Unity.Netcode for {nameof(GameObject)}s error!");
|
throw new Exception($"Failed to find valid scene internal Unity.Netcode for {nameof(GameObject)}s error!");
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadSceneMode = (sceneHash == sceneEventData.SceneHash ? sceneEventData.LoadSceneMode : LoadSceneMode.Additive);
|
var loadSceneMode = (sceneEventData.ClientSceneHash == sceneEventData.SceneHash ? sceneEventData.LoadSceneMode : LoadSceneMode.Additive);
|
||||||
|
|
||||||
// For now, during a synchronization event, we will make the first scene the "base/master" scene that denotes a "complete scene switch"
|
// For now, during a synchronization event, we will make the first scene the "base/master" scene that denotes a "complete scene switch"
|
||||||
if (loadSceneMode == LoadSceneMode.Single)
|
if (loadSceneMode == LoadSceneMode.Single)
|
||||||
@@ -1556,9 +1520,9 @@ namespace Unity.Netcode
|
|||||||
SceneManager.SetActiveScene(nextScene);
|
SceneManager.SetActiveScene(nextScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ServerSceneHandleToClientSceneHandle.ContainsKey(sceneHandle))
|
if (!ServerSceneHandleToClientSceneHandle.ContainsKey(sceneEventData.ClientSceneHandle))
|
||||||
{
|
{
|
||||||
ServerSceneHandleToClientSceneHandle.Add(sceneHandle, nextScene.handle);
|
ServerSceneHandleToClientSceneHandle.Add(sceneEventData.ClientSceneHandle, nextScene.handle);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1573,14 +1537,14 @@ namespace Unity.Netcode
|
|||||||
var responseSceneEventData = BeginSceneEvent();
|
var responseSceneEventData = BeginSceneEvent();
|
||||||
responseSceneEventData.LoadSceneMode = loadSceneMode;
|
responseSceneEventData.LoadSceneMode = loadSceneMode;
|
||||||
responseSceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
responseSceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
||||||
responseSceneEventData.SceneHash = sceneHash;
|
responseSceneEventData.SceneHash = sceneEventData.ClientSceneHash;
|
||||||
|
|
||||||
|
|
||||||
var message = new SceneEventMessage
|
var message = new SceneEventMessage
|
||||||
{
|
{
|
||||||
EventData = responseSceneEventData
|
EventData = responseSceneEventData
|
||||||
};
|
};
|
||||||
var size = m_NetworkManager.SendMessage(message, k_DeliveryType, m_NetworkManager.ServerClientId);
|
var size = m_NetworkManager.SendMessage(ref message, k_DeliveryType, m_NetworkManager.ServerClientId);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(m_NetworkManager.ServerClientId, (uint)responseSceneEventData.SceneEventType, sceneName, size);
|
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(m_NetworkManager.ServerClientId, (uint)responseSceneEventData.SceneEventType, sceneName, size);
|
||||||
|
|
||||||
@@ -1830,21 +1794,23 @@ namespace Unity.Netcode
|
|||||||
/// Moves all NetworkObjects that don't have the <see cref="NetworkObject.DestroyWithScene"/> set to
|
/// Moves all NetworkObjects that don't have the <see cref="NetworkObject.DestroyWithScene"/> set to
|
||||||
/// the "Do not destroy on load" scene.
|
/// the "Do not destroy on load" scene.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void MoveObjectsToDontDestroyOnLoad()
|
internal void MoveObjectsToDontDestroyOnLoad()
|
||||||
{
|
{
|
||||||
// Move ALL NetworkObjects to the temp scene
|
// Move ALL NetworkObjects marked to persist scene transitions into the DDOL scene
|
||||||
var objectsToKeep = new HashSet<NetworkObject>(m_NetworkManager.SpawnManager.SpawnedObjectsList);
|
var objectsToKeep = new HashSet<NetworkObject>(m_NetworkManager.SpawnManager.SpawnedObjectsList);
|
||||||
|
|
||||||
foreach (var sobj in objectsToKeep)
|
foreach (var sobj in objectsToKeep)
|
||||||
{
|
{
|
||||||
if (!sobj.DestroyWithScene || (sobj.IsSceneObject != null && sobj.IsSceneObject.Value && sobj.gameObject.scene == DontDestroyOnLoadScene))
|
if (sobj == null)
|
||||||
{
|
{
|
||||||
// Only move objects with no parent as child objects will follow
|
continue;
|
||||||
if (sobj.gameObject.transform.parent == null)
|
}
|
||||||
|
|
||||||
|
if (!sobj.DestroyWithScene || sobj.gameObject.scene == DontDestroyOnLoadScene)
|
||||||
|
{
|
||||||
|
// Only move dynamically spawned network objects with no parent as child objects will follow
|
||||||
|
if (sobj.gameObject.transform.parent == null && sobj.IsSceneObject != null && !sobj.IsSceneObject.Value)
|
||||||
{
|
{
|
||||||
UnityEngine.Object.DontDestroyOnLoad(sobj.gameObject);
|
UnityEngine.Object.DontDestroyOnLoad(sobj.gameObject);
|
||||||
// Since we are doing a scene transition, disable the GameObject until the next scene is loaded
|
|
||||||
sobj.gameObject.SetActive(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_NetworkManager.IsServer)
|
else if (m_NetworkManager.IsServer)
|
||||||
@@ -1879,7 +1845,7 @@ namespace Unity.Netcode
|
|||||||
// at the end of scene loading we use this list to soft synchronize all in-scene placed NetworkObjects
|
// at the end of scene loading we use this list to soft synchronize all in-scene placed NetworkObjects
|
||||||
foreach (var networkObjectInstance in networkObjects)
|
foreach (var networkObjectInstance in networkObjects)
|
||||||
{
|
{
|
||||||
// We check to make sure the NetworkManager instance is the same one to be "MultiInstanceHelpers" compatible and filter the list on a per scene basis (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 (additive scenes)
|
||||||
if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy &&
|
if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy &&
|
||||||
networkObjectInstance.gameObject.scene.handle == sceneToFilterBy.handle)
|
networkObjectInstance.gameObject.scene.handle == sceneToFilterBy.handle)
|
||||||
{
|
{
|
||||||
@@ -1907,24 +1873,26 @@ namespace Unity.Netcode
|
|||||||
/// Moves all spawned NetworkObjects (from do not destroy on load) to the scene specified
|
/// Moves all spawned NetworkObjects (from do not destroy on load) to the scene specified
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scene">scene to move the NetworkObjects to</param>
|
/// <param name="scene">scene to move the NetworkObjects to</param>
|
||||||
private void MoveObjectsFromDontDestroyOnLoadToScene(Scene scene)
|
internal void MoveObjectsFromDontDestroyOnLoadToScene(Scene scene)
|
||||||
{
|
{
|
||||||
// Move ALL NetworkObjects to the temp scene
|
// Move ALL NetworkObjects to the temp scene
|
||||||
var objectsToKeep = m_NetworkManager.SpawnManager.SpawnedObjectsList;
|
var objectsToKeep = m_NetworkManager.SpawnManager.SpawnedObjectsList;
|
||||||
|
|
||||||
foreach (var sobj in objectsToKeep)
|
foreach (var sobj in objectsToKeep)
|
||||||
{
|
{
|
||||||
if (sobj.gameObject.scene == DontDestroyOnLoadScene && (sobj.IsSceneObject == null || sobj.IsSceneObject.Value))
|
if (sobj == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// If it is in the DDOL then
|
||||||
// Only move objects with no parent as child objects will follow
|
if (sobj.gameObject.scene == DontDestroyOnLoadScene)
|
||||||
if (sobj.gameObject.transform.parent == null)
|
|
||||||
{
|
{
|
||||||
// set it back to active at this point
|
// only move dynamically spawned network objects, with no parent as child objects will follow,
|
||||||
sobj.gameObject.SetActive(true);
|
// back into the currently active scene
|
||||||
SceneManager.MoveGameObjectToScene(sobj.gameObject, scene);
|
if (sobj.gameObject.transform.parent == null && sobj.IsSceneObject != null && !sobj.IsSceneObject.Value)
|
||||||
|
{
|
||||||
|
SceneManager.MoveGameObjectToScene(sobj.gameObject, scene);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using System.Linq;
|
|||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -100,6 +99,10 @@ namespace Unity.Netcode
|
|||||||
internal uint SceneHash;
|
internal uint SceneHash;
|
||||||
internal int SceneHandle;
|
internal int SceneHandle;
|
||||||
|
|
||||||
|
// Used by the client during synchronization
|
||||||
|
internal uint ClientSceneHash;
|
||||||
|
internal int ClientSceneHandle;
|
||||||
|
|
||||||
/// Only used for <see cref="SceneEventType.Synchronize"/> scene events, this assures permissions when writing
|
/// Only used for <see cref="SceneEventType.Synchronize"/> scene events, this assures permissions when writing
|
||||||
/// NetworkVariable information. If that process changes, then we need to update this
|
/// NetworkVariable information. If that process changes, then we need to update this
|
||||||
internal ulong TargetClientId;
|
internal ulong TargetClientId;
|
||||||
@@ -230,7 +233,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void AddSpawnedNetworkObjects()
|
internal void AddSpawnedNetworkObjects()
|
||||||
{
|
{
|
||||||
m_NetworkObjectsSync = m_NetworkManager.SpawnManager.SpawnedObjectsList.ToList();
|
m_NetworkObjectsSync.Clear();
|
||||||
|
foreach (var sobj in m_NetworkManager.SpawnManager.SpawnedObjectsList)
|
||||||
|
{
|
||||||
|
if (sobj.Observers.Contains(TargetClientId))
|
||||||
|
{
|
||||||
|
m_NetworkObjectsSync.Add(sobj);
|
||||||
|
}
|
||||||
|
}
|
||||||
m_NetworkObjectsSync.Sort(SortNetworkObjects);
|
m_NetworkObjectsSync.Sort(SortNetworkObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a FastBufferReader from a NativeArray.
|
/// Create a FastBufferReader from a NativeArray.
|
||||||
///
|
///
|
||||||
/// A new buffer will be created using the given allocator and the value will be copied in.
|
/// A new buffer will be created using the given allocator and the value will be copied in.
|
||||||
/// FastBufferReader will then own the data.
|
/// FastBufferReader will then own the data.
|
||||||
///
|
///
|
||||||
@@ -93,12 +93,12 @@ namespace Unity.Netcode
|
|||||||
/// <param name="offset"></param>
|
/// <param name="offset"></param>
|
||||||
public unsafe FastBufferReader(NativeArray<byte> buffer, Allocator allocator, int length = -1, int offset = 0)
|
public unsafe FastBufferReader(NativeArray<byte> buffer, Allocator allocator, int length = -1, int offset = 0)
|
||||||
{
|
{
|
||||||
Handle = CreateHandle((byte*)buffer.GetUnsafePtr(), Math.Max(1, length == -1 ? buffer.Length : length), offset, allocator);
|
Handle = CreateHandle((byte*)buffer.GetUnsafePtr(), length == -1 ? buffer.Length : length, offset, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a FastBufferReader from an ArraySegment.
|
/// Create a FastBufferReader from an ArraySegment.
|
||||||
///
|
///
|
||||||
/// A new buffer will be created using the given allocator and the value will be copied in.
|
/// A new buffer will be created using the given allocator and the value will be copied in.
|
||||||
/// FastBufferReader will then own the data.
|
/// FastBufferReader will then own the data.
|
||||||
///
|
///
|
||||||
@@ -117,13 +117,13 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
fixed (byte* data = buffer.Array)
|
fixed (byte* data = buffer.Array)
|
||||||
{
|
{
|
||||||
Handle = CreateHandle(data, Math.Max(1, length == -1 ? buffer.Count : length), offset, allocator);
|
Handle = CreateHandle(data, length == -1 ? buffer.Count : length, offset, allocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a FastBufferReader from an existing byte array.
|
/// Create a FastBufferReader from an existing byte array.
|
||||||
///
|
///
|
||||||
/// A new buffer will be created using the given allocator and the value will be copied in.
|
/// A new buffer will be created using the given allocator and the value will be copied in.
|
||||||
/// FastBufferReader will then own the data.
|
/// FastBufferReader will then own the data.
|
||||||
///
|
///
|
||||||
@@ -142,13 +142,13 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
fixed (byte* data = buffer)
|
fixed (byte* data = buffer)
|
||||||
{
|
{
|
||||||
Handle = CreateHandle(data, Math.Max(1, length == -1 ? buffer.Length : length), offset, allocator);
|
Handle = CreateHandle(data, length == -1 ? buffer.Length : length, offset, allocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a FastBufferReader from an existing byte buffer.
|
/// Create a FastBufferReader from an existing byte buffer.
|
||||||
///
|
///
|
||||||
/// A new buffer will be created using the given allocator and the value will be copied in.
|
/// A new buffer will be created using the given allocator and the value will be copied in.
|
||||||
/// FastBufferReader will then own the data.
|
/// FastBufferReader will then own the data.
|
||||||
///
|
///
|
||||||
@@ -165,12 +165,12 @@ namespace Unity.Netcode
|
|||||||
/// <param name="offset">The offset of the buffer to start copying from</param>
|
/// <param name="offset">The offset of the buffer to start copying from</param>
|
||||||
public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0)
|
public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0)
|
||||||
{
|
{
|
||||||
Handle = CreateHandle(buffer, Math.Max(1, length), offset, allocator);
|
Handle = CreateHandle(buffer, length, offset, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a FastBufferReader from a FastBufferWriter.
|
/// Create a FastBufferReader from a FastBufferWriter.
|
||||||
///
|
///
|
||||||
/// A new buffer will be created using the given allocator and the value will be copied in.
|
/// A new buffer will be created using the given allocator and the value will be copied in.
|
||||||
/// FastBufferReader will then own the data.
|
/// FastBufferReader will then own the data.
|
||||||
///
|
///
|
||||||
@@ -187,7 +187,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="offset">The offset of the buffer to start copying from</param>
|
/// <param name="offset">The offset of the buffer to start copying from</param>
|
||||||
public unsafe FastBufferReader(FastBufferWriter writer, Allocator allocator, int length = -1, int offset = 0)
|
public unsafe FastBufferReader(FastBufferWriter writer, Allocator allocator, int length = -1, int offset = 0)
|
||||||
{
|
{
|
||||||
Handle = CreateHandle(writer.GetUnsafePtr(), Math.Max(1, length == -1 ? writer.Length : length), offset, allocator);
|
Handle = CreateHandle(writer.GetUnsafePtr(), length == -1 ? writer.Length : length, offset, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -250,7 +250,7 @@ namespace Unity.Netcode
|
|||||||
/// When you know you will be reading multiple fields back-to-back and you know the total size,
|
/// When you know you will be reading multiple fields back-to-back and you know the total size,
|
||||||
/// you can call TryBeginRead() once on the total size, and then follow it with calls to
|
/// you can call TryBeginRead() once on the total size, and then follow it with calls to
|
||||||
/// ReadValue() instead of ReadValueSafe() for faster serialization.
|
/// ReadValue() instead of ReadValueSafe() for faster serialization.
|
||||||
///
|
///
|
||||||
/// Unsafe read operations will throw OverflowException in editor and development builds if you
|
/// Unsafe read operations will throw OverflowException in editor and development builds if you
|
||||||
/// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown
|
/// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown
|
||||||
/// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following
|
/// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following
|
||||||
@@ -284,7 +284,7 @@ namespace Unity.Netcode
|
|||||||
/// When you know you will be reading multiple fields back-to-back and you know the total size,
|
/// When you know you will be reading multiple fields back-to-back and you know the total size,
|
||||||
/// you can call TryBeginRead() once on the total size, and then follow it with calls to
|
/// you can call TryBeginRead() once on the total size, and then follow it with calls to
|
||||||
/// ReadValue() instead of ReadValueSafe() for faster serialization.
|
/// ReadValue() instead of ReadValueSafe() for faster serialization.
|
||||||
///
|
///
|
||||||
/// Unsafe read operations will throw OverflowException in editor and development builds if you
|
/// Unsafe read operations will throw OverflowException in editor and development builds if you
|
||||||
/// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown
|
/// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown
|
||||||
/// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following
|
/// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ namespace Unity.Netcode
|
|||||||
var newSize = Math.Min(desiredSize, Handle->MaxCapacity);
|
var newSize = Math.Min(desiredSize, Handle->MaxCapacity);
|
||||||
byte* newBuffer = (byte*)UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf<byte>(), Handle->Allocator);
|
byte* newBuffer = (byte*)UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf<byte>(), Handle->Allocator);
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
UnsafeUtility.MemSet(newBuffer, 0, sizeof(WriterHandle) + newSize);
|
UnsafeUtility.MemSet(newBuffer, 0, newSize);
|
||||||
#endif
|
#endif
|
||||||
UnsafeUtility.MemCpy(newBuffer, Handle->BufferPointer, Length);
|
UnsafeUtility.MemCpy(newBuffer, Handle->BufferPointer, Length);
|
||||||
if (Handle->BufferGrew)
|
if (Handle->BufferGrew)
|
||||||
@@ -428,7 +428,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="count"></param>
|
/// <param name="count"></param>
|
||||||
/// <param name="offset"></param>
|
/// <param name="offset"></param>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
public void WriteNetworkSerializable<T>(INetworkSerializable[] array, int count = -1, int offset = 0) where T : INetworkSerializable
|
public void WriteNetworkSerializable<T>(T[] array, int count = -1, int offset = 0) where T : INetworkSerializable
|
||||||
{
|
{
|
||||||
int sizeInTs = count != -1 ? count : array.Length - offset;
|
int sizeInTs = count != -1 ? count : array.Length - offset;
|
||||||
WriteValueSafe(sizeInTs);
|
WriteValueSafe(sizeInTs);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace Unity.Netcode
|
|||||||
public MessageHeader Header;
|
public MessageHeader Header;
|
||||||
public ulong SenderId;
|
public ulong SenderId;
|
||||||
public float Timestamp;
|
public float Timestamp;
|
||||||
|
public int SerializedHeaderSize;
|
||||||
}
|
}
|
||||||
private struct TriggerInfo
|
private struct TriggerInfo
|
||||||
{
|
{
|
||||||
@@ -101,7 +102,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>
|
||||||
internal unsafe void TriggerOnSpawn(ulong networkObjectId, FastBufferReader reader, in NetworkContext context)
|
internal unsafe void TriggerOnSpawn(ulong networkObjectId, FastBufferReader reader, ref NetworkContext context)
|
||||||
{
|
{
|
||||||
if (!m_Triggers.ContainsKey(networkObjectId))
|
if (!m_Triggers.ContainsKey(networkObjectId))
|
||||||
{
|
{
|
||||||
@@ -117,7 +118,8 @@ namespace Unity.Netcode
|
|||||||
Reader = new FastBufferReader(reader.GetUnsafePtr(), Allocator.Persistent, reader.Length),
|
Reader = new FastBufferReader(reader.GetUnsafePtr(), Allocator.Persistent, reader.Length),
|
||||||
Header = context.Header,
|
Header = context.Header,
|
||||||
Timestamp = context.Timestamp,
|
Timestamp = context.Timestamp,
|
||||||
SenderId = context.SenderId
|
SenderId = context.SenderId,
|
||||||
|
SerializedHeaderSize = context.SerializedHeaderSize
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +156,24 @@ namespace Unity.Netcode
|
|||||||
m_Triggers.Remove(staleKeys[i]);
|
m_Triggers.Remove(staleKeys[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Cleans up any trigger that's existed for more than a second.
|
||||||
|
/// These triggers were probably for situations where a request was received after a despawn rather than before a spawn.
|
||||||
|
/// </summary>
|
||||||
|
internal void CleanupAllTriggers()
|
||||||
|
{
|
||||||
|
foreach (var kvp in m_Triggers)
|
||||||
|
{
|
||||||
|
foreach (var data in kvp.Value.TriggerData)
|
||||||
|
{
|
||||||
|
data.Reader.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
kvp.Value.TriggerData.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Triggers.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
internal void RemoveOwnership(NetworkObject networkObject)
|
internal void RemoveOwnership(NetworkObject networkObject)
|
||||||
{
|
{
|
||||||
@@ -167,28 +187,44 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is not spawned");
|
throw new SpawnStateException("Object is not spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = NetworkManager.ConnectedClients[networkObject.OwnerClientId].OwnedObjects.Count - 1;
|
// If we made it here then we are the server and if the server is determined to already be the owner
|
||||||
i > -1;
|
// then ignore the RemoveOwnership invocation.
|
||||||
i--)
|
if (networkObject.OwnerClientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
if (NetworkManager.ConnectedClients[networkObject.OwnerClientId].OwnedObjects[i] == networkObject)
|
return;
|
||||||
{
|
|
||||||
NetworkManager.ConnectedClients[networkObject.OwnerClientId].OwnedObjects.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
networkObject.OwnerClientIdInternal = null;
|
// Make sure the connected client entry exists before trying to remove ownership.
|
||||||
|
if (TryGetNetworkClient(networkObject.OwnerClientId, out NetworkClient networkClient))
|
||||||
var message = new ChangeOwnershipMessage
|
|
||||||
{
|
{
|
||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
for (int i = networkClient.OwnedObjects.Count - 1; i > -1; i--)
|
||||||
OwnerClientId = networkObject.OwnerClientId
|
{
|
||||||
};
|
if (networkClient.OwnedObjects[i] == networkObject)
|
||||||
var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
{
|
||||||
|
networkClient.OwnedObjects.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var client in NetworkManager.ConnectedClients)
|
networkObject.OwnerClientIdInternal = null;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"No connected clients prior to removing ownership for {networkObject.name}. Make sure you are not initializing or shutting down when removing ownership.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +243,7 @@ namespace Unity.Netcode
|
|||||||
return NetworkManager.ConnectedClients.TryGetValue(clientId, out networkClient);
|
return NetworkManager.ConnectedClients.TryGetValue(clientId, out networkClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientId == NetworkManager.LocalClient.ClientId)
|
if (NetworkManager.LocalClient != null && clientId == NetworkManager.LocalClient.ClientId)
|
||||||
{
|
{
|
||||||
networkClient = NetworkManager.LocalClient;
|
networkClient = NetworkManager.LocalClient;
|
||||||
return true;
|
return true;
|
||||||
@@ -244,13 +280,17 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
networkObject.OwnerClientId = clientId;
|
networkObject.OwnerClientId = clientId;
|
||||||
|
|
||||||
|
if (TryGetNetworkClient(clientId, out NetworkClient newNetworkClient))
|
||||||
|
{
|
||||||
|
newNetworkClient.OwnedObjects.Add(networkObject);
|
||||||
|
}
|
||||||
|
|
||||||
var message = new ChangeOwnershipMessage
|
var message = new ChangeOwnershipMessage
|
||||||
{
|
{
|
||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
NetworkObjectId = networkObject.NetworkObjectId,
|
||||||
OwnerClientId = networkObject.OwnerClientId
|
OwnerClientId = networkObject.OwnerClientId
|
||||||
};
|
};
|
||||||
var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
||||||
|
|
||||||
foreach (var client in NetworkManager.ConnectedClients)
|
foreach (var client in NetworkManager.ConnectedClients)
|
||||||
{
|
{
|
||||||
@@ -386,6 +426,15 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is already spawned");
|
throw new SpawnStateException("Object is already spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!sceneObject)
|
||||||
|
{
|
||||||
|
var networkObjectChildren = networkObject.GetComponentsInChildren<NetworkObject>();
|
||||||
|
if (networkObjectChildren.Length > 1)
|
||||||
|
{
|
||||||
|
Debug.LogError("Spawning NetworkObjects with nested NetworkObjects is only supported for scene objects. Child NetworkObjects will not be spawned over the network!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SpawnNetworkObjectLocallyCommon(networkObject, networkId, sceneObject, playerObject, ownerClientId, destroyWithScene);
|
SpawnNetworkObjectLocallyCommon(networkObject, networkId, sceneObject, playerObject, ownerClientId, destroyWithScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,12 +532,19 @@ namespace Unity.Netcode
|
|||||||
foreach (var trigger in triggerInfo.TriggerData)
|
foreach (var trigger in triggerInfo.TriggerData)
|
||||||
{
|
{
|
||||||
// Reader will be disposed within HandleMessage
|
// Reader will be disposed within HandleMessage
|
||||||
NetworkManager.MessagingSystem.HandleMessage(trigger.Header, trigger.Reader, trigger.SenderId, trigger.Timestamp);
|
NetworkManager.MessagingSystem.HandleMessage(trigger.Header, trigger.Reader, trigger.SenderId, trigger.Timestamp, trigger.SerializedHeaderSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerInfo.TriggerData.Dispose();
|
triggerInfo.TriggerData.Dispose();
|
||||||
m_Triggers.Remove(networkId);
|
m_Triggers.Remove(networkId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// propagate the IsSceneObject setting to child NetworkObjects
|
||||||
|
var children = networkObject.GetComponentsInChildren<NetworkObject>();
|
||||||
|
foreach (var childObject in children)
|
||||||
|
{
|
||||||
|
childObject.IsSceneObject = sceneObject;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject)
|
internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject)
|
||||||
@@ -507,7 +563,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
ObjectInfo = networkObject.GetMessageSceneObject(clientId)
|
ObjectInfo = networkObject.GetMessageSceneObject(clientId)
|
||||||
};
|
};
|
||||||
var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
||||||
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
||||||
|
|
||||||
networkObject.MarkVariablesDirty();
|
networkObject.MarkVariablesDirty();
|
||||||
@@ -580,7 +636,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void DestroyNonSceneObjects()
|
internal void DespawnAndDestroyNetworkObjects()
|
||||||
{
|
{
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
||||||
|
|
||||||
@@ -588,17 +644,25 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (networkObjects[i].NetworkManager == NetworkManager)
|
if (networkObjects[i].NetworkManager == NetworkManager)
|
||||||
{
|
{
|
||||||
if (networkObjects[i].IsSceneObject != null && networkObjects[i].IsSceneObject.Value == false)
|
if (NetworkManager.PrefabHandler.ContainsHandler(networkObjects[i]))
|
||||||
{
|
{
|
||||||
if (NetworkManager.PrefabHandler.ContainsHandler(networkObjects[i]))
|
OnDespawnObject(networkObjects[i], false);
|
||||||
{
|
// Leave destruction up to the handler
|
||||||
NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObjects[i]);
|
NetworkManager.PrefabHandler.HandleNetworkPrefabDestroy(networkObjects[i]);
|
||||||
OnDespawnObject(networkObjects[i], false);
|
}
|
||||||
}
|
else if (networkObjects[i].IsSpawned)
|
||||||
else
|
{
|
||||||
{
|
// If it is an in-scene placed NetworkObject then just despawn
|
||||||
UnityEngine.Object.Destroy(networkObjects[i].gameObject);
|
// and let it be destroyed when the scene is unloaded. Otherwise,
|
||||||
}
|
// despawn and destroy it.
|
||||||
|
var shouldDestroy = !(networkObjects[i].IsSceneObject != null
|
||||||
|
&& networkObjects[i].IsSceneObject.Value);
|
||||||
|
|
||||||
|
OnDespawnObject(networkObjects[i], shouldDestroy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(networkObjects[i].gameObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -634,6 +698,7 @@ namespace Unity.Netcode
|
|||||||
internal void ServerSpawnSceneObjectsOnStartSweep()
|
internal void ServerSpawnSceneObjectsOnStartSweep()
|
||||||
{
|
{
|
||||||
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>();
|
||||||
|
var networkObjectsToSpawn = new List<NetworkObject>();
|
||||||
|
|
||||||
for (int i = 0; i < networkObjects.Length; i++)
|
for (int i = 0; i < networkObjects.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -641,10 +706,15 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (networkObjects[i].IsSceneObject == null)
|
if (networkObjects[i].IsSceneObject == null)
|
||||||
{
|
{
|
||||||
SpawnNetworkObjectLocally(networkObjects[i], GetNetworkObjectId(), true, false, null, true);
|
networkObjectsToSpawn.Add(networkObjects[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var networkObject in networkObjectsToSpawn)
|
||||||
|
{
|
||||||
|
SpawnNetworkObjectLocally(networkObject, GetNetworkObjectId(), true, false, null, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObject)
|
internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObject)
|
||||||
@@ -668,17 +738,21 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move child NetworkObjects to the root when parent NetworkObject is destroyed
|
// If we are shutting down the NetworkManager, then ignore resetting the parent
|
||||||
foreach (var spawnedNetObj in SpawnedObjectsList)
|
if (!NetworkManager.ShutdownInProgress)
|
||||||
{
|
{
|
||||||
var (isReparented, latestParent) = spawnedNetObj.GetNetworkParenting();
|
// Move child NetworkObjects to the root when parent NetworkObject is destroyed
|
||||||
if (isReparented && latestParent == networkObject.NetworkObjectId)
|
foreach (var spawnedNetObj in SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
spawnedNetObj.gameObject.transform.parent = null;
|
var (isReparented, latestParent) = spawnedNetObj.GetNetworkParenting();
|
||||||
|
if (isReparented && latestParent == networkObject.NetworkObjectId)
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"{nameof(NetworkObject)} #{spawnedNetObj.NetworkObjectId} moved to the root because its parent {nameof(NetworkObject)} #{networkObject.NetworkObjectId} is destroyed");
|
spawnedNetObj.gameObject.transform.parent = null;
|
||||||
|
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"{nameof(NetworkObject)} #{spawnedNetObj.NetworkObjectId} moved to the root because its parent {nameof(NetworkObject)} #{networkObject.NetworkObjectId} is destroyed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -735,7 +809,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
NetworkObjectId = networkObject.NetworkObjectId
|
NetworkObjectId = networkObject.NetworkObjectId
|
||||||
};
|
};
|
||||||
var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, m_TargetClientIds);
|
var size = NetworkManager.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);
|
||||||
@@ -774,7 +848,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
foreach (var sobj in SpawnedObjectsList)
|
foreach (var sobj in SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
if (sobj.CheckObjectVisibility == null || NetworkManager.IsServer)
|
if (sobj.CheckObjectVisibility == null)
|
||||||
{
|
{
|
||||||
if (!sobj.Observers.Contains(clientId))
|
if (!sobj.Observers.Contains(clientId))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -114,6 +114,11 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
double d = m_TimeSec / m_TickInterval;
|
double d = m_TimeSec / m_TickInterval;
|
||||||
m_CachedTick = (int)d;
|
m_CachedTick = (int)d;
|
||||||
|
// This check is needed due to double division imprecision of large numbers
|
||||||
|
if ((d - m_CachedTick) >= 0.999999999999)
|
||||||
|
{
|
||||||
|
m_CachedTick++;
|
||||||
|
}
|
||||||
m_CachedTickOffset = ((d - Math.Truncate(d)) * m_TickInterval);
|
m_CachedTickOffset = ((d - Math.Truncate(d)) * m_TickInterval);
|
||||||
|
|
||||||
// This handles negative time, decreases tick by 1 and makes offset positive.
|
// This handles negative time, decreases tick by 1 and makes offset positive.
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace Unity.Netcode
|
|||||||
/// <value><c>true</c> if is supported; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if is supported; otherwise, <c>false</c>.</value>
|
||||||
public virtual bool IsSupported => true;
|
public virtual bool IsSupported => true;
|
||||||
|
|
||||||
|
internal INetworkMetrics NetworkMetrics;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for transport network events
|
/// Delegate for transport network events
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -95,6 +97,14 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the transport
|
/// Initializes the transport
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract void Initialize();
|
/// /// <param name="networkManager">optionally pass in NetworkManager</param>
|
||||||
|
public abstract void Initialize(NetworkManager networkManager = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if UNITY_INCLUDE_TESTS
|
||||||
|
public abstract class TestingNetworkTransport : NetworkTransport
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if UNITY_UNET_PRESENT
|
||||||
using System;
|
using System;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
@@ -50,3 +51,4 @@ namespace Unity.Netcode.Transports.UNET
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if UNITY_UNET_PRESENT
|
||||||
#pragma warning disable 618 // disable is obsolete
|
#pragma warning disable 618 // disable is obsolete
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
using System;
|
using System;
|
||||||
@@ -41,6 +42,8 @@ namespace Unity.Netcode.Transports.UNET
|
|||||||
|
|
||||||
public override ulong ServerClientId => GetNetcodeClientId(0, 0, true);
|
public override ulong ServerClientId => GetNetcodeClientId(0, 0, true);
|
||||||
|
|
||||||
|
internal NetworkManager NetworkManager;
|
||||||
|
|
||||||
protected void LateUpdate()
|
protected void LateUpdate()
|
||||||
{
|
{
|
||||||
if (UnityEngine.Networking.NetworkTransport.IsStarted && MessageSendMode == SendMode.Queued)
|
if (UnityEngine.Networking.NetworkTransport.IsStarted && MessageSendMode == SendMode.Queued)
|
||||||
@@ -48,7 +51,7 @@ namespace Unity.Netcode.Transports.UNET
|
|||||||
#if UNITY_WEBGL
|
#if UNITY_WEBGL
|
||||||
Debug.LogError("Cannot use queued sending mode for WebGL");
|
Debug.LogError("Cannot use queued sending mode for WebGL");
|
||||||
#else
|
#else
|
||||||
if (NetworkManager.Singleton.IsServer)
|
if (NetworkManager != null && NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
foreach (var targetClient in NetworkManager.Singleton.ConnectedClientsList)
|
foreach (var targetClient in NetworkManager.Singleton.ConnectedClientsList)
|
||||||
{
|
{
|
||||||
@@ -230,8 +233,10 @@ namespace Unity.Netcode.Transports.UNET
|
|||||||
UnityEngine.Networking.NetworkTransport.Shutdown();
|
UnityEngine.Networking.NetworkTransport.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize(NetworkManager networkManager = null)
|
||||||
{
|
{
|
||||||
|
NetworkManager = networkManager;
|
||||||
|
|
||||||
m_MessageBuffer = new byte[MessageBufferSize];
|
m_MessageBuffer = new byte[MessageBufferSize];
|
||||||
|
|
||||||
UnityEngine.Networking.NetworkTransport.Init();
|
UnityEngine.Networking.NetworkTransport.Init();
|
||||||
@@ -279,3 +284,4 @@ namespace Unity.Netcode.Transports.UNET
|
|||||||
}
|
}
|
||||||
#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
|
||||||
#pragma warning restore 618 // restore is obsolete
|
#pragma warning restore 618 // restore is obsolete
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -6,21 +6,28 @@
|
|||||||
"Unity.Multiplayer.NetStats",
|
"Unity.Multiplayer.NetStats",
|
||||||
"Unity.Multiplayer.NetStatsReporting",
|
"Unity.Multiplayer.NetStatsReporting",
|
||||||
"Unity.Multiplayer.NetworkSolutionInterface",
|
"Unity.Multiplayer.NetworkSolutionInterface",
|
||||||
|
"Unity.Multiplayer.Tools.MetricTypes",
|
||||||
|
"Unity.Multiplayer.Tools.NetStats",
|
||||||
|
"Unity.Multiplayer.Tools.NetStatsReporting",
|
||||||
|
"Unity.Multiplayer.Tools.NetworkSolutionInterface",
|
||||||
"Unity.Collections"
|
"Unity.Collections"
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
|
||||||
"excludePlatforms": [],
|
|
||||||
"allowUnsafeCode": true,
|
"allowUnsafeCode": true,
|
||||||
"overrideReferences": false,
|
|
||||||
"precompiledReferences": [],
|
|
||||||
"autoReferenced": true,
|
|
||||||
"defineConstraints": [],
|
|
||||||
"versionDefines": [
|
"versionDefines": [
|
||||||
{
|
{
|
||||||
"name": "com.unity.multiplayer.tools",
|
"name": "com.unity.multiplayer.tools",
|
||||||
"expression": "",
|
"expression": "",
|
||||||
"define": "MULTIPLAYER_TOOLS"
|
"define": "MULTIPLAYER_TOOLS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Unity",
|
||||||
|
"expression": "(0,2022.2.0a5)",
|
||||||
|
"define": "UNITY_UNET_PRESENT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.unity.multiplayer.tools",
|
||||||
|
"expression": "1.0.0-pre.4",
|
||||||
|
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_4"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"noEngineReferences": false
|
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,7 @@ namespace Unity.Netcode.Samples
|
|||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
|
CanCommitToTransform = IsOwner;
|
||||||
base.Update();
|
base.Update();
|
||||||
if (NetworkManager.Singleton != null && (NetworkManager.Singleton.IsConnectedClient || NetworkManager.Singleton.IsListening))
|
if (NetworkManager.Singleton != null && (NetworkManager.Singleton.IsConnectedClient || NetworkManager.Singleton.IsListening))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: fb1b6e801936c7f4a9af28dbed5ea2ff
|
guid: d627e2fb516d92242a4930e5cd9291e3
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 4e60372130aba464f9f9ae4a24bb9fe0
|
guid: e9af0202c9057c944b67aad6e4cdac96
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
6
TestHelpers/Runtime/AssemblyInfo.cs
Normal file
6
TestHelpers/Runtime/AssemblyInfo.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("TestProject.EditorTests")]
|
||||||
|
[assembly: InternalsVisibleTo("TestProject.RuntimeTests")]
|
||||||
|
[assembly: InternalsVisibleTo("TestProject.ToolsIntegration.RuntimeTests")]
|
||||||
|
[assembly: InternalsVisibleTo("Unity.Netcode.RuntimeTests")]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: e946a6fdcfcb9dd48b76b38871c0a77b
|
guid: 10ca1ce26995e754599c9eedc2c228d8
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: f2ef964afcae91248b2298b479ed1b53
|
guid: 19fbc3f43e13a9144a9c66c68a1c43c1
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
93
TestHelpers/Runtime/Components/ObjectNameIdentifier.cs
Normal file
93
TestHelpers/Runtime/Components/ObjectNameIdentifier.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
|
||||||
|
namespace Unity.Netcode.TestHelpers.Runtime
|
||||||
|
{
|
||||||
|
public class ObjectNameIdentifier : NetworkBehaviour
|
||||||
|
{
|
||||||
|
private ulong m_CurrentOwner;
|
||||||
|
private ulong m_CurrentNetworkObjectId;
|
||||||
|
private bool m_IsRegistered;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keep a reference to the assigned NetworkObject
|
||||||
|
/// <see cref="OnDestroy"/>
|
||||||
|
/// </summary>
|
||||||
|
private NetworkObject m_NetworkObject;
|
||||||
|
|
||||||
|
public override void OnNetworkSpawn()
|
||||||
|
{
|
||||||
|
RegisterAndLabelNetworkObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void RegisterAndLabelNetworkObject()
|
||||||
|
{
|
||||||
|
if (!m_IsRegistered)
|
||||||
|
{
|
||||||
|
// This is required otherwise it will try to continue to update the NetworkBehaviour even if
|
||||||
|
// it has been destroyed.
|
||||||
|
m_NetworkObject = NetworkObject;
|
||||||
|
m_CurrentOwner = OwnerClientId;
|
||||||
|
m_CurrentNetworkObjectId = NetworkObjectId;
|
||||||
|
var objectOriginalName = gameObject.name.Replace("(Clone)", "");
|
||||||
|
var serverOrClient = IsServer ? "Server" : "Client";
|
||||||
|
if (NetworkObject.IsPlayerObject)
|
||||||
|
{
|
||||||
|
gameObject.name = NetworkManager.LocalClientId == OwnerClientId ? $"{objectOriginalName}({OwnerClientId})-Local{objectOriginalName}" :
|
||||||
|
$"{objectOriginalName}({OwnerClientId})-On{serverOrClient}({NetworkManager.LocalClientId})";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gameObject.name = $"{objectOriginalName}({NetworkObjectId})-On{serverOrClient}({NetworkManager.LocalClientId})";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't add the player objects to the global list of NetworkObjects
|
||||||
|
if (!NetworkObject.IsPlayerObject)
|
||||||
|
{
|
||||||
|
NetcodeIntegrationTest.RegisterNetworkObject(NetworkObject);
|
||||||
|
}
|
||||||
|
m_IsRegistered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DeRegisterNetworkObject()
|
||||||
|
{
|
||||||
|
if (m_IsRegistered)
|
||||||
|
{
|
||||||
|
NetcodeIntegrationTest.DeregisterNetworkObject(m_CurrentOwner, m_CurrentNetworkObjectId);
|
||||||
|
m_IsRegistered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLostOwnership()
|
||||||
|
{
|
||||||
|
DeRegisterNetworkObject();
|
||||||
|
RegisterAndLabelNetworkObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGainedOwnership()
|
||||||
|
{
|
||||||
|
DeRegisterNetworkObject();
|
||||||
|
RegisterAndLabelNetworkObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkDespawn()
|
||||||
|
{
|
||||||
|
DeRegisterNetworkObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
if (m_NetworkObject != null)
|
||||||
|
{
|
||||||
|
DeRegisterNetworkObject();
|
||||||
|
// This is required otherwise it will try to continue to update the NetworkBehaviour even if
|
||||||
|
// it has been destroyed (most likely integration test specific)
|
||||||
|
if (m_NetworkObject.ChildNetworkBehaviours != null && m_NetworkObject.ChildNetworkBehaviours.Contains(this))
|
||||||
|
{
|
||||||
|
NetworkObject.ChildNetworkBehaviours.Remove(this);
|
||||||
|
}
|
||||||
|
m_NetworkObject = null;
|
||||||
|
}
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
TestHelpers/Runtime/Components/ObjectNameIdentifier.cs.meta
Normal file
11
TestHelpers/Runtime/Components/ObjectNameIdentifier.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a915cfb2e4f748e4f9526a8bf5ee84f2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
60
TestHelpers/Runtime/ConditionalPredicate.cs
Normal file
60
TestHelpers/Runtime/ConditionalPredicate.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
namespace Unity.Netcode.TestHelpers.Runtime
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Derive from this class to create your own conditional handling for your <see cref="NetcodeIntegrationTest"/>
|
||||||
|
/// integration tests when dealing with more complicated scenarios where initializing values, storing state to be
|
||||||
|
/// used across several integration tests.
|
||||||
|
/// </summary>
|
||||||
|
public class ConditionalPredicateBase : IConditionalPredicate
|
||||||
|
{
|
||||||
|
private bool m_TimedOut;
|
||||||
|
|
||||||
|
public bool TimedOut { get { return m_TimedOut; } }
|
||||||
|
|
||||||
|
protected virtual bool OnHasConditionBeenReached()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasConditionBeenReached()
|
||||||
|
{
|
||||||
|
return OnHasConditionBeenReached();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnStarted() { }
|
||||||
|
|
||||||
|
public void Started()
|
||||||
|
{
|
||||||
|
OnStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnFinished() { }
|
||||||
|
|
||||||
|
public void Finished(bool timedOut)
|
||||||
|
{
|
||||||
|
m_TimedOut = timedOut;
|
||||||
|
OnFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IConditionalPredicate
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Test the conditions of the test to be reached
|
||||||
|
/// </summary>
|
||||||
|
bool HasConditionBeenReached();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait for condition has started
|
||||||
|
/// </summary>
|
||||||
|
void Started();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wait for condition has finished:
|
||||||
|
/// Condition(s) met or timed out
|
||||||
|
/// </summary>
|
||||||
|
void Finished(bool timedOut);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
11
TestHelpers/Runtime/ConditionalPredicate.cs.meta
Normal file
11
TestHelpers/Runtime/ConditionalPredicate.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dada6cae693646a4095924917e5e707a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
115
TestHelpers/Runtime/IntegrationTestSceneHandler.cs
Normal file
115
TestHelpers/Runtime/IntegrationTestSceneHandler.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.TestHelpers.Runtime
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The default SceneManagerHandler used for all NetcodeIntegrationTest derived children.
|
||||||
|
/// </summary>
|
||||||
|
internal class IntegrationTestSceneHandler : ISceneManagerHandler, IDisposable
|
||||||
|
{
|
||||||
|
internal CoroutineRunner CoroutineRunner;
|
||||||
|
|
||||||
|
// Default client simulated delay time
|
||||||
|
protected const float k_ClientLoadingSimulatedDelay = 0.02f;
|
||||||
|
|
||||||
|
// Controls the client simulated delay time
|
||||||
|
protected float m_ClientLoadingSimulatedDelay = k_ClientLoadingSimulatedDelay;
|
||||||
|
|
||||||
|
public delegate bool CanClientsLoadUnloadDelegateHandler();
|
||||||
|
public event CanClientsLoadUnloadDelegateHandler CanClientsLoad;
|
||||||
|
public event CanClientsLoadUnloadDelegateHandler CanClientsUnload;
|
||||||
|
|
||||||
|
internal List<Coroutine> CoroutinesRunning = new List<Coroutine>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to control when clients should attempt to fake-load a scene
|
||||||
|
/// Note: Unit/Integration tests that only use <see cref="NetcodeIntegrationTestHelpers"/>
|
||||||
|
/// need to subscribe to the CanClientsLoad and CanClientsUnload events
|
||||||
|
/// in order to control when clients can fake-load.
|
||||||
|
/// Tests that derive from <see cref="NetcodeIntegrationTest"/> already have integrated
|
||||||
|
/// support and you can override <see cref="NetcodeIntegrationTest.CanClientsLoad"/> and
|
||||||
|
/// <see cref="NetcodeIntegrationTest.CanClientsUnload"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected bool OnCanClientsLoad()
|
||||||
|
{
|
||||||
|
if (CanClientsLoad != null)
|
||||||
|
{
|
||||||
|
return CanClientsLoad.Invoke();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fake-Loads a scene for a client
|
||||||
|
/// </summary>
|
||||||
|
internal IEnumerator ClientLoadSceneCoroutine(string sceneName, ISceneManagerHandler.SceneEventAction sceneEventAction)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(m_ClientLoadingSimulatedDelay);
|
||||||
|
while (!OnCanClientsLoad())
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(m_ClientLoadingSimulatedDelay);
|
||||||
|
}
|
||||||
|
sceneEventAction.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected bool OnCanClientsUnload()
|
||||||
|
{
|
||||||
|
if (CanClientsUnload != null)
|
||||||
|
{
|
||||||
|
return CanClientsUnload.Invoke();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fake-Unloads a scene for a client
|
||||||
|
/// </summary>
|
||||||
|
internal IEnumerator ClientUnloadSceneCoroutine(ISceneManagerHandler.SceneEventAction sceneEventAction)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(m_ClientLoadingSimulatedDelay);
|
||||||
|
while (!OnCanClientsUnload())
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(m_ClientLoadingSimulatedDelay);
|
||||||
|
}
|
||||||
|
sceneEventAction.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, ISceneManagerHandler.SceneEventAction sceneEventAction)
|
||||||
|
{
|
||||||
|
CoroutinesRunning.Add(CoroutineRunner.StartCoroutine(ClientLoadSceneCoroutine(sceneName, sceneEventAction)));
|
||||||
|
// This is OK to return a "nothing" AsyncOperation since we are simulating client loading
|
||||||
|
return new AsyncOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncOperation UnloadSceneAsync(Scene scene, ISceneManagerHandler.SceneEventAction sceneEventAction)
|
||||||
|
{
|
||||||
|
CoroutinesRunning.Add(CoroutineRunner.StartCoroutine(ClientUnloadSceneCoroutine(sceneEventAction)));
|
||||||
|
// This is OK to return a "nothing" AsyncOperation since we are simulating client loading
|
||||||
|
return new AsyncOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntegrationTestSceneHandler()
|
||||||
|
{
|
||||||
|
if (CoroutineRunner == null)
|
||||||
|
{
|
||||||
|
CoroutineRunner = new GameObject("UnitTestSceneHandlerCoroutine").AddComponent<CoroutineRunner>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var coroutine in CoroutinesRunning)
|
||||||
|
{
|
||||||
|
CoroutineRunner.StopCoroutine(coroutine);
|
||||||
|
}
|
||||||
|
CoroutineRunner.StopAllCoroutines();
|
||||||
|
|
||||||
|
Object.Destroy(CoroutineRunner.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
TestHelpers/Runtime/IntegrationTestSceneHandler.cs.meta
Normal file
11
TestHelpers/Runtime/IntegrationTestSceneHandler.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 384935cc0ae40d641910e4c3924038c6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: af81f9951b096ff4cb8e4f8a4106104a
|
guid: ebacdb7d8cb876a43b4a908dd6d83aa9
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -1,20 +1,11 @@
|
|||||||
#if MULTIPLAYER_TOOLS
|
#if MULTIPLAYER_TOOLS
|
||||||
using System;
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using Unity.Multiplayer.Tools.MetricTypes;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.TestTools;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.RuntimeTests.Metrics.Utility
|
namespace Unity.Netcode.TestHelpers.Runtime.Metrics
|
||||||
{
|
{
|
||||||
internal abstract class SingleClientMetricTestBase : BaseMultiInstanceTest
|
internal abstract class SingleClientMetricTestBase : NetcodeIntegrationTest
|
||||||
{
|
{
|
||||||
protected override int NbClients => 1;
|
protected override int NumberOfClients => 1;
|
||||||
|
|
||||||
protected virtual Action<GameObject> UpdatePlayerPrefab => _ => { };
|
|
||||||
|
|
||||||
internal NetworkManager Server { get; private set; }
|
internal NetworkManager Server { get; private set; }
|
||||||
|
|
||||||
@@ -24,23 +15,24 @@ namespace Unity.Netcode.RuntimeTests.Metrics.Utility
|
|||||||
|
|
||||||
internal NetworkMetrics ClientMetrics { get; private set; }
|
internal NetworkMetrics ClientMetrics { get; private set; }
|
||||||
|
|
||||||
[UnitySetUp]
|
protected override void OnServerAndClientsCreated()
|
||||||
public override IEnumerator Setup()
|
|
||||||
{
|
{
|
||||||
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, UpdatePlayerPrefab);
|
|
||||||
|
|
||||||
Server = m_ServerNetworkManager;
|
Server = m_ServerNetworkManager;
|
||||||
ServerMetrics = Server.NetworkMetrics as NetworkMetrics;
|
|
||||||
Client = m_ClientNetworkManagers[0];
|
Client = m_ClientNetworkManagers[0];
|
||||||
|
base.OnServerAndClientsCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerator OnStartedServerAndClients()
|
||||||
|
{
|
||||||
|
ServerMetrics = Server.NetworkMetrics as NetworkMetrics;
|
||||||
ClientMetrics = Client.NetworkMetrics as NetworkMetrics;
|
ClientMetrics = Client.NetworkMetrics as NetworkMetrics;
|
||||||
|
yield return base.OnStartedServerAndClients();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class DualClientMetricTestBase : BaseMultiInstanceTest
|
public abstract class DualClientMetricTestBase : NetcodeIntegrationTest
|
||||||
{
|
{
|
||||||
protected override int NbClients => 2;
|
protected override int NumberOfClients => 2;
|
||||||
|
|
||||||
protected virtual Action<GameObject> UpdatePlayerPrefab => _ => { };
|
|
||||||
|
|
||||||
internal NetworkManager Server { get; private set; }
|
internal NetworkManager Server { get; private set; }
|
||||||
|
|
||||||
@@ -54,17 +46,20 @@ namespace Unity.Netcode.RuntimeTests.Metrics.Utility
|
|||||||
|
|
||||||
internal NetworkMetrics SecondClientMetrics { get; private set; }
|
internal NetworkMetrics SecondClientMetrics { get; private set; }
|
||||||
|
|
||||||
[UnitySetUp]
|
protected override void OnServerAndClientsCreated()
|
||||||
public override IEnumerator Setup()
|
|
||||||
{
|
{
|
||||||
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, UpdatePlayerPrefab);
|
|
||||||
|
|
||||||
Server = m_ServerNetworkManager;
|
Server = m_ServerNetworkManager;
|
||||||
ServerMetrics = Server.NetworkMetrics as NetworkMetrics;
|
|
||||||
FirstClient = m_ClientNetworkManagers[0];
|
FirstClient = m_ClientNetworkManagers[0];
|
||||||
|
SecondClient = m_ClientNetworkManagers[1];
|
||||||
|
base.OnServerAndClientsCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerator OnStartedServerAndClients()
|
||||||
|
{
|
||||||
|
ServerMetrics = Server.NetworkMetrics as NetworkMetrics;
|
||||||
FirstClientMetrics = FirstClient.NetworkMetrics as NetworkMetrics;
|
FirstClientMetrics = FirstClient.NetworkMetrics as NetworkMetrics;
|
||||||
SecondClient = m_ClientNetworkManagers[0];
|
|
||||||
SecondClientMetrics = SecondClient.NetworkMetrics as NetworkMetrics;
|
SecondClientMetrics = SecondClient.NetworkMetrics as NetworkMetrics;
|
||||||
|
yield return base.OnStartedServerAndClients();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#if MULTIPLAYER_TOOLS
|
#if MULTIPLAYER_TOOLS
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode.RuntimeTests.Metrics.Utility
|
namespace Unity.Netcode.TestHelpers.Runtime.Metrics
|
||||||
{
|
{
|
||||||
public class NetworkVariableComponent : NetworkBehaviour
|
public class NetworkVariableComponent : NetworkBehaviour
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Unity.Netcode.RuntimeTests.Metrics.Utility
|
namespace Unity.Netcode.TestHelpers.Runtime.Metrics
|
||||||
{
|
{
|
||||||
public class RpcTestComponent : NetworkBehaviour
|
public class RpcTestComponent : NetworkBehaviour
|
||||||
{
|
{
|
||||||
50
TestHelpers/Runtime/Metrics/WaitForCounterMetricValue.cs
Normal file
50
TestHelpers/Runtime/Metrics/WaitForCounterMetricValue.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
using Unity.Multiplayer.Tools.MetricTypes;
|
||||||
|
using Unity.Multiplayer.Tools.NetStats;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.TestHelpers.Runtime.Metrics
|
||||||
|
{
|
||||||
|
internal class WaitForCounterMetricValue : WaitForMetricValues<Counter>
|
||||||
|
{
|
||||||
|
private long m_Value;
|
||||||
|
|
||||||
|
public delegate bool CounterFilter(long metric);
|
||||||
|
private CounterFilter m_CounterFilterDelegate;
|
||||||
|
|
||||||
|
public WaitForCounterMetricValue(IMetricDispatcher dispatcher, DirectionalMetricInfo directionalMetricName)
|
||||||
|
: base(dispatcher, directionalMetricName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public WaitForCounterMetricValue(IMetricDispatcher dispatcher, DirectionalMetricInfo directionalMetricName, CounterFilter counterFilter)
|
||||||
|
: this(dispatcher, directionalMetricName)
|
||||||
|
{
|
||||||
|
m_CounterFilterDelegate = counterFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long AssertMetricValueHaveBeenFound()
|
||||||
|
{
|
||||||
|
AssertHasError();
|
||||||
|
AssertIsFound();
|
||||||
|
|
||||||
|
return m_Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Observe(MetricCollection collection)
|
||||||
|
{
|
||||||
|
if (FindMetric(collection, out var metric))
|
||||||
|
{
|
||||||
|
var typedMetric = metric as Counter;
|
||||||
|
if (typedMetric == default)
|
||||||
|
{
|
||||||
|
SetError(metric);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Value = typedMetric.Value;
|
||||||
|
m_Found = m_CounterFilterDelegate != null ? m_CounterFilterDelegate(m_Value) : true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aa1d3026d48b43bfa4c76e253b08b3ae
|
||||||
|
timeCreated: 1644269156
|
||||||
60
TestHelpers/Runtime/Metrics/WaitForEventMetricValues.cs
Normal file
60
TestHelpers/Runtime/Metrics/WaitForEventMetricValues.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Unity.Multiplayer.Tools.MetricTypes;
|
||||||
|
using Unity.Multiplayer.Tools.NetStats;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.TestHelpers.Runtime.Metrics
|
||||||
|
{
|
||||||
|
internal class WaitForEventMetricValues<TMetric> : WaitForMetricValues<TMetric>
|
||||||
|
{
|
||||||
|
IReadOnlyCollection<TMetric> m_EventValues;
|
||||||
|
|
||||||
|
public delegate bool EventFilter(TMetric metric);
|
||||||
|
EventFilter m_EventFilterDelegate;
|
||||||
|
|
||||||
|
public WaitForEventMetricValues(IMetricDispatcher dispatcher, DirectionalMetricInfo directionalMetricName)
|
||||||
|
: base(dispatcher, directionalMetricName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public WaitForEventMetricValues(IMetricDispatcher dispatcher, DirectionalMetricInfo directionalMetricName, EventFilter eventFilter)
|
||||||
|
: this(dispatcher, directionalMetricName)
|
||||||
|
{
|
||||||
|
m_EventFilterDelegate = eventFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyCollection<TMetric> AssertMetricValuesHaveBeenFound()
|
||||||
|
{
|
||||||
|
AssertHasError();
|
||||||
|
AssertIsFound();
|
||||||
|
|
||||||
|
return m_EventValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Observe(MetricCollection collection)
|
||||||
|
{
|
||||||
|
if (FindMetric(collection, out var metric))
|
||||||
|
{
|
||||||
|
var typedMetric = metric as IEventMetric<TMetric>;
|
||||||
|
if (typedMetric == default)
|
||||||
|
{
|
||||||
|
SetError(metric);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typedMetric.Values.Any())
|
||||||
|
{
|
||||||
|
// Apply filter if one was provided
|
||||||
|
m_EventValues = m_EventFilterDelegate != null ? typedMetric.Values.Where(x => m_EventFilterDelegate(x)).ToList() : typedMetric.Values.ToList();
|
||||||
|
m_Found = m_EventValues.Count > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 319c55f92728431283c9e888d8f9d70e
|
||||||
|
timeCreated: 1644269156
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user