Compare commits
2 Commits
1.0.0-pre.
...
1.0.0-pre.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60e2dabef4 | ||
|
|
5b4aaa8b59 |
71
CHANGELOG.md
71
CHANGELOG.md
@@ -6,6 +6,72 @@ 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.7] - 2022-04-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added editor only check prior to entering into play mode if the currently open and active scene is in the build list and if not displays a dialog box asking the user if they would like to automatically add it prior to entering into play mode. (#1828)
|
||||||
|
- Added `UnityTransport` implementation and `com.unity.transport` package dependency (#1823)
|
||||||
|
- Added `NetworkVariableWritePermission` to `NetworkVariableBase` and implemented `Owner` client writable netvars. (#1762)
|
||||||
|
- `UnityTransport` settings can now be set programmatically. (#1845)
|
||||||
|
- `FastBufferWriter` and Reader IsInitialized property. (#1859)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated `UnityTransport` dependency on `com.unity.transport` to 1.0.0 (#1849)
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Removed `SnapshotSystem` (#1852)
|
||||||
|
- Removed `com.unity.modules.animation`, `com.unity.modules.physics` and `com.unity.modules.physics2d` dependencies from the package (#1812)
|
||||||
|
- Removed `com.unity.collections` dependency from the package (#1849)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed in-scene placed NetworkObjects not being found/ignored after a client disconnects and then reconnects. (#1850)
|
||||||
|
- Fixed issue where `UnityTransport` send queues were not flushed when calling `DisconnectLocalClient` or `DisconnectRemoteClient`. (#1847)
|
||||||
|
- Fixed NetworkBehaviour dependency verification check for an existing NetworkObject not searching from root parent transform relative GameObject. (#1841)
|
||||||
|
- Fixed issue where entries were not being removed from the NetworkSpawnManager.OwnershipToObjectsTable. (#1838)
|
||||||
|
- Fixed ClientRpcs would always send to all connected clients by default as opposed to only sending to the NetworkObject's Observers list by default. (#1836)
|
||||||
|
- Fixed clarity for NetworkSceneManager client side notification when it receives a scene hash value that does not exist in its local hash table. (#1828)
|
||||||
|
- Fixed client throws a key not found exception when it times out using UNet or UTP. (#1821)
|
||||||
|
- Fixed network variable updates are no longer limited to 32,768 bytes when NetworkConfig.EnsureNetworkVariableLengthSafety is enabled. The limits are now determined by what the transport can send in a message. (#1811)
|
||||||
|
- Fixed in-scene NetworkObjects get destroyed if a client fails to connect and shuts down the NetworkManager. (#1809)
|
||||||
|
- Fixed user never being notified in the editor that a NetworkBehaviour requires a NetworkObject to function properly. (#1808)
|
||||||
|
- Fixed PlayerObjects and dynamically spawned NetworkObjects not being added to the NetworkClient's OwnedObjects (#1801)
|
||||||
|
- Fixed issue where NetworkManager would continue starting even if the NetworkTransport selected failed. (#1780)
|
||||||
|
- Fixed issue when spawning new player if an already existing player exists it does not remove IsPlayer from the previous player (#1779)
|
||||||
|
- Fixed lack of notification that NetworkManager and NetworkObject cannot be added to the same GameObject with in-editor notifications (#1777)
|
||||||
|
- Fixed parenting warning printing for false positives (#1855)
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
- Fixed in-scene placed NetworkObjects not respawning after shutting down the NetworkManager and then starting it back up again (#1769)
|
||||||
|
|
||||||
## [1.0.0-pre.5] - 2022-01-26
|
## [1.0.0-pre.5] - 2022-01-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -15,12 +81,15 @@ Additional documentation and release notes are available at [Multiplayer Documen
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- NetworkManager's GameObject is no longer allowed to be nested under one or more GameObject(s).(#1484)
|
- NetworkManager's GameObject is no longer allowed to be nested under one or more GameObject(s).(#1484)
|
||||||
- NetworkManager DontDestroy property was removed and now NetworkManager always is migrated into the DontDestroyOnLoad scene. (#1484)
|
- NetworkManager DontDestroy property was removed and now NetworkManager always is migrated into the DontDestroyOnLoad scene. (#1484)'
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed network tick value sometimes being duplicated or skipped. (#1614)
|
- Fixed network tick value sometimes being duplicated or skipped. (#1614)
|
||||||
- Fixed The ClientNetworkTransform sample script to allow for owner changes at runtime. (#1606)
|
- 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
|
## [1.0.0-pre.4] - 2021-01-04
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -179,7 +186,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (t < 0.0f)
|
if (t < 0.0f)
|
||||||
{
|
{
|
||||||
throw new OverflowException($"t = {t} but must be >= 0. range {range}, RenderTime {renderTime}, Start time {m_StartTimeConsumed}, end time {m_EndTimeConsumed}");
|
// There is no mechanism to guarantee renderTime to not be before m_StartTimeConsumed
|
||||||
|
// This clamps t to a minimum of 0 and fixes issues with longer frames and pauses
|
||||||
|
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
|
{
|
||||||
|
NetworkLog.LogError($"renderTime was before m_StartTimeConsumed. This should never happen. {nameof(renderTime)} is {renderTime}, {nameof(m_StartTimeConsumed)} is {m_StartTimeConsumed}");
|
||||||
|
}
|
||||||
|
t = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t > 3.0f) // max extrapolation
|
if (t > 3.0f) // max extrapolation
|
||||||
@@ -211,6 +225,8 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
m_LastBufferedItemReceived = new BufferedItem(newMeasurement, sentTime);
|
m_LastBufferedItemReceived = new BufferedItem(newMeasurement, sentTime);
|
||||||
ResetTo(newMeasurement, sentTime);
|
ResetTo(newMeasurement, sentTime);
|
||||||
|
// Next line keeps renderTime above m_StartTimeConsumed. Fixes pause/unpause issues
|
||||||
|
m_Buffer.Add(m_LastBufferedItemReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
|
#if COM_UNITY_MODULES_ANIMATION
|
||||||
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
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A prototype component for syncing animations
|
/// NetworkAnimator enables remote synchronization of <see cref="UnityEngine.Animator"/> state for on network objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AddComponentMenu("Netcode/" + nameof(NetworkAnimator))]
|
[AddComponentMenu("Netcode/" + nameof(NetworkAnimator))]
|
||||||
[RequireComponent(typeof(Animator))]
|
[RequireComponent(typeof(Animator))]
|
||||||
@@ -14,24 +14,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 +44,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 +51,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 +70,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 +89,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 +101,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 +158,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 +284,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 +323,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 +344,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 +368,22 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="SetTrigger(string)" />
|
||||||
|
/// <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,18 +392,40 @@ 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 <see cref="SetTrigger(string)">SetTrigger</see> for more on how triggers are special
|
||||||
|
/// </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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ResetTrigger(string)" path="summary" />
|
||||||
|
/// <param name="hash">The hash for the trigger to activate</param>
|
||||||
public void ResetTrigger(int hash)
|
public void ResetTrigger(int hash)
|
||||||
{
|
{
|
||||||
SetTrigger(hash, true);
|
SetTrigger(hash, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_ANIMATION
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if COM_UNITY_MODULES_PHYSICS
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode.Components
|
namespace Unity.Netcode.Components
|
||||||
@@ -78,3 +79,4 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_PHYSICS
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#if COM_UNITY_MODULES_PHYSICS2D
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Unity.Netcode.Components
|
namespace Unity.Netcode.Components
|
||||||
@@ -78,3 +79,4 @@ namespace Unity.Netcode.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_PHYSICS2D
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -270,7 +272,7 @@ namespace Unity.Netcode.Components
|
|||||||
/// If using different values, please use RPCs to write to the server. Netcode doesn't support client side network variable writing
|
/// If using different values, please use RPCs to write to the server. Netcode doesn't support client side network variable writing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// This is public to make sure that users don't depend on this IsClient && IsOwner check in their code. If this logic changes in the future, we can make it invisible here
|
// This is public to make sure that users don't depend on this IsClient && IsOwner check in their code. If this logic changes in the future, we can make it invisible here
|
||||||
public bool CanCommitToTransform;
|
public bool CanCommitToTransform { get; protected set; }
|
||||||
protected bool m_CachedIsServer;
|
protected bool m_CachedIsServer;
|
||||||
protected NetworkManager m_CachedNetworkManager;
|
protected NetworkManager m_CachedNetworkManager;
|
||||||
|
|
||||||
@@ -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);
|
||||||
@@ -644,7 +691,6 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
if (!NetworkObject.IsSpawned)
|
if (!NetworkObject.IsSpawned)
|
||||||
{
|
{
|
||||||
// todo MTT-849 should never happen but yet it does! maybe revisit/dig after NetVar updates and snapshot system lands?
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,7 +702,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)
|
||||||
{
|
{
|
||||||
@@ -734,7 +784,7 @@ namespace Unity.Netcode.Components
|
|||||||
{
|
{
|
||||||
m_ReplicatedNetworkState.SetDirty(true);
|
m_ReplicatedNetworkState.SetDirty(true);
|
||||||
}
|
}
|
||||||
else
|
else if (m_Transform != null)
|
||||||
{
|
{
|
||||||
ApplyInterpolatedNetworkStateToTransform(m_ReplicatedNetworkState.Value, m_Transform);
|
ApplyInterpolatedNetworkStateToTransform(m_ReplicatedNetworkState.Value, m_Transform);
|
||||||
}
|
}
|
||||||
@@ -812,6 +862,17 @@ namespace Unity.Netcode.Components
|
|||||||
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 +892,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,22 @@
|
|||||||
"Unity.Netcode.Runtime",
|
"Unity.Netcode.Runtime",
|
||||||
"Unity.Collections"
|
"Unity.Collections"
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
|
||||||
"excludePlatforms": [],
|
|
||||||
"allowUnsafeCode": true,
|
"allowUnsafeCode": true,
|
||||||
"overrideReferences": false,
|
"versionDefines": [
|
||||||
"precompiledReferences": [],
|
{
|
||||||
"autoReferenced": true,
|
"name": "com.unity.modules.animation",
|
||||||
"defineConstraints": [],
|
"expression": "",
|
||||||
"versionDefines": [],
|
"define": "COM_UNITY_MODULES_ANIMATION"
|
||||||
"noEngineReferences": false
|
},
|
||||||
|
{
|
||||||
|
"name": "com.unity.modules.physics",
|
||||||
|
"expression": "",
|
||||||
|
"define": "COM_UNITY_MODULES_PHYSICS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "com.unity.modules.physics2d",
|
||||||
|
"expression": "",
|
||||||
|
"define": "COM_UNITY_MODULES_PHYSICS2D"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,17 +306,12 @@ 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)
|
||||||
{
|
{
|
||||||
var assembly = m_AssemblyResolver.Resolve(reference);
|
var assembly = m_AssemblyResolver.Resolve(reference);
|
||||||
@@ -319,8 +321,7 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -332,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)
|
||||||
@@ -356,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));
|
||||||
}
|
}
|
||||||
@@ -395,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)
|
||||||
{
|
{
|
||||||
@@ -477,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;
|
||||||
@@ -521,7 +531,6 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
|
|
||||||
if (isValid)
|
if (isValid)
|
||||||
{
|
{
|
||||||
isServerRpc = customAttributeType_FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
|
|
||||||
rpcAttribute = customAttribute;
|
rpcAttribute = customAttribute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -569,12 +578,12 @@ 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)
|
||||||
@@ -584,17 +593,15 @@ 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.Name == "ValueType" && !checkType.IsValueType))
|
||||||
|| (resolvedConstraint.IsClass &&
|
|
||||||
!checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName))
|
|
||||||
|| (resolvedConstraint.Name == "ValueType" && !checkType.IsValueType))
|
|
||||||
{
|
{
|
||||||
meetsConstraints = false;
|
meetsConstraints = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meetsConstraints)
|
if (meetsConstraints)
|
||||||
{
|
{
|
||||||
var instanceMethod = new GenericInstanceMethod(method);
|
var instanceMethod = new GenericInstanceMethod(method);
|
||||||
@@ -624,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;
|
||||||
@@ -635,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;
|
||||||
@@ -707,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);
|
||||||
@@ -736,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;
|
||||||
@@ -772,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)
|
||||||
@@ -797,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)
|
||||||
@@ -854,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
|
||||||
@@ -867,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));
|
||||||
@@ -886,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);
|
||||||
@@ -895,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -942,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));
|
||||||
|
|
||||||
@@ -956,11 +1014,11 @@ 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
|
||||||
var method = methodRef.Resolve();
|
var method = methodRef.Resolve();
|
||||||
var checkParameter = method.Parameters[0];
|
var checkParameter = method.Parameters[0];
|
||||||
var isExtensionMethod = false;
|
var isExtensionMethod = false;
|
||||||
@@ -971,11 +1029,11 @@ namespace Unity.Netcode.Editor.CodeGen
|
|||||||
}
|
}
|
||||||
if (!isExtensionMethod || method.Parameters[0].ParameterType.IsByReference)
|
if (!isExtensionMethod || method.Parameters[0].ParameterType.IsByReference)
|
||||||
{
|
{
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx));
|
instructions.Add(processor.Create(OpCodes.Ldloc, bufWriterLocIdx));
|
||||||
}
|
}
|
||||||
if (checkParameter.IsIn || checkParameter.IsOut || checkParameter.ParameterType.IsByReference)
|
if (checkParameter.IsIn || checkParameter.IsOut || checkParameter.ParameterType.IsByReference)
|
||||||
{
|
{
|
||||||
@@ -986,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));
|
||||||
}
|
}
|
||||||
@@ -1015,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
|
||||||
@@ -1039,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
|
||||||
@@ -1068,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);
|
||||||
@@ -1132,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)
|
||||||
@@ -1161,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);
|
||||||
@@ -1233,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
|
||||||
@@ -1268,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);
|
||||||
@@ -1336,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,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 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Unity.Netcode.Components;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEditor.Animations;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
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)]
|
|
||||||
[CanEditMultipleObjects]
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
Init();
|
|
||||||
serializedObject.Update();
|
|
||||||
DrawControls();
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawControls()
|
|
||||||
{
|
|
||||||
EditorGUI.BeginChangeCheck();
|
|
||||||
EditorGUILayout.PropertyField(m_AnimatorProperty, m_AnimatorLabel);
|
|
||||||
if (EditorGUI.EndChangeCheck())
|
|
||||||
{
|
|
||||||
m_AnimSync.ResetParameterOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_AnimSync.Animator == null)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -211,5 +211,91 @@ namespace Unity.Netcode.Editor
|
|||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
EditorGUI.EndChangeCheck();
|
EditorGUI.EndChangeCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked once when a NetworkBehaviour component is
|
||||||
|
/// displayed in the inspector view.
|
||||||
|
/// </summary>
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
// When we first add a NetworkBehaviour this editor will be enabled
|
||||||
|
// so we go ahead and check for an already existing NetworkObject here
|
||||||
|
CheckForNetworkObject((target as NetworkBehaviour).gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal const string AutoAddNetworkObjectIfNoneExists = "AutoAdd-NetworkObject-When-None-Exist";
|
||||||
|
|
||||||
|
public static Transform GetRootParentTransform(Transform transform)
|
||||||
|
{
|
||||||
|
if (transform.parent == null || transform.parent == transform)
|
||||||
|
{
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
return GetRootParentTransform(transform.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to determine if a GameObject has one or more NetworkBehaviours but
|
||||||
|
/// does not already have a NetworkObject component. If not it will notify
|
||||||
|
/// the user that NetworkBehaviours require a NetworkObject.
|
||||||
|
/// </summary>
|
||||||
|
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
|
||||||
|
{
|
||||||
|
// If there are no NetworkBehaviours or no gameObject, then exit early
|
||||||
|
if (gameObject == null || (gameObject.GetComponent<NetworkBehaviour>() == null && gameObject.GetComponentInChildren<NetworkBehaviour>() == null))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now get the root parent transform to the current GameObject (or itself)
|
||||||
|
var rootTransform = GetRootParentTransform(gameObject.transform);
|
||||||
|
|
||||||
|
// Otherwise, check to see if there is any NetworkObject from the root GameObject down to all children.
|
||||||
|
// If not, notify the user that NetworkBehaviours require that the relative GameObject has a NetworkObject component.
|
||||||
|
var networkObject = rootTransform.GetComponent<NetworkObject>();
|
||||||
|
if (networkObject == null)
|
||||||
|
{
|
||||||
|
networkObject = rootTransform.GetComponentInChildren<NetworkObject>();
|
||||||
|
|
||||||
|
if (networkObject == null)
|
||||||
|
{
|
||||||
|
// If we are removing a NetworkObject but there is still one or more NetworkBehaviour components
|
||||||
|
// and the user has already turned "Auto-Add NetworkObject" on when first notified about the requirement
|
||||||
|
// then just send a reminder to the user why the NetworkObject they just deleted seemingly "re-appeared"
|
||||||
|
// again.
|
||||||
|
if (networkObjectRemoved && EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists) && EditorPrefs.GetBool(AutoAddNetworkObjectIfNoneExists))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"{gameObject.name} still has {nameof(NetworkBehaviour)}s and Auto-Add NetworkObjects is enabled. A NetworkObject is being added back to {gameObject.name}.");
|
||||||
|
Debug.Log($"To reset Auto-Add NetworkObjects: Select the Netcode->General->Reset Auto-Add NetworkObject menu item.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify and provide the option to add it one time, always add a NetworkObject, or do nothing and let the user manually add it
|
||||||
|
if (EditorUtility.DisplayDialog($"{nameof(NetworkBehaviour)}s require a {nameof(NetworkObject)}",
|
||||||
|
$"{gameObject.name} does not have a {nameof(NetworkObject)} component. Would you like to add one now?", "Yes", "No (manually add it)",
|
||||||
|
DialogOptOutDecisionType.ForThisMachine, AutoAddNetworkObjectIfNoneExists))
|
||||||
|
{
|
||||||
|
gameObject.AddComponent<NetworkObject>();
|
||||||
|
var activeScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||||
|
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(activeScene);
|
||||||
|
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(activeScene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This allows users to reset the Auto-Add NetworkObject preference
|
||||||
|
/// so the next time they add a NetworkBehaviour to a GameObject without
|
||||||
|
/// a NetworkObject it will display the dialog box again and not
|
||||||
|
/// automatically add a NetworkObject.
|
||||||
|
/// </summary>
|
||||||
|
[MenuItem("Netcode/General/Reset Auto-Add NetworkObject", false, 1)]
|
||||||
|
private static void ResetMultiplayerToolsTipStatus()
|
||||||
|
{
|
||||||
|
if (EditorPrefs.HasKey(AutoAddNetworkObjectIfNoneExists))
|
||||||
|
{
|
||||||
|
EditorPrefs.SetBool(AutoAddNetworkObjectIfNoneExists, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,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);
|
||||||
}
|
}
|
||||||
@@ -135,9 +135,13 @@ namespace Unity.Netcode.Editor
|
|||||||
m_NetworkPrefabsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.NetworkPrefabs)), true, true, true, true);
|
m_NetworkPrefabsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.NetworkPrefabs)), true, true, true, true);
|
||||||
m_NetworkPrefabsList.elementHeightCallback = index =>
|
m_NetworkPrefabsList.elementHeightCallback = index =>
|
||||||
{
|
{
|
||||||
var networkPrefab = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index);
|
var networkOverrideInt = 0;
|
||||||
var networkOverrideProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Override));
|
if (m_NetworkPrefabsList.count > 0)
|
||||||
var networkOverrideInt = networkOverrideProp.enumValueIndex;
|
{
|
||||||
|
var networkPrefab = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index);
|
||||||
|
var networkOverrideProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Override));
|
||||||
|
networkOverrideInt = networkOverrideProp.enumValueIndex;
|
||||||
|
}
|
||||||
|
|
||||||
return 8 + (networkOverrideInt == 0 ? EditorGUIUtility.singleLineHeight : (EditorGUIUtility.singleLineHeight * 2) + 5);
|
return 8 + (networkOverrideInt == 0 ? EditorGUIUtility.singleLineHeight : (EditorGUIUtility.singleLineHeight * 2) + 5);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
namespace Unity.Netcode.Editor
|
||||||
@@ -12,7 +14,7 @@ namespace Unity.Netcode.Editor
|
|||||||
{
|
{
|
||||||
internal static NetworkManagerHelper Singleton;
|
internal static NetworkManagerHelper Singleton;
|
||||||
|
|
||||||
// This is primarily to handle multiInstance scenarios where more than 1 NetworkManager could exist
|
// 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>();
|
private static Dictionary<NetworkManager, Transform> s_LastKnownNetworkManagerParents = new Dictionary<NetworkManager, Transform>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -25,7 +27,6 @@ namespace Unity.Netcode.Editor
|
|||||||
{
|
{
|
||||||
Singleton = new NetworkManagerHelper();
|
Singleton = new NetworkManagerHelper();
|
||||||
NetworkManager.NetworkManagerHelper = Singleton;
|
NetworkManager.NetworkManagerHelper = Singleton;
|
||||||
|
|
||||||
EditorApplication.playModeStateChanged -= EditorApplication_playModeStateChanged;
|
EditorApplication.playModeStateChanged -= EditorApplication_playModeStateChanged;
|
||||||
EditorApplication.hierarchyChanged -= EditorApplication_hierarchyChanged;
|
EditorApplication.hierarchyChanged -= EditorApplication_hierarchyChanged;
|
||||||
|
|
||||||
@@ -40,20 +41,106 @@ namespace Unity.Netcode.Editor
|
|||||||
case PlayModeStateChange.ExitingEditMode:
|
case PlayModeStateChange.ExitingEditMode:
|
||||||
{
|
{
|
||||||
s_LastKnownNetworkManagerParents.Clear();
|
s_LastKnownNetworkManagerParents.Clear();
|
||||||
|
ScenesInBuildActiveSceneCheck();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detects if a user is trying to enter into play mode when both conditions are true:
|
||||||
|
/// - the currently active and open scene is not added to the scenes in build list
|
||||||
|
/// - an instance of a NetworkManager with scene management enabled can be found
|
||||||
|
/// If both conditions are met then the user is presented with a dialog box that
|
||||||
|
/// provides the user with the option to add the scene to the scenes in build list
|
||||||
|
/// before entering into play mode or the user can continue under those conditions.
|
||||||
|
///
|
||||||
|
/// ** When scene management is enabled the user should treat all scenes that need to
|
||||||
|
/// be synchronized using network scene management as if they were preparing for a build.
|
||||||
|
/// Any scene that needs to be loaded at run time has to be included in the scenes in
|
||||||
|
/// build list. **
|
||||||
|
/// </summary>
|
||||||
|
private static void ScenesInBuildActiveSceneCheck()
|
||||||
|
{
|
||||||
|
var scenesList = EditorBuildSettings.scenes.ToList();
|
||||||
|
var activeScene = SceneManager.GetActiveScene();
|
||||||
|
var isSceneInBuildSettings = scenesList.Where((c) => c.path == activeScene.path).Count() == 1;
|
||||||
|
var networkManager = Object.FindObjectOfType<NetworkManager>();
|
||||||
|
if (!isSceneInBuildSettings && networkManager != null)
|
||||||
|
{
|
||||||
|
if (networkManager.NetworkConfig != null && networkManager.NetworkConfig.EnableSceneManagement)
|
||||||
|
{
|
||||||
|
if (EditorUtility.DisplayDialog("Add Scene to Scenes in Build", $"The current scene was not found in the scenes" +
|
||||||
|
$" in build and a {nameof(NetworkManager)} instance was found with scene management enabled! Clients will not be able " +
|
||||||
|
$"to synchronize to this scene unless it is added to the scenes in build list.\n\nWould you like to add it now?",
|
||||||
|
"Yes", "No - Continue"))
|
||||||
|
{
|
||||||
|
scenesList.Add(new EditorBuildSettingsScene(activeScene.path, true));
|
||||||
|
EditorBuildSettings.scenes = scenesList.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked only when the hierarchy changes
|
||||||
|
/// </summary>
|
||||||
private static void EditorApplication_hierarchyChanged()
|
private static void EditorApplication_hierarchyChanged()
|
||||||
{
|
{
|
||||||
var allNetworkManagers = Resources.FindObjectsOfTypeAll<NetworkManager>();
|
var allNetworkManagers = Resources.FindObjectsOfTypeAll<NetworkManager>();
|
||||||
foreach (var networkManager in allNetworkManagers)
|
foreach (var networkManager in allNetworkManagers)
|
||||||
{
|
{
|
||||||
networkManager.NetworkManagerCheckForParent();
|
if (!networkManager.NetworkManagerCheckForParent())
|
||||||
|
{
|
||||||
|
Singleton.CheckAndNotifyUserNetworkObjectRemoved(networkManager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles notifying users that they cannot add a NetworkObject component
|
||||||
|
/// to a GameObject that also has a NetworkManager component. The NetworkObject
|
||||||
|
/// will always be removed.
|
||||||
|
/// GameObject + NetworkObject then NetworkManager = NetworkObject removed
|
||||||
|
/// GameObject + NetworkManager then NetworkObject = NetworkObject removed
|
||||||
|
/// Note: Since this is always invoked after <see cref="NetworkManagerCheckForParent"/>
|
||||||
|
/// we do not need to check for parent when searching for a NetworkObject component
|
||||||
|
/// </summary>
|
||||||
|
public void CheckAndNotifyUserNetworkObjectRemoved(NetworkManager networkManager, bool editorTest = false)
|
||||||
|
{
|
||||||
|
// Check for any NetworkObject at the same gameObject relative layer
|
||||||
|
var networkObject = networkManager.gameObject.GetComponent<NetworkObject>();
|
||||||
|
|
||||||
|
if (networkObject == null)
|
||||||
|
{
|
||||||
|
// if none is found, check to see if any children have a NetworkObject
|
||||||
|
networkObject = networkManager.gameObject.GetComponentInChildren<NetworkObject>();
|
||||||
|
if (networkObject == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EditorApplication.isUpdating)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(networkObject);
|
||||||
|
|
||||||
|
if (!EditorApplication.isPlaying && !editorTest)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog($"Removing {nameof(NetworkObject)}", NetworkManagerAndNetworkObjectNotAllowedMessage(), "OK");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError(NetworkManagerAndNetworkObjectNotAllowedMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NetworkManagerAndNetworkObjectNotAllowedMessage()
|
||||||
|
{
|
||||||
|
return $"A {nameof(GameObject)} cannot have both a {nameof(NetworkManager)} and {nameof(NetworkObject)} assigned to it or any children under it.";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles notifying the user, via display dialog window, that they have nested a NetworkManager.
|
/// 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 edit mode it provides the option to automatically fix the issue
|
||||||
|
|||||||
@@ -100,5 +100,32 @@ namespace Unity.Netcode.Editor
|
|||||||
GUI.enabled = guiEnabled;
|
GUI.enabled = guiEnabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Saved for use in OnDestroy
|
||||||
|
private GameObject m_GameObject;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked once when a NetworkObject component is
|
||||||
|
/// displayed in the inspector view.
|
||||||
|
/// </summary>
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
// We set the GameObject upon being enabled because when the
|
||||||
|
// NetworkObject component is removed (i.e. when OnDestroy is invoked)
|
||||||
|
// it is no longer valid/available.
|
||||||
|
m_GameObject = (target as NetworkObject).gameObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked once when a NetworkObject component is
|
||||||
|
/// no longer displayed in the inspector view.
|
||||||
|
/// </summary>
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
// Since this is also invoked when a NetworkObject component is removed
|
||||||
|
// from a GameObject, we go ahead and check for a NetworkObject when
|
||||||
|
// this custom editor is destroyed.
|
||||||
|
NetworkBehaviourEditor.CheckForNetworkObject(m_GameObject, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Unity.Netcode.Components;
|
|||||||
|
|
||||||
namespace Unity.Netcode.Editor
|
namespace Unity.Netcode.Editor
|
||||||
{
|
{
|
||||||
[CustomEditor(typeof(NetworkTransform))]
|
[CustomEditor(typeof(NetworkTransform), true)]
|
||||||
public class NetworkTransformEditor : UnityEditor.Editor
|
public class NetworkTransformEditor : UnityEditor.Editor
|
||||||
{
|
{
|
||||||
private SerializedProperty m_SyncPositionXProperty;
|
private SerializedProperty m_SyncPositionXProperty;
|
||||||
@@ -112,6 +112,7 @@ namespace Unity.Netcode.Editor
|
|||||||
EditorGUILayout.PropertyField(m_InLocalSpaceProperty);
|
EditorGUILayout.PropertyField(m_InLocalSpaceProperty);
|
||||||
EditorGUILayout.PropertyField(m_InterpolateProperty);
|
EditorGUILayout.PropertyField(m_InterpolateProperty);
|
||||||
|
|
||||||
|
#if COM_UNITY_MODULES_PHYSICS
|
||||||
// if rigidbody is present but network rigidbody is not present
|
// if rigidbody is present but network rigidbody is not present
|
||||||
var go = ((NetworkTransform)target).gameObject;
|
var go = ((NetworkTransform)target).gameObject;
|
||||||
if (go.TryGetComponent<Rigidbody>(out _) && go.TryGetComponent<NetworkRigidbody>(out _) == false)
|
if (go.TryGetComponent<Rigidbody>(out _) && go.TryGetComponent<NetworkRigidbody>(out _) == false)
|
||||||
@@ -119,12 +120,15 @@ namespace Unity.Netcode.Editor
|
|||||||
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody but no NetworkRigidbody.\n" +
|
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody but no NetworkRigidbody.\n" +
|
||||||
"Add a NetworkRigidbody component to improve Rigidbody synchronization.", MessageType.Warning);
|
"Add a NetworkRigidbody component to improve Rigidbody synchronization.", MessageType.Warning);
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_PHYSICS
|
||||||
|
|
||||||
|
#if COM_UNITY_MODULES_PHYSICS2D
|
||||||
if (go.TryGetComponent<Rigidbody2D>(out _) && go.TryGetComponent<NetworkRigidbody2D>(out _) == false)
|
if (go.TryGetComponent<Rigidbody2D>(out _) && go.TryGetComponent<NetworkRigidbody2D>(out _) == false)
|
||||||
{
|
{
|
||||||
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody2D but no NetworkRigidbody2D.\n" +
|
EditorGUILayout.HelpBox("This GameObject contains a Rigidbody2D but no NetworkRigidbody2D.\n" +
|
||||||
"Add a NetworkRigidbody2D component to improve Rigidbody2D synchronization.", MessageType.Warning);
|
"Add a NetworkRigidbody2D component to improve Rigidbody2D synchronization.", MessageType.Warning);
|
||||||
}
|
}
|
||||||
|
#endif // COM_UNITY_MODULES_PHYSICS2D
|
||||||
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: fb1b6e801936c7f4a9af28dbed5ea2ff
|
guid: a325130169714440ba1b4878082e8956
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
53
Editor/PackageChecker/UTPAdapterChecker.cs
Normal file
53
Editor/PackageChecker/UTPAdapterChecker.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#if COM_UNITY_NETCODE_ADAPTER_UTP
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.PackageManager;
|
||||||
|
using UnityEditor.PackageManager.Requests;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Editor.PackageChecker
|
||||||
|
{
|
||||||
|
[InitializeOnLoad]
|
||||||
|
internal class UTPAdapterChecker
|
||||||
|
{
|
||||||
|
private const string k_UTPAdapterPackageName = "com.unity.netcode.adapter.utp";
|
||||||
|
|
||||||
|
private static ListRequest s_ListRequest = null;
|
||||||
|
|
||||||
|
static UTPAdapterChecker()
|
||||||
|
{
|
||||||
|
if (s_ListRequest == null)
|
||||||
|
{
|
||||||
|
s_ListRequest = Client.List();
|
||||||
|
EditorApplication.update += EditorUpdate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EditorUpdate()
|
||||||
|
{
|
||||||
|
if (!s_ListRequest.IsCompleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorApplication.update -= EditorUpdate;
|
||||||
|
|
||||||
|
if (s_ListRequest.Status == StatusCode.Success)
|
||||||
|
{
|
||||||
|
if (s_ListRequest.Result.Any(p => p.name == k_UTPAdapterPackageName))
|
||||||
|
{
|
||||||
|
Debug.Log($"({nameof(UTPAdapterChecker)}) Found UTP Adapter package, it is no longer needed, `UnityTransport` is now directly integrated into the SDK therefore removing it from the project.");
|
||||||
|
Client.Remove(k_UTPAdapterPackageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var error = s_ListRequest.Error;
|
||||||
|
Debug.LogError($"({nameof(UTPAdapterChecker)}) Cannot check the list of packages -> error #{error.errorCode}: {error.message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
s_ListRequest = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // COM_UNITY_NETCODE_ADAPTER_UTP
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: bd9e1475e8c8e4a6d935fe2409e3bd26
|
guid: df5ed97df956b4aad91a221ba59fa304
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "Unity.Netcode.Editor.PackageChecker",
|
||||||
|
"rootNamespace": "Unity.Netcode.Editor.PackageChecker",
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"versionDefines": [
|
||||||
|
{
|
||||||
|
"name": "com.unity.netcode.adapter.utp",
|
||||||
|
"expression": "",
|
||||||
|
"define": "COM_UNITY_NETCODE_ADAPTER_UTP"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: de64d7f9ca85d4bf59c8c24738bc1057
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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,3 +1,5 @@
|
|||||||
|
# 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)
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ 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")]
|
||||||
|
|||||||
@@ -138,19 +138,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableNetworkLogs = true;
|
public bool EnableNetworkLogs = true;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether or not to enable Snapshot System for variable updates. Not supported in this version.
|
|
||||||
/// </summary>
|
|
||||||
public bool UseSnapshotDelta { get; internal set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// Whether or not to enable Snapshot System for spawn and despawn commands. Not supported in this version.
|
|
||||||
/// </summary>
|
|
||||||
public bool UseSnapshotSpawn { get; internal set; } = false;
|
|
||||||
/// <summary>
|
|
||||||
/// When Snapshot System spawn is enabled: max size of Snapshot Messages. Meant to fit MTU.
|
|
||||||
/// </summary>
|
|
||||||
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)
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -239,6 +226,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);
|
||||||
|
|||||||
@@ -20,6 +20,17 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The NetworkObject's owned by this Client
|
/// The NetworkObject's owned by this Client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly List<NetworkObject> OwnedObjects = new List<NetworkObject>();
|
public List<NetworkObject> OwnedObjects
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (PlayerObject != null && PlayerObject.NetworkManager != null && PlayerObject.NetworkManager.IsListening)
|
||||||
|
{
|
||||||
|
return PlayerObject.NetworkManager.SpawnManager.GetClientOwnedObjects(ClientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<NetworkObject>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,388 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal struct IndexAllocatorEntry
|
|
||||||
{
|
|
||||||
internal int Pos; // Position where the memory of this slot is
|
|
||||||
internal int Length; // Length of the memory allocated to this slot
|
|
||||||
internal int Next; // Next and Prev define the order of the slots in the buffer
|
|
||||||
internal int Prev;
|
|
||||||
internal bool Free; // Whether this is a free slot
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class IndexAllocator
|
|
||||||
{
|
|
||||||
private const int k_NotSet = -1;
|
|
||||||
private readonly int m_MaxSlot; // Maximum number of sections (free or not) in the buffer
|
|
||||||
private readonly int m_BufferSize; // Size of the buffer we allocated into
|
|
||||||
private int m_LastSlot = 0; // Last allocated slot
|
|
||||||
private IndexAllocatorEntry[] m_Slots; // Array of slots
|
|
||||||
private int[] m_IndexToSlot; // Mapping from the client's index to the slot index
|
|
||||||
|
|
||||||
internal IndexAllocator(int bufferSize, int maxSlot)
|
|
||||||
{
|
|
||||||
m_MaxSlot = maxSlot;
|
|
||||||
m_BufferSize = bufferSize;
|
|
||||||
m_Slots = new IndexAllocatorEntry[m_MaxSlot];
|
|
||||||
m_IndexToSlot = new int[m_MaxSlot];
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reset this IndexAllocator to an empty one, with the same sized buffer and slots
|
|
||||||
/// </summary>
|
|
||||||
internal void Reset()
|
|
||||||
{
|
|
||||||
// todo: could be made faster, for example by having a last index
|
|
||||||
// and not needing valid stuff past it
|
|
||||||
for (int i = 0; i < m_MaxSlot; i++)
|
|
||||||
{
|
|
||||||
m_Slots[i].Free = true;
|
|
||||||
m_Slots[i].Next = i + 1;
|
|
||||||
m_Slots[i].Prev = i - 1;
|
|
||||||
m_Slots[i].Pos = m_BufferSize;
|
|
||||||
m_Slots[i].Length = 0;
|
|
||||||
|
|
||||||
m_IndexToSlot[i] = k_NotSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Slots[0].Pos = 0;
|
|
||||||
m_Slots[0].Length = m_BufferSize;
|
|
||||||
m_Slots[0].Prev = k_NotSet;
|
|
||||||
m_Slots[m_MaxSlot - 1].Next = k_NotSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the amount of memory used
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// Returns the amount of memory used, starting at 0, ending after the last used slot
|
|
||||||
/// </returns>
|
|
||||||
internal int Range
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
// when the whole buffer is free, m_LastSlot points to an empty slot
|
|
||||||
if (m_Slots[m_LastSlot].Free)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// otherwise return the end of the last slot used
|
|
||||||
return m_Slots[m_LastSlot].Pos + m_Slots[m_LastSlot].Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allocate a slot with "size" position, for index "index"
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The client index to identify this. Used in Deallocate to identify which slot</param>
|
|
||||||
/// <param name="size">The size required. </param>
|
|
||||||
/// <param name="pos">Returns the position to use in the buffer </param>
|
|
||||||
/// <returns>
|
|
||||||
/// true if successful, false is there isn't enough memory available or no slots are large enough
|
|
||||||
/// </returns>
|
|
||||||
internal bool Allocate(int index, int size, out int pos)
|
|
||||||
{
|
|
||||||
pos = 0;
|
|
||||||
// size must be positive, index must be within range
|
|
||||||
if (size < 0 || index < 0 || index >= m_MaxSlot)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// refuse allocation if the index is already in use
|
|
||||||
if (m_IndexToSlot[index] != k_NotSet)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: this is the slowest part
|
|
||||||
// improvement 1: list of free blocks (minor)
|
|
||||||
// improvement 2: heap of free blocks
|
|
||||||
for (int i = 0; i < m_MaxSlot; i++)
|
|
||||||
{
|
|
||||||
if (m_Slots[i].Free && m_Slots[i].Length >= size)
|
|
||||||
{
|
|
||||||
m_IndexToSlot[index] = i;
|
|
||||||
|
|
||||||
int leftOver = m_Slots[i].Length - size;
|
|
||||||
int next = m_Slots[i].Next;
|
|
||||||
if (m_Slots[next].Free)
|
|
||||||
{
|
|
||||||
m_Slots[next].Pos -= leftOver;
|
|
||||||
m_Slots[next].Length += leftOver;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int add = MoveSlotAfter(i);
|
|
||||||
|
|
||||||
m_Slots[add].Pos = m_Slots[i].Pos + size;
|
|
||||||
m_Slots[add].Length = m_Slots[i].Length - size;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Slots[i].Free = false;
|
|
||||||
m_Slots[i].Length = size;
|
|
||||||
|
|
||||||
pos = m_Slots[i].Pos;
|
|
||||||
|
|
||||||
// if we allocate past the current range, we are the last slot
|
|
||||||
if (m_Slots[i].Pos + m_Slots[i].Length > Range)
|
|
||||||
{
|
|
||||||
m_LastSlot = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deallocate a slot
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The client index to identify this. Same index used in Allocate </param>
|
|
||||||
/// <returns>
|
|
||||||
/// true if successful, false is there isn't an allocated slot at this index
|
|
||||||
/// </returns>
|
|
||||||
internal bool Deallocate(int index)
|
|
||||||
{
|
|
||||||
// size must be positive, index must be within range
|
|
||||||
if (index < 0 || index >= m_MaxSlot)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int slot = m_IndexToSlot[index];
|
|
||||||
|
|
||||||
if (slot == k_NotSet)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Slots[slot].Free)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Slots[slot].Free = true;
|
|
||||||
|
|
||||||
int prev = m_Slots[slot].Prev;
|
|
||||||
int next = m_Slots[slot].Next;
|
|
||||||
|
|
||||||
// if previous slot was free, merge and grow
|
|
||||||
if (prev != k_NotSet && m_Slots[prev].Free)
|
|
||||||
{
|
|
||||||
m_Slots[prev].Length += m_Slots[slot].Length;
|
|
||||||
m_Slots[slot].Length = 0;
|
|
||||||
|
|
||||||
// if the slot we're merging was the last one, the last one is now the one we merged with
|
|
||||||
if (slot == m_LastSlot)
|
|
||||||
{
|
|
||||||
m_LastSlot = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: verify what this does on full or nearly full cases
|
|
||||||
MoveSlotToEnd(slot);
|
|
||||||
slot = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
next = m_Slots[slot].Next;
|
|
||||||
|
|
||||||
// merge with next slot if it is free
|
|
||||||
if (next != k_NotSet && m_Slots[next].Free)
|
|
||||||
{
|
|
||||||
m_Slots[slot].Length += m_Slots[next].Length;
|
|
||||||
m_Slots[next].Length = 0;
|
|
||||||
MoveSlotToEnd(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we just deallocate the last one, we need to move last back
|
|
||||||
if (slot == m_LastSlot)
|
|
||||||
{
|
|
||||||
m_LastSlot = m_Slots[m_LastSlot].Prev;
|
|
||||||
// if there's nothing allocated anymore, use 0
|
|
||||||
if (m_LastSlot == k_NotSet)
|
|
||||||
{
|
|
||||||
m_LastSlot = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark the index as available
|
|
||||||
m_IndexToSlot[index] = k_NotSet;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take a slot at the end and link it to go just after "slot".
|
|
||||||
// Used when allocating part of a slot and we need an entry for the rest
|
|
||||||
// Returns the slot that was picked
|
|
||||||
private int MoveSlotAfter(int slot)
|
|
||||||
{
|
|
||||||
int ret = m_Slots[m_MaxSlot - 1].Prev;
|
|
||||||
int p0 = m_Slots[ret].Prev;
|
|
||||||
|
|
||||||
m_Slots[p0].Next = m_MaxSlot - 1;
|
|
||||||
m_Slots[m_MaxSlot - 1].Prev = p0;
|
|
||||||
|
|
||||||
int p1 = m_Slots[slot].Next;
|
|
||||||
m_Slots[slot].Next = ret;
|
|
||||||
m_Slots[p1].Prev = ret;
|
|
||||||
|
|
||||||
m_Slots[ret].Prev = slot;
|
|
||||||
m_Slots[ret].Next = p1;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the slot "slot" to the end of the list.
|
|
||||||
// Used when merging two slots, that gives us an extra entry at the end
|
|
||||||
private void MoveSlotToEnd(int slot)
|
|
||||||
{
|
|
||||||
// if we're already there
|
|
||||||
if (m_Slots[slot].Next == k_NotSet)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int prev = m_Slots[slot].Prev;
|
|
||||||
int next = m_Slots[slot].Next;
|
|
||||||
|
|
||||||
m_Slots[prev].Next = next;
|
|
||||||
if (next != k_NotSet)
|
|
||||||
{
|
|
||||||
m_Slots[next].Prev = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
int p0 = m_Slots[m_MaxSlot - 1].Prev;
|
|
||||||
|
|
||||||
m_Slots[p0].Next = slot;
|
|
||||||
m_Slots[slot].Next = m_MaxSlot - 1;
|
|
||||||
|
|
||||||
m_Slots[m_MaxSlot - 1].Prev = slot;
|
|
||||||
m_Slots[slot].Prev = p0;
|
|
||||||
|
|
||||||
m_Slots[slot].Pos = m_BufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// runs a bunch of consistency check on the Allocator
|
|
||||||
internal bool Verify()
|
|
||||||
{
|
|
||||||
int pos = k_NotSet;
|
|
||||||
int count = 0;
|
|
||||||
int total = 0;
|
|
||||||
int endPos = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
int prev = pos;
|
|
||||||
if (pos != k_NotSet)
|
|
||||||
{
|
|
||||||
pos = m_Slots[pos].Next;
|
|
||||||
if (pos == k_NotSet)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Slots[pos].Prev != prev)
|
|
||||||
{
|
|
||||||
// the previous is not correct
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Slots[pos].Length < 0)
|
|
||||||
{
|
|
||||||
// Length should be positive
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev != k_NotSet && m_Slots[prev].Free && m_Slots[pos].Free && m_Slots[pos].Length > 0)
|
|
||||||
{
|
|
||||||
// should not have two consecutive free slots
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Slots[pos].Pos != total)
|
|
||||||
{
|
|
||||||
// slots should all line up nicely
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_Slots[pos].Free)
|
|
||||||
{
|
|
||||||
endPos = m_Slots[pos].Pos + m_Slots[pos].Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
total += m_Slots[pos].Length;
|
|
||||||
count++;
|
|
||||||
|
|
||||||
} while (pos != k_NotSet);
|
|
||||||
|
|
||||||
if (count != m_MaxSlot)
|
|
||||||
{
|
|
||||||
// some slots were lost
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (total != m_BufferSize)
|
|
||||||
{
|
|
||||||
// total buffer should be accounted for
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endPos != Range)
|
|
||||||
{
|
|
||||||
// end position should match reported end position
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug display the allocator structure
|
|
||||||
internal void DebugDisplay()
|
|
||||||
{
|
|
||||||
string logMessage = "IndexAllocator structure\n";
|
|
||||||
|
|
||||||
bool[] seen = new bool[m_MaxSlot];
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
int count = 0;
|
|
||||||
bool prevEmpty = false;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
seen[pos] = true;
|
|
||||||
count++;
|
|
||||||
if (m_Slots[pos].Length == 0 && prevEmpty)
|
|
||||||
{
|
|
||||||
// don't display repetitive empty slots
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logMessage += string.Format("{0}:{1}, {2} ({3}) \n", m_Slots[pos].Pos, m_Slots[pos].Length,
|
|
||||||
m_Slots[pos].Free ? "Free" : "Used", pos);
|
|
||||||
if (m_Slots[pos].Length == 0)
|
|
||||||
{
|
|
||||||
prevEmpty = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
prevEmpty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = m_Slots[pos].Next;
|
|
||||||
} while (pos != k_NotSet && !seen[pos]);
|
|
||||||
|
|
||||||
logMessage += string.Format("{0} Total entries\n", count);
|
|
||||||
|
|
||||||
Debug.Log(logMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,42 +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,
|
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))
|
||||||
@@ -102,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.");
|
||||||
}
|
}
|
||||||
@@ -129,73 +153,85 @@ 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 (clientRpcParams.Send.TargetClientIds != null)
|
||||||
if (rpcParams.Send.TargetClientIds != null)
|
|
||||||
{
|
{
|
||||||
foreach (var clientId in rpcParams.Send.TargetClientIds)
|
foreach (var targetClientId in clientRpcParams.Send.TargetClientIds)
|
||||||
{
|
{
|
||||||
if (clientId == NetworkManager.ServerClientId)
|
if (targetClientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
shouldSendToHost = true;
|
shouldSendToHost = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check to make sure we are sending to only observers, if not log an error.
|
||||||
|
if (NetworkManager.LogLevel >= LogLevel.Error && !NetworkObject.Observers.Contains(targetClientId))
|
||||||
|
{
|
||||||
|
NetworkLog.LogError(GenerateObserverErrorMessage(clientRpcParams, targetClientId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 targetClientId in clientRpcParams.Send.TargetClientIdsNativeArray)
|
||||||
{
|
{
|
||||||
if (clientId == NetworkManager.ServerClientId)
|
if (targetClientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
shouldSendToHost = true;
|
shouldSendToHost = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check to make sure we are sending to only observers, if not log an error.
|
||||||
|
if (NetworkManager.LogLevel >= LogLevel.Error && !NetworkObject.Observers.Contains(targetClientId))
|
||||||
|
{
|
||||||
|
NetworkLog.LogError(GenerateObserverErrorMessage(clientRpcParams, targetClientId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
messageSize = NetworkManager.SendMessage(message, networkDelivery, rpcParams.Send.TargetClientIdsNativeArray.Value);
|
rpcWriteSize = NetworkManager.SendMessage(ref clientRpcMessage, networkDelivery, clientRpcParams.Send.TargetClientIdsNativeArray.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
shouldSendToHost = IsHost;
|
var observerEnumerator = NetworkObject.Observers.GetEnumerator();
|
||||||
messageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ConnectedClientsIds);
|
while (observerEnumerator.MoveNext())
|
||||||
|
{
|
||||||
|
// Skip over the host
|
||||||
|
if (IsHost && observerEnumerator.Current == NetworkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
shouldSendToHost = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rpcWriteSize = NetworkManager.MessagingSystem.SendMessage(ref clientRpcMessage, networkDelivery, observerEnumerator.Current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
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))
|
||||||
{
|
{
|
||||||
@@ -206,12 +242,18 @@ namespace Unity.Netcode
|
|||||||
NetworkObject,
|
NetworkObject,
|
||||||
rpcMethodName,
|
rpcMethodName,
|
||||||
__getTypeName(),
|
__getTypeName(),
|
||||||
messageSize);
|
rpcWriteSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal string GenerateObserverErrorMessage(ClientRpcParams clientRpcParams, ulong targetClientId)
|
||||||
|
{
|
||||||
|
var containerNameHoldingId = clientRpcParams.Send.TargetClientIds != null ? nameof(ClientRpcParams.Send.TargetClientIds) : nameof(ClientRpcParams.Send.TargetClientIdsNativeArray);
|
||||||
|
return $"Sending ClientRpc to non-observer! {containerNameHoldingId} contains clientId {targetClientId} that is not an observer!";
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the NetworkManager that owns this NetworkBehaviour instance
|
/// Gets the NetworkManager that owns this NetworkBehaviour instance
|
||||||
/// See note around `NetworkObject` for how there is a chicken / egg problem when we are not initialized
|
/// See note around `NetworkObject` for how there is a chicken / egg problem when we are not initialized
|
||||||
@@ -219,50 +261,50 @@ namespace Unity.Netcode
|
|||||||
public NetworkManager NetworkManager => NetworkObject.NetworkManager;
|
public NetworkManager NetworkManager => NetworkObject.NetworkManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if the object is the the personal clients player object
|
/// If a NetworkObject is assigned, it will return whether or not this NetworkObject
|
||||||
|
/// is the local player object. If no NetworkObject is assigned it will always return false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsLocalPlayer => NetworkObject.IsLocalPlayer;
|
public bool IsLocalPlayer { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if the object is owned by the local player or if the object is the local player object
|
/// Gets if the object is owned by the local player or if the object is the local player object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsOwner => NetworkObject.IsOwner;
|
public bool IsOwner { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if we are executing as server
|
/// Gets if we are executing as server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool IsServer => IsRunning && NetworkManager.IsServer;
|
protected bool IsServer { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if we are executing as client
|
/// Gets if we are executing as client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool IsClient => IsRunning && NetworkManager.IsClient;
|
protected bool IsClient { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets if we are executing as Host, I.E Server and Client
|
/// Gets if we are executing as Host, I.E Server and Client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool IsHost => IsRunning && NetworkManager.IsHost;
|
protected bool IsHost { get; private set; }
|
||||||
|
|
||||||
private bool IsRunning => NetworkManager && NetworkManager.IsListening;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets Whether or not the object has a owner
|
/// Gets Whether or not the object has a owner
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsOwnedByServer => NetworkObject.IsOwnedByServer;
|
public bool IsOwnedByServer { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to determine if it is safe to access NetworkObject and NetworkManager from within a NetworkBehaviour component
|
/// Used to determine if it is safe to access NetworkObject and NetworkManager from within a NetworkBehaviour component
|
||||||
/// Primarily useful when checking NetworkObject/NetworkManager properties within FixedUpate
|
/// Primarily useful when checking NetworkObject/NetworkManager properties within FixedUpate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsSpawned => HasNetworkObject ? NetworkObject.IsSpawned : false;
|
public bool IsSpawned { get; internal set; }
|
||||||
|
|
||||||
internal bool IsBehaviourEditable()
|
internal bool IsBehaviourEditable()
|
||||||
{
|
{
|
||||||
// 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>
|
||||||
@@ -284,10 +326,14 @@ namespace Unity.Netcode
|
|||||||
m_NetworkObject = GetComponentInParent<NetworkObject>();
|
m_NetworkObject = GetComponentInParent<NetworkObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_NetworkObject == null || NetworkManager.Singleton == null ||
|
// ShutdownInProgress check:
|
||||||
(NetworkManager.Singleton != null && !NetworkManager.Singleton.ShutdownInProgress))
|
// 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))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel < LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"Could not get {nameof(NetworkObject)} for the {nameof(NetworkBehaviour)}. Are you missing a {nameof(NetworkObject)} component?");
|
NetworkLog.LogWarning($"Could not get {nameof(NetworkObject)} for the {nameof(NetworkBehaviour)}. Are you missing a {nameof(NetworkObject)} component?");
|
||||||
}
|
}
|
||||||
@@ -307,12 +353,12 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour
|
/// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong NetworkObjectId => NetworkObject.NetworkObjectId;
|
public ulong NetworkObjectId { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject
|
/// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort NetworkBehaviourId => NetworkObject.GetNetworkBehaviourOrderIndex(this);
|
public ushort NetworkBehaviourId { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster
|
/// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster
|
||||||
@@ -332,7 +378,47 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ClientId that owns the NetworkObject
|
/// Gets the ClientId that owns the NetworkObject
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong OwnerClientId => NetworkObject.OwnerClientId;
|
public ulong OwnerClientId { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates properties with network session related
|
||||||
|
/// dependencies such as a NetworkObject's spawned
|
||||||
|
/// state or NetworkManager's session state.
|
||||||
|
/// </summary>
|
||||||
|
internal void UpdateNetworkProperties()
|
||||||
|
{
|
||||||
|
// Set NetworkObject dependent properties
|
||||||
|
if (NetworkObject != null)
|
||||||
|
{
|
||||||
|
// Set identification related properties
|
||||||
|
NetworkObjectId = NetworkObject.NetworkObjectId;
|
||||||
|
IsLocalPlayer = NetworkObject.IsLocalPlayer;
|
||||||
|
|
||||||
|
// This is "OK" because GetNetworkBehaviourOrderIndex uses the order of
|
||||||
|
// NetworkObject.ChildNetworkBehaviours which is set once when first
|
||||||
|
// accessed.
|
||||||
|
NetworkBehaviourId = NetworkObject.GetNetworkBehaviourOrderIndex(this);
|
||||||
|
|
||||||
|
// Set ownership related properties
|
||||||
|
IsOwnedByServer = NetworkObject.IsOwnedByServer;
|
||||||
|
IsOwner = NetworkObject.IsOwner;
|
||||||
|
OwnerClientId = NetworkObject.OwnerClientId;
|
||||||
|
|
||||||
|
// Set NetworkManager dependent properties
|
||||||
|
if (NetworkManager != null)
|
||||||
|
{
|
||||||
|
IsHost = NetworkManager.IsListening && NetworkManager.IsHost;
|
||||||
|
IsClient = NetworkManager.IsListening && NetworkManager.IsClient;
|
||||||
|
IsServer = NetworkManager.IsListening && NetworkManager.IsServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Shouldn't happen, but if so then set the properties to their default value;
|
||||||
|
{
|
||||||
|
OwnerClientId = NetworkObjectId = default;
|
||||||
|
IsOwnedByServer = IsOwner = IsHost = IsClient = IsServer = default;
|
||||||
|
NetworkBehaviourId = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when the <see cref="NetworkObject"/> gets spawned, message handlers are ready to be registered and the network is setup.
|
/// Gets called when the <see cref="NetworkObject"/> gets spawned, message handlers are ready to be registered and the network is setup.
|
||||||
@@ -346,12 +432,17 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void InternalOnNetworkSpawn()
|
internal void InternalOnNetworkSpawn()
|
||||||
{
|
{
|
||||||
|
IsSpawned = true;
|
||||||
InitializeVariables();
|
InitializeVariables();
|
||||||
|
UpdateNetworkProperties();
|
||||||
|
OnNetworkSpawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InternalOnNetworkDespawn()
|
internal void InternalOnNetworkDespawn()
|
||||||
{
|
{
|
||||||
|
IsSpawned = false;
|
||||||
|
UpdateNetworkProperties();
|
||||||
|
OnNetworkDespawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -359,11 +450,23 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void OnGainedOwnership() { }
|
public virtual void OnGainedOwnership() { }
|
||||||
|
|
||||||
|
internal void InternalOnGainedOwnership()
|
||||||
|
{
|
||||||
|
UpdateNetworkProperties();
|
||||||
|
OnGainedOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when we loose ownership of this object
|
/// Gets called when we loose ownership of this object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void OnLostOwnership() { }
|
public virtual void OnLostOwnership() { }
|
||||||
|
|
||||||
|
internal void InternalOnLostOwnership()
|
||||||
|
{
|
||||||
|
UpdateNetworkProperties();
|
||||||
|
OnLostOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
|
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -416,20 +519,17 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
m_VarInit = true;
|
m_VarInit = true;
|
||||||
|
|
||||||
FieldInfo[] sortedFields = GetFieldInfoForType(GetType());
|
var sortedFields = GetFieldInfoForType(GetType());
|
||||||
|
|
||||||
for (int i = 0; i < sortedFields.Length; i++)
|
for (int i = 0; i < sortedFields.Length; i++)
|
||||||
{
|
{
|
||||||
Type fieldType = sortedFields[i].FieldType;
|
var fieldType = sortedFields[i].FieldType;
|
||||||
|
|
||||||
if (fieldType.IsSubclassOf(typeof(NetworkVariableBase)))
|
if (fieldType.IsSubclassOf(typeof(NetworkVariableBase)))
|
||||||
{
|
{
|
||||||
var instance = (NetworkVariableBase)sortedFields[i].GetValue(this);
|
var instance = (NetworkVariableBase)sortedFields[i].GetValue(this);
|
||||||
|
|
||||||
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);
|
||||||
@@ -483,7 +583,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void VariableUpdate(ulong clientId)
|
internal void VariableUpdate(ulong targetClientId)
|
||||||
{
|
{
|
||||||
if (!m_VarInit)
|
if (!m_VarInit)
|
||||||
{
|
{
|
||||||
@@ -491,67 +591,58 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
PreNetworkVariableWrite();
|
PreNetworkVariableWrite();
|
||||||
NetworkVariableUpdate(clientId, NetworkBehaviourId);
|
NetworkVariableUpdate(targetClientId, NetworkBehaviourId);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly List<int> NetworkVariableIndexesToReset = new List<int>();
|
internal readonly List<int> NetworkVariableIndexesToReset = new List<int>();
|
||||||
internal readonly HashSet<int> NetworkVariableIndexesToResetSet = new HashSet<int>();
|
internal readonly HashSet<int> NetworkVariableIndexesToResetSet = new HashSet<int>();
|
||||||
|
|
||||||
private void NetworkVariableUpdate(ulong clientId, int behaviourIndex)
|
private void NetworkVariableUpdate(ulong targetClientId, int behaviourIndex)
|
||||||
{
|
{
|
||||||
if (!CouldHaveDirtyNetworkVariables())
|
if (!CouldHaveDirtyNetworkVariables())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotDelta)
|
for (int j = 0; j < m_DeliveryMappedNetworkVariableIndices.Count; j++)
|
||||||
{
|
{
|
||||||
|
var shouldSend = false;
|
||||||
for (int k = 0; k < NetworkVariableFields.Count; k++)
|
for (int k = 0; k < NetworkVariableFields.Count; k++)
|
||||||
{
|
{
|
||||||
NetworkManager.SnapshotSystem.Store(NetworkObjectId, behaviourIndex, k, NetworkVariableFields[k]);
|
var networkVariable = NetworkVariableFields[k];
|
||||||
}
|
if (networkVariable.IsDirty() && networkVariable.CanClientRead(targetClientId))
|
||||||
}
|
|
||||||
|
|
||||||
if (!NetworkManager.NetworkConfig.UseSnapshotDelta)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < m_DeliveryMappedNetworkVariableIndices.Count; j++)
|
|
||||||
{
|
|
||||||
var shouldSend = false;
|
|
||||||
for (int k = 0; k < NetworkVariableFields.Count; k++)
|
|
||||||
{
|
{
|
||||||
if (NetworkVariableFields[k].ShouldWrite(clientId, IsServer))
|
shouldSend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldSend)
|
||||||
|
{
|
||||||
|
var message = new NetworkVariableDeltaMessage
|
||||||
|
{
|
||||||
|
NetworkObjectId = NetworkObjectId,
|
||||||
|
NetworkBehaviourIndex = NetworkObject.GetNetworkBehaviourOrderIndex(this),
|
||||||
|
NetworkBehaviour = this,
|
||||||
|
TargetClientId = targetClientId,
|
||||||
|
DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j]
|
||||||
|
};
|
||||||
|
// TODO: Serialization is where the IsDirty flag gets changed.
|
||||||
|
// Messages don't get sent from the server to itself, so if we're host and sending to ourselves,
|
||||||
|
// we still have to actually serialize the message even though we're not sending it, otherwise
|
||||||
|
// the dirty flag doesn't change properly. These two pieces should be decoupled at some point
|
||||||
|
// so we don't have to do this serialization work if we're not going to use the result.
|
||||||
|
if (IsServer && targetClientId == NetworkManager.ServerClientId)
|
||||||
|
{
|
||||||
|
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
||||||
|
using (tmpWriter)
|
||||||
{
|
{
|
||||||
shouldSend = true;
|
message.Serialize(tmpWriter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (shouldSend)
|
|
||||||
{
|
{
|
||||||
var message = new NetworkVariableDeltaMessage
|
NetworkManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], targetClientId);
|
||||||
{
|
|
||||||
NetworkObjectId = NetworkObjectId,
|
|
||||||
NetworkBehaviourIndex = NetworkObject.GetNetworkBehaviourOrderIndex(this),
|
|
||||||
NetworkBehaviour = this,
|
|
||||||
ClientId = clientId,
|
|
||||||
DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j]
|
|
||||||
};
|
|
||||||
// TODO: Serialization is where the IsDirty flag gets changed.
|
|
||||||
// Messages don't get sent from the server to itself, so if we're host and sending to ourselves,
|
|
||||||
// we still have to actually serialize the message even though we're not sending it, otherwise
|
|
||||||
// the dirty flag doesn't change properly. These two pieces should be decoupled at some point
|
|
||||||
// so we don't have to do this serialization work if we're not going to use the result.
|
|
||||||
if (IsServer && clientId == NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp);
|
|
||||||
using (tmpWriter)
|
|
||||||
{
|
|
||||||
message.Serialize(tmpWriter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NetworkManager.SendMessage(message, m_DeliveryTypesForNetworkVariableGroups[j], clientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -579,7 +670,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong clientId)
|
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
|
||||||
{
|
{
|
||||||
if (NetworkVariableFields.Count == 0)
|
if (NetworkVariableFields.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -588,7 +679,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||||
{
|
{
|
||||||
bool canClientRead = NetworkVariableFields[j].CanClientRead(clientId);
|
bool canClientRead = NetworkVariableFields[j].CanClientRead(targetClientId);
|
||||||
|
|
||||||
if (canClientRead)
|
if (canClientRead)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||||
{
|
{
|
||||||
sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId);
|
sobj.ChildNetworkBehaviours[k].VariableUpdate(NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ using Unity.Multiplayer.Tools;
|
|||||||
#endif
|
#endif
|
||||||
using Unity.Profiling;
|
using Unity.Profiling;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Debug = UnityEngine.Debug;
|
using Debug = UnityEngine.Debug;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
@@ -45,7 +46,7 @@ namespace Unity.Netcode
|
|||||||
private static ProfilerMarker s_TransportDisconnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportDisconnect");
|
private static ProfilerMarker s_TransportDisconnect = new ProfilerMarker($"{nameof(NetworkManager)}.TransportDisconnect");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private const double k_TimeSyncFrequency = 1.0d; // sync every second, TODO will be removed once timesync is done via snapshots
|
private const double k_TimeSyncFrequency = 1.0d; // sync every second
|
||||||
private const float k_DefaultBufferSizeSec = 0.05f; // todo talk with UX/Product, find good default value for this
|
private const float k_DefaultBufferSizeSec = 0.05f; // todo talk with UX/Product, find good default value for this
|
||||||
|
|
||||||
internal static string PrefabDebugHelper(NetworkPrefab networkPrefab)
|
internal static string PrefabDebugHelper(NetworkPrefab networkPrefab)
|
||||||
@@ -53,7 +54,6 @@ namespace Unity.Netcode
|
|||||||
return $"{nameof(NetworkPrefab)} \"{networkPrefab.Prefab.gameObject.name}\"";
|
return $"{nameof(NetworkPrefab)} \"{networkPrefab.Prefab.gameObject.name}\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal SnapshotSystem SnapshotSystem { get; private set; }
|
|
||||||
internal NetworkBehaviourUpdater BehaviourUpdater { get; private set; }
|
internal NetworkBehaviourUpdater BehaviourUpdater { get; private set; }
|
||||||
|
|
||||||
internal MessagingSystem MessagingSystem { get; private set; }
|
internal MessagingSystem MessagingSystem { get; private set; }
|
||||||
@@ -85,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
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,13 +125,11 @@ namespace Unity.Netcode
|
|||||||
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
|
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
|
||||||
{
|
{
|
||||||
if (m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) &&
|
if (m_NetworkManager.PendingClients.TryGetValue(senderId, out PendingClient client) &&
|
||||||
(client.ConnectionState == PendingClient.State.PendingApproval ||
|
(client.ConnectionState == PendingClient.State.PendingApproval || (client.ConnectionState == PendingClient.State.PendingConnection && messageType != typeof(ConnectionRequestMessage))))
|
||||||
(client.ConnectionState == PendingClient.State.PendingConnection &&
|
|
||||||
messageType != typeof(ConnectionRequestMessage))))
|
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"Message received from {nameof(senderId)}={senderId.ToString()} before it has been accepted");
|
NetworkLog.LogWarning($"Message received from {nameof(senderId)}={senderId} before it has been accepted");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -139,6 +137,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
return !m_NetworkManager.m_StopProcessingMessages;
|
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
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NetworkManagerMessageSender : IMessageSender
|
private class NetworkManagerMessageSender : IMessageSender
|
||||||
@@ -221,14 +227,12 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
public NetworkSceneManager SceneManager { get; private set; }
|
public NetworkSceneManager SceneManager { get; private set; }
|
||||||
|
|
||||||
public readonly ulong ServerClientId = 0;
|
public const ulong ServerClientId = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the networkId of the server
|
/// Gets the networkId of the server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ulong m_ServerTransportId => NetworkConfig.NetworkTransport?.ServerClientId ??
|
private ulong m_ServerTransportId => NetworkConfig.NetworkTransport?.ServerClientId ?? throw new NullReferenceException($"The transport in the active {nameof(NetworkConfig)} is null");
|
||||||
throw new NullReferenceException(
|
|
||||||
$"The transport in the active {nameof(NetworkConfig)} is null");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns ServerClientId if IsServer or LocalClientId if not
|
/// Returns ServerClientId if IsServer or LocalClientId if not
|
||||||
@@ -359,16 +363,14 @@ namespace Unity.Netcode
|
|||||||
/// <param name="approved">Whether or not the client was approved</param>
|
/// <param name="approved">Whether or not the client was approved</param>
|
||||||
/// <param name="position">The position to spawn the client at. If null, the prefab position is used.</param>
|
/// <param name="position">The position to spawn the client at. If null, the prefab position is used.</param>
|
||||||
/// <param name="rotation">The rotation to spawn the client with. If null, the prefab position is used.</param>
|
/// <param name="rotation">The rotation to spawn the client with. If null, the prefab position is used.</param>
|
||||||
public delegate void ConnectionApprovedDelegate(bool createPlayerObject, uint? playerPrefabHash, bool approved,
|
public delegate void ConnectionApprovedDelegate(bool createPlayerObject, uint? playerPrefabHash, bool approved, Vector3? position, Quaternion? rotation);
|
||||||
Vector3? position, Quaternion? rotation);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The callback to invoke during connection approval
|
/// The callback to invoke during connection approval
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<byte[], ulong, ConnectionApprovedDelegate> ConnectionApprovalCallback = null;
|
public event Action<byte[], ulong, ConnectionApprovedDelegate> ConnectionApprovalCallback = null;
|
||||||
|
|
||||||
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action) =>
|
internal void InvokeConnectionApproval(byte[] payload, ulong clientId, ConnectionApprovedDelegate action) => ConnectionApprovalCallback?.Invoke(payload, clientId, action);
|
||||||
ConnectionApprovalCallback?.Invoke(payload, clientId, action);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current NetworkConfig
|
/// The current NetworkConfig
|
||||||
@@ -555,12 +557,7 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//This 'if' should never enter
|
NetworkConfig.NetworkTransport.NetworkMetrics = NetworkMetrics;
|
||||||
if (SnapshotSystem != null)
|
|
||||||
{
|
|
||||||
SnapshotSystem.Dispose();
|
|
||||||
SnapshotSystem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server)
|
if (server)
|
||||||
{
|
{
|
||||||
@@ -574,8 +571,6 @@ 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
|
||||||
@@ -583,6 +578,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++)
|
||||||
@@ -779,7 +775,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
NetworkConfig.NetworkTransport.OnTransportEvent += HandleRawTransportPoll;
|
NetworkConfig.NetworkTransport.OnTransportEvent += HandleRawTransportPoll;
|
||||||
|
|
||||||
NetworkConfig.NetworkTransport.Initialize();
|
NetworkConfig.NetworkTransport.Initialize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -789,44 +785,35 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogInfo("StartServer()");
|
NetworkLog.LogInfo(nameof(StartServer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsServer || IsClient)
|
if (!CanStart(StartType.Server))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning("Cannot start server while an instance is already running");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkConfig.ConnectionApproval)
|
|
||||||
{
|
|
||||||
if (ConnectionApprovalCallback == null)
|
|
||||||
{
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning(
|
|
||||||
"No ConnectionApproval callback defined. Connection approval will timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Initialize(true);
|
Initialize(true);
|
||||||
|
|
||||||
var result = NetworkConfig.NetworkTransport.StartServer();
|
// If we failed to start then shutdown and notify user that the transport failed to start
|
||||||
|
if (NetworkConfig.NetworkTransport.StartServer())
|
||||||
|
{
|
||||||
|
IsServer = true;
|
||||||
|
IsClient = false;
|
||||||
|
IsListening = true;
|
||||||
|
|
||||||
IsServer = true;
|
SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
|
||||||
IsClient = false;
|
|
||||||
IsListening = true;
|
|
||||||
|
|
||||||
SpawnManager.ServerSpawnSceneObjectsOnStartSweep();
|
OnServerStarted?.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError($"Server is shutting down due to network transport start failure of {NetworkConfig.NetworkTransport.GetType().Name}!");
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
OnServerStarted?.Invoke();
|
return false;
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -839,26 +826,26 @@ namespace Unity.Netcode
|
|||||||
NetworkLog.LogInfo(nameof(StartClient));
|
NetworkLog.LogInfo(nameof(StartClient));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsServer || IsClient)
|
if (!CanStart(StartType.Client))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning("Cannot start client while an instance is already running");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Initialize(false);
|
Initialize(false);
|
||||||
MessagingSystem.ClientConnected(ServerClientId);
|
MessagingSystem.ClientConnected(ServerClientId);
|
||||||
|
|
||||||
var result = NetworkConfig.NetworkTransport.StartClient();
|
if (!NetworkConfig.NetworkTransport.StartClient())
|
||||||
|
{
|
||||||
|
Debug.LogError($"Client is shutting down due to network transport start failure of {NetworkConfig.NetworkTransport.GetType().Name}!");
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
IsServer = false;
|
IsServer = false;
|
||||||
IsClient = true;
|
IsClient = true;
|
||||||
IsListening = true;
|
IsListening = true;
|
||||||
|
|
||||||
return result;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -871,31 +858,21 @@ namespace Unity.Netcode
|
|||||||
NetworkLog.LogInfo(nameof(StartHost));
|
NetworkLog.LogInfo(nameof(StartHost));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsServer || IsClient)
|
if (!CanStart(StartType.Host))
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning("Cannot start host while an instance is already running");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkConfig.ConnectionApproval)
|
|
||||||
{
|
|
||||||
if (ConnectionApprovalCallback == null)
|
|
||||||
{
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning(
|
|
||||||
"No ConnectionApproval callback defined. Connection approval will timeout");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Initialize(true);
|
Initialize(true);
|
||||||
|
|
||||||
var result = NetworkConfig.NetworkTransport.StartServer();
|
// If we failed to start then shutdown and notify user that the transport failed to start
|
||||||
|
if (!NetworkConfig.NetworkTransport.StartServer())
|
||||||
|
{
|
||||||
|
Debug.LogError($"Server is shutting down due to network transport start failure of {NetworkConfig.NetworkTransport.GetType().Name}!");
|
||||||
|
Shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
MessagingSystem.ClientConnected(ServerClientId);
|
MessagingSystem.ClientConnected(ServerClientId);
|
||||||
LocalClientId = ServerClientId;
|
LocalClientId = ServerClientId;
|
||||||
NetworkMetrics.SetConnectionId(LocalClientId);
|
NetworkMetrics.SetConnectionId(LocalClientId);
|
||||||
@@ -931,7 +908,53 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
OnServerStarted?.Invoke();
|
OnServerStarted?.Invoke();
|
||||||
|
|
||||||
return result;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum StartType
|
||||||
|
{
|
||||||
|
Server,
|
||||||
|
Host,
|
||||||
|
Client
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanStart(StartType type)
|
||||||
|
{
|
||||||
|
if (IsListening)
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning("Cannot start " + type + " while an instance is already running");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NetworkConfig.ConnectionApproval)
|
||||||
|
{
|
||||||
|
if (ConnectionApprovalCallback == null)
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning(
|
||||||
|
"No ConnectionApproval callback defined. Connection approval will timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConnectionApprovalCallback != null)
|
||||||
|
{
|
||||||
|
if (!NetworkConfig.ConnectionApproval)
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning(
|
||||||
|
"A ConnectionApproval callback is defined but ConnectionApproval is disabled. In order to use ConnectionApproval it has to be explicitly enabled ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSingleton()
|
public void SetSingleton()
|
||||||
@@ -1003,6 +1026,7 @@ namespace Unity.Netcode
|
|||||||
internal interface INetworkManagerHelper
|
internal interface INetworkManagerHelper
|
||||||
{
|
{
|
||||||
bool NotifyUserOfNestedNetworkManager(NetworkManager networkManager, bool ignoreNetworkManagerCache = false, bool editorTest = false);
|
bool NotifyUserOfNestedNetworkManager(NetworkManager networkManager, bool ignoreNetworkManagerCache = false, bool editorTest = false);
|
||||||
|
void CheckAndNotifyUserNetworkObjectRemoved(NetworkManager networkManager, bool editorTest = false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1122,12 +1146,6 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
this.UnregisterAllNetworkUpdates();
|
this.UnregisterAllNetworkUpdates();
|
||||||
|
|
||||||
if (SnapshotSystem != null)
|
|
||||||
{
|
|
||||||
SnapshotSystem.Dispose();
|
|
||||||
SnapshotSystem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NetworkTickSystem != null)
|
if (NetworkTickSystem != null)
|
||||||
{
|
{
|
||||||
NetworkTickSystem.Tick -= OnNetworkManagerTick;
|
NetworkTickSystem.Tick -= OnNetworkManagerTick;
|
||||||
@@ -1263,7 +1281,11 @@ namespace Unity.Netcode
|
|||||||
if (!m_ShuttingDown || !m_StopProcessingMessages)
|
if (!m_ShuttingDown || !m_StopProcessingMessages)
|
||||||
{
|
{
|
||||||
MessagingSystem.ProcessSendQueues();
|
MessagingSystem.ProcessSendQueues();
|
||||||
|
NetworkMetrics.UpdateNetworkObjectsCount(SpawnManager.SpawnedObjects.Count);
|
||||||
|
NetworkMetrics.UpdateConnectionsCount((IsServer) ? ConnectedClients.Count : 1);
|
||||||
NetworkMetrics.DispatchFrame();
|
NetworkMetrics.DispatchFrame();
|
||||||
|
|
||||||
|
NetworkObject.VerifyParentingStatus();
|
||||||
}
|
}
|
||||||
SpawnManager.CleanupStaleTriggers();
|
SpawnManager.CleanupStaleTriggers();
|
||||||
|
|
||||||
@@ -1277,7 +1299,6 @@ namespace Unity.Netcode
|
|||||||
/// This function runs once whenever the local tick is incremented and is responsible for the following (in order):
|
/// This function runs once whenever the local tick is incremented and is responsible for the following (in order):
|
||||||
/// - collect commands/inputs and send them to the server (TBD)
|
/// - collect commands/inputs and send them to the server (TBD)
|
||||||
/// - call NetworkFixedUpdate on all NetworkBehaviours in prediction/client authority mode
|
/// - call NetworkFixedUpdate on all NetworkBehaviours in prediction/client authority mode
|
||||||
/// - create a snapshot from resulting state
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnNetworkManagerTick()
|
private void OnNetworkManagerTick()
|
||||||
{
|
{
|
||||||
@@ -1299,7 +1320,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)
|
||||||
@@ -1324,12 +1345,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];
|
||||||
}
|
}
|
||||||
@@ -1344,7 +1365,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;
|
||||||
|
|
||||||
@@ -1395,18 +1429,15 @@ namespace Unity.Netcode
|
|||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
s_TransportDisconnect.Begin();
|
s_TransportDisconnect.Begin();
|
||||||
#endif
|
#endif
|
||||||
clientId = TransportIdToClientId(clientId);
|
clientId = TransportIdCleanUp(clientId, transportId);
|
||||||
|
|
||||||
OnClientDisconnectCallback?.Invoke(clientId);
|
|
||||||
|
|
||||||
m_TransportIdToClientIdMap.Remove(transportId);
|
|
||||||
m_ClientIdToTransportIdMap.Remove(clientId);
|
|
||||||
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogInfo($"Disconnect Event From {clientId}");
|
NetworkLog.LogInfo($"Disconnect Event From {clientId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnClientDisconnectCallback?.Invoke(clientId);
|
||||||
|
|
||||||
if (IsServer)
|
if (IsServer)
|
||||||
{
|
{
|
||||||
OnClientDisconnectFromServer(clientId);
|
OnClientDisconnectFromServer(clientId);
|
||||||
@@ -1422,7 +1453,32 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe int SendMessage<TMessageType, TClientIdListType>(in TMessageType message, NetworkDelivery delivery, in TClientIdListType clientIds)
|
/// <summary>
|
||||||
|
/// Handles cleaning up the transport id/client id tables after
|
||||||
|
/// receiving a disconnect event from transport
|
||||||
|
/// </summary>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private ulong TransportIdCleanUp(ulong clientId, ulong transportId)
|
||||||
|
{
|
||||||
|
// This check is for clients that attempted to connect but failed.
|
||||||
|
// When this happens, the client will not have an entry within the
|
||||||
|
// m_TransportIdToClientIdMap or m_ClientIdToTransportIdMap lookup
|
||||||
|
// tables so we exit early and just return 0 to be used for the
|
||||||
|
// disconnect event.
|
||||||
|
if (!IsServer && !m_TransportIdToClientIdMap.ContainsKey(clientId))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
clientId = TransportIdToClientId(clientId);
|
||||||
|
|
||||||
|
m_TransportIdToClientIdMap.Remove(transportId);
|
||||||
|
m_ClientIdToTransportIdMap.Remove(clientId);
|
||||||
|
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
{
|
{
|
||||||
@@ -1445,12 +1501,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
|
||||||
{
|
{
|
||||||
@@ -1473,19 +1535,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
|
||||||
@@ -1493,7 +1560,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)
|
||||||
@@ -1517,7 +1595,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);
|
||||||
@@ -1545,32 +1623,45 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = networkClient.OwnedObjects.Count - 1; i >= 0; i--)
|
// Get the NetworkObjects owned by the disconnected client
|
||||||
|
var clientOwnedObjects = SpawnManager.GetClientOwnedObjects(clientId);
|
||||||
|
if (clientOwnedObjects == null)
|
||||||
{
|
{
|
||||||
var ownedObject = networkClient.OwnedObjects[i];
|
// This could happen if a client is never assigned a player object and is disconnected
|
||||||
if (ownedObject != null)
|
// Only log this in verbose/developer mode
|
||||||
|
if (LogLevel == LogLevel.Developer)
|
||||||
{
|
{
|
||||||
if (!ownedObject.DontDestroyWithOwner)
|
NetworkLog.LogWarning($"ClientID {clientId} disconnected with (0) zero owned objects! Was a player prefab not assigned?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Handle changing ownership and prefab handlers
|
||||||
|
for (int i = clientOwnedObjects.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var ownedObject = clientOwnedObjects[i];
|
||||||
|
if (ownedObject != null)
|
||||||
{
|
{
|
||||||
if (PrefabHandler.ContainsHandler(ConnectedClients[clientId].OwnedObjects[i]
|
if (!ownedObject.DontDestroyWithOwner)
|
||||||
.GlobalObjectIdHash))
|
|
||||||
{
|
{
|
||||||
PrefabHandler.HandleNetworkPrefabDestroy(ConnectedClients[clientId].OwnedObjects[i]);
|
if (PrefabHandler.ContainsHandler(clientOwnedObjects[i].GlobalObjectIdHash))
|
||||||
|
{
|
||||||
|
PrefabHandler.HandleNetworkPrefabDestroy(clientOwnedObjects[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Destroy(ownedObject.gameObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Destroy(ownedObject.gameObject);
|
ownedObject.RemoveOwnership();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ownedObject.RemoveOwnership();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Could(should?) be replaced with more memory per client, by storing the visibility
|
// TODO: Could(should?) be replaced with more memory per client, by storing the visibility
|
||||||
|
|
||||||
foreach (var sobj in SpawnManager.SpawnedObjectsList)
|
foreach (var sobj in SpawnManager.SpawnedObjectsList)
|
||||||
{
|
{
|
||||||
sobj.Observers.Remove(clientId);
|
sobj.Observers.Remove(clientId);
|
||||||
@@ -1614,7 +1705,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
|
||||||
@@ -1661,12 +1752,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)
|
||||||
@@ -1719,14 +1809,14 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
var message = new CreateObjectMessage
|
var message = new CreateObjectMessage
|
||||||
{
|
{
|
||||||
ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key, false)
|
ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key)
|
||||||
};
|
};
|
||||||
message.ObjectInfo.Header.Hash = playerPrefabHash;
|
message.ObjectInfo.Header.Hash = playerPrefabHash;
|
||||||
message.ObjectInfo.Header.IsSceneObject = false;
|
message.ObjectInfo.Header.IsSceneObject = false;
|
||||||
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
|
||||||
@@ -54,8 +54,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal NetworkManager NetworkManagerOwner;
|
internal NetworkManager NetworkManagerOwner;
|
||||||
|
|
||||||
private ulong m_NetworkObjectId;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the unique Id of this object that is synced across the network
|
/// Gets the unique Id of this object that is synced across the network
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -64,33 +62,7 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ClientId of the owner of this NetworkObject
|
/// Gets the ClientId of the owner of this NetworkObject
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong OwnerClientId
|
public ulong OwnerClientId { get; internal set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (OwnerClientIdInternal == null)
|
|
||||||
{
|
|
||||||
return NetworkManager != null ? NetworkManager.ServerClientId : 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return OwnerClientIdInternal.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal set
|
|
||||||
{
|
|
||||||
if (NetworkManager != null && value == NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
OwnerClientIdInternal = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OwnerClientIdInternal = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ulong? OwnerClientIdInternal = null;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, the object will always be replicated as root on clients and the parent will be ignored.
|
/// If true, the object will always be replicated as root on clients and the parent will be ignored.
|
||||||
@@ -234,11 +206,6 @@ namespace Unity.Netcode
|
|||||||
throw new VisibilityChangeException("The object is already visible");
|
throw new VisibilityChangeException("The object is already visible");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
|
||||||
{
|
|
||||||
SnapshotSpawn(clientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Observers.Add(clientId);
|
Observers.Add(clientId);
|
||||||
|
|
||||||
NetworkManager.SpawnManager.SendSpawnCallForObject(clientId, this);
|
NetworkManager.SpawnManager.SendSpawnCallForObject(clientId, this);
|
||||||
@@ -314,23 +281,15 @@ namespace Unity.Netcode
|
|||||||
throw new VisibilityChangeException("Cannot hide an object from the server");
|
throw new VisibilityChangeException("Cannot hide an object from the server");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Observers.Remove(clientId);
|
Observers.Remove(clientId);
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
var message = new DestroyObjectMessage
|
||||||
{
|
{
|
||||||
SnapshotDespawn(clientId);
|
NetworkObjectId = NetworkObjectId
|
||||||
}
|
};
|
||||||
else
|
// Send destroy call
|
||||||
{
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
|
||||||
var message = new DestroyObjectMessage
|
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
||||||
{
|
|
||||||
NetworkObjectId = NetworkObjectId
|
|
||||||
};
|
|
||||||
// Send destroy call
|
|
||||||
var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, clientId);
|
|
||||||
NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientId, this, size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -345,14 +304,14 @@ namespace Unity.Netcode
|
|||||||
throw new ArgumentNullException("At least one " + nameof(NetworkObject) + " has to be provided");
|
throw new ArgumentNullException("At least one " + nameof(NetworkObject) + " has to be provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManager networkManager = networkObjects[0].NetworkManager;
|
var networkManager = networkObjects[0].NetworkManager;
|
||||||
|
|
||||||
if (!networkManager.IsServer)
|
if (!networkManager.IsServer)
|
||||||
{
|
{
|
||||||
throw new NotServerException("Only server can change visibility");
|
throw new NotServerException("Only server can change visibility");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientId == networkManager.ServerClientId)
|
if (clientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
throw new VisibilityChangeException("Cannot hide an object from the server");
|
throw new VisibilityChangeException("Cannot hide an object from the server");
|
||||||
}
|
}
|
||||||
@@ -384,84 +343,21 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned
|
if (NetworkManager != null && NetworkManager.IsListening && NetworkManager.IsServer == false && IsSpawned &&
|
||||||
&& (IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true)))
|
(IsSceneObject == null || (IsSceneObject != null && IsSceneObject.Value != true)))
|
||||||
{
|
{
|
||||||
throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead.");
|
throw new NotServerException($"Destroy a spawned {nameof(NetworkObject)} on a non-host client is not valid. Call {nameof(Destroy)} or {nameof(Despawn)} on the server/host instead.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager != null && NetworkManager.SpawnManager != null && NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
if (NetworkManager != null && NetworkManager.SpawnManager != null &&
|
||||||
|
NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
||||||
{
|
{
|
||||||
NetworkManager.SpawnManager.OnDespawnObject(networkObject, false);
|
NetworkManager.SpawnManager.OnDespawnObject(networkObject, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SnapshotDespawnCommand GetDespawnCommand()
|
|
||||||
{
|
|
||||||
var command = new SnapshotDespawnCommand();
|
|
||||||
command.NetworkObjectId = NetworkObjectId;
|
|
||||||
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SnapshotSpawnCommand GetSpawnCommand()
|
|
||||||
{
|
|
||||||
var command = new SnapshotSpawnCommand();
|
|
||||||
command.NetworkObjectId = NetworkObjectId;
|
|
||||||
command.OwnerClientId = OwnerClientId;
|
|
||||||
command.IsPlayerObject = IsPlayerObject;
|
|
||||||
command.IsSceneObject = (IsSceneObject == null) || IsSceneObject.Value;
|
|
||||||
|
|
||||||
ulong? parent = NetworkManager.SpawnManager.GetSpawnParentId(this);
|
|
||||||
if (parent != null)
|
|
||||||
{
|
|
||||||
command.ParentNetworkId = parent.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// write own network id, when no parents. todo: optimize this.
|
|
||||||
command.ParentNetworkId = command.NetworkObjectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
command.GlobalObjectIdHash = HostCheckForGlobalObjectIdHashOverride();
|
|
||||||
// todo: check if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(clientId)) for any clientId
|
|
||||||
command.ObjectPosition = transform.position;
|
|
||||||
command.ObjectRotation = transform.rotation;
|
|
||||||
command.ObjectScale = transform.localScale;
|
|
||||||
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SnapshotSpawn()
|
|
||||||
{
|
|
||||||
var command = GetSpawnCommand();
|
|
||||||
NetworkManager.SnapshotSystem.Spawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SnapshotSpawn(ulong clientId)
|
|
||||||
{
|
|
||||||
var command = GetSpawnCommand();
|
|
||||||
command.TargetClientIds = new List<ulong>();
|
|
||||||
command.TargetClientIds.Add(clientId);
|
|
||||||
NetworkManager.SnapshotSystem.Spawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SnapshotDespawn()
|
|
||||||
{
|
|
||||||
var command = GetDespawnCommand();
|
|
||||||
NetworkManager.SnapshotSystem.Despawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SnapshotDespawn(ulong clientId)
|
|
||||||
{
|
|
||||||
var command = GetDespawnCommand();
|
|
||||||
command.TargetClientIds = new List<ulong>();
|
|
||||||
command.TargetClientIds.Add(clientId);
|
|
||||||
NetworkManager.SnapshotSystem.Despawn(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void SpawnInternal(bool destroyWithScene, ulong? ownerClientId, bool playerObject)
|
private void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool playerObject)
|
||||||
{
|
{
|
||||||
if (!NetworkManager.IsListening)
|
if (!NetworkManager.IsListening)
|
||||||
{
|
{
|
||||||
@@ -475,12 +371,6 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), false, playerObject, ownerClientId, destroyWithScene);
|
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), false, playerObject, ownerClientId, destroyWithScene);
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
|
||||||
{
|
|
||||||
SnapshotSpawn();
|
|
||||||
}
|
|
||||||
|
|
||||||
ulong ownerId = ownerClientId != null ? ownerClientId.Value : NetworkManager.ServerClientId;
|
|
||||||
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
|
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
|
||||||
{
|
{
|
||||||
if (Observers.Contains(NetworkManager.ConnectedClientsList[i].ClientId))
|
if (Observers.Contains(NetworkManager.ConnectedClientsList[i].ClientId))
|
||||||
@@ -496,7 +386,7 @@ namespace Unity.Netcode
|
|||||||
/// <param name="destroyWithScene">Should the object be destroyed when the scene is changed</param>
|
/// <param name="destroyWithScene">Should the object be destroyed when the scene is changed</param>
|
||||||
public void Spawn(bool destroyWithScene = false)
|
public void Spawn(bool destroyWithScene = false)
|
||||||
{
|
{
|
||||||
SpawnInternal(destroyWithScene, null, false);
|
SpawnInternal(destroyWithScene, NetworkManager.ServerClientId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -547,17 +437,29 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void InvokeBehaviourOnLostOwnership()
|
internal void InvokeBehaviourOnLostOwnership()
|
||||||
{
|
{
|
||||||
|
// Server already handles this earlier, hosts should ignore, all clients should update
|
||||||
|
if (!NetworkManager.IsServer)
|
||||||
|
{
|
||||||
|
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId, true);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
ChildNetworkBehaviours[i].OnLostOwnership();
|
ChildNetworkBehaviours[i].InternalOnLostOwnership();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InvokeBehaviourOnGainedOwnership()
|
internal void InvokeBehaviourOnGainedOwnership()
|
||||||
{
|
{
|
||||||
|
// Server already handles this earlier, hosts should ignore and only client owners should update
|
||||||
|
if (!NetworkManager.IsServer && NetworkManager.LocalClientId == OwnerClientId)
|
||||||
|
{
|
||||||
|
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
ChildNetworkBehaviours[i].OnGainedOwnership();
|
ChildNetworkBehaviours[i].InternalOnGainedOwnership();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,7 +616,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, clientIds, idx);
|
NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientIds, idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,13 +658,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
if (!NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(m_LatestParent.Value))
|
if (!NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(m_LatestParent.Value))
|
||||||
{
|
{
|
||||||
if (OrphanChildren.Add(this))
|
OrphanChildren.Add(this);
|
||||||
{
|
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
|
||||||
{
|
|
||||||
NetworkLog.LogWarning($"{nameof(NetworkObject)} ({name}) cannot find its parent, added to {nameof(OrphanChildren)} set");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,19 +689,21 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
internal void InvokeBehaviourNetworkSpawn()
|
internal void InvokeBehaviourNetworkSpawn()
|
||||||
{
|
{
|
||||||
|
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId);
|
||||||
|
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
|
ChildNetworkBehaviours[i].InternalOnNetworkSpawn();
|
||||||
ChildNetworkBehaviours[i].OnNetworkSpawn();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InvokeBehaviourNetworkDespawn()
|
internal void InvokeBehaviourNetworkDespawn()
|
||||||
{
|
{
|
||||||
|
NetworkManager.SpawnManager.UpdateOwnershipTable(this, OwnerClientId, true);
|
||||||
|
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
ChildNetworkBehaviours[i].InternalOnNetworkDespawn();
|
ChildNetworkBehaviours[i].InternalOnNetworkDespawn();
|
||||||
ChildNetworkBehaviours[i].OnNetworkDespawn();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,13 +732,13 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong clientId)
|
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
{
|
{
|
||||||
var behavior = ChildNetworkBehaviours[i];
|
var behavior = ChildNetworkBehaviours[i];
|
||||||
behavior.InitializeVariables();
|
behavior.InitializeVariables();
|
||||||
behavior.WriteNetworkVariableData(writer, clientId);
|
behavior.WriteNetworkVariableData(writer, targetClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -853,6 +751,27 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NGO currently guarantees that the client will receive spawn data for all objects in one network tick.
|
||||||
|
// Children may arrive before their parents; when they do they are stored in OrphanedChildren and then
|
||||||
|
// resolved when their parents arrived. Because we don't send a partial list of spawns (yet), something
|
||||||
|
// has gone wrong if by the end of an update we still have unresolved orphans
|
||||||
|
//
|
||||||
|
|
||||||
|
// if and when we have different systems for where it is expected that orphans survive across ticks,
|
||||||
|
// then this warning will remind us that we need to revamp the system because then we can no longer simply
|
||||||
|
// spawn the orphan without its parent (at least, not when its transform is set to local coords mode)
|
||||||
|
// - because then you’ll have children popping at the wrong location not having their parent’s global position to root them
|
||||||
|
// - and then they’ll pop to the correct location after they get the parent, and that would be not good
|
||||||
|
internal static void VerifyParentingStatus()
|
||||||
|
{
|
||||||
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
|
{
|
||||||
|
if (OrphanChildren.Count > 0)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"{nameof(NetworkObject)} ({OrphanChildren.Count}) children not resolved to parents by the end of frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
internal void SetNetworkVariableData(FastBufferReader reader)
|
internal void SetNetworkVariableData(FastBufferReader reader)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||||
@@ -918,7 +837,6 @@ namespace Unity.Netcode
|
|||||||
public bool IsSceneObject;
|
public bool IsSceneObject;
|
||||||
public bool HasTransform;
|
public bool HasTransform;
|
||||||
public bool IsReparented;
|
public bool IsReparented;
|
||||||
public bool HasNetworkVariables;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HeaderData Header;
|
public HeaderData Header;
|
||||||
@@ -979,10 +897,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Header.HasNetworkVariables)
|
OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
|
||||||
{
|
|
||||||
OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void Deserialize(FastBufferReader reader)
|
public unsafe void Deserialize(FastBufferReader reader)
|
||||||
@@ -1022,7 +937,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal SceneObject GetMessageSceneObject(ulong targetClientId, bool includeNetworkVariableData = true)
|
internal SceneObject GetMessageSceneObject(ulong targetClientId)
|
||||||
{
|
{
|
||||||
var obj = new SceneObject
|
var obj = new SceneObject
|
||||||
{
|
{
|
||||||
@@ -1033,7 +948,6 @@ namespace Unity.Netcode
|
|||||||
OwnerClientId = OwnerClientId,
|
OwnerClientId = OwnerClientId,
|
||||||
IsSceneObject = IsSceneObject ?? true,
|
IsSceneObject = IsSceneObject ?? true,
|
||||||
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
||||||
HasNetworkVariables = includeNetworkVariableData
|
|
||||||
},
|
},
|
||||||
OwnerObject = this,
|
OwnerObject = this,
|
||||||
TargetClientId = targetClientId
|
TargetClientId = targetClientId
|
||||||
@@ -1142,8 +1056,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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal class ConnectionRtt
|
|
||||||
{
|
|
||||||
private double[] m_RttSendTimes; // times at which packet were sent for RTT computations
|
|
||||||
private int[] m_SendSequence; // tick, or other key, at which packets were sent (to allow matching)
|
|
||||||
private double[] m_MeasuredLatencies; // measured latencies (ring buffer)
|
|
||||||
private int m_LatenciesBegin = 0; // ring buffer begin
|
|
||||||
private int m_LatenciesEnd = 0; // ring buffer end
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Round-trip-time data
|
|
||||||
/// </summary>
|
|
||||||
public struct Rtt
|
|
||||||
{
|
|
||||||
public double BestSec; // best RTT
|
|
||||||
public double AverageSec; // average RTT
|
|
||||||
public double WorstSec; // worst RTT
|
|
||||||
public double LastSec; // latest ack'ed RTT
|
|
||||||
public int SampleCount; // number of contributing samples
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionRtt()
|
|
||||||
{
|
|
||||||
m_RttSendTimes = new double[NetworkConfig.RttWindowSize];
|
|
||||||
m_SendSequence = new int[NetworkConfig.RttWindowSize];
|
|
||||||
m_MeasuredLatencies = new double[NetworkConfig.RttWindowSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the Round-trip-time computation for this client
|
|
||||||
/// </summary>
|
|
||||||
public Rtt GetRtt()
|
|
||||||
{
|
|
||||||
var ret = new Rtt();
|
|
||||||
var index = m_LatenciesBegin;
|
|
||||||
double total = 0.0;
|
|
||||||
ret.BestSec = m_MeasuredLatencies[m_LatenciesBegin];
|
|
||||||
ret.WorstSec = m_MeasuredLatencies[m_LatenciesBegin];
|
|
||||||
|
|
||||||
while (index != m_LatenciesEnd)
|
|
||||||
{
|
|
||||||
total += m_MeasuredLatencies[index];
|
|
||||||
ret.SampleCount++;
|
|
||||||
ret.BestSec = Math.Min(ret.BestSec, m_MeasuredLatencies[index]);
|
|
||||||
ret.WorstSec = Math.Max(ret.WorstSec, m_MeasuredLatencies[index]);
|
|
||||||
index = (index + 1) % NetworkConfig.RttAverageSamples;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret.SampleCount != 0)
|
|
||||||
{
|
|
||||||
ret.AverageSec = total / ret.SampleCount;
|
|
||||||
// the latest RTT is one before m_LatenciesEnd
|
|
||||||
ret.LastSec = m_MeasuredLatencies[(m_LatenciesEnd + (NetworkConfig.RttWindowSize - 1)) % NetworkConfig.RttWindowSize];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret.AverageSec = 0;
|
|
||||||
ret.BestSec = 0;
|
|
||||||
ret.WorstSec = 0;
|
|
||||||
ret.SampleCount = 0;
|
|
||||||
ret.LastSec = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void NotifySend(int sequence, double timeSec)
|
|
||||||
{
|
|
||||||
m_RttSendTimes[sequence % NetworkConfig.RttWindowSize] = timeSec;
|
|
||||||
m_SendSequence[sequence % NetworkConfig.RttWindowSize] = sequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void NotifyAck(int sequence, double timeSec)
|
|
||||||
{
|
|
||||||
// if the same slot was not used by a later send
|
|
||||||
if (m_SendSequence[sequence % NetworkConfig.RttWindowSize] == sequence)
|
|
||||||
{
|
|
||||||
double latency = timeSec - m_RttSendTimes[sequence % NetworkConfig.RttWindowSize];
|
|
||||||
|
|
||||||
m_MeasuredLatencies[m_LatenciesEnd] = latency;
|
|
||||||
m_LatenciesEnd = (m_LatenciesEnd + 1) % NetworkConfig.RttAverageSamples;
|
|
||||||
|
|
||||||
if (m_LatenciesEnd == m_LatenciesBegin)
|
|
||||||
{
|
|
||||||
m_LatenciesBegin = (m_LatenciesBegin + 1) % NetworkConfig.RttAverageSamples;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -14,9 +14,9 @@ namespace Unity.Netcode
|
|||||||
public static LogLevel CurrentLogLevel => NetworkManager.Singleton == null ? LogLevel.Normal : NetworkManager.Singleton.LogLevel;
|
public static LogLevel CurrentLogLevel => NetworkManager.Singleton == null ? LogLevel.Normal : NetworkManager.Singleton.LogLevel;
|
||||||
|
|
||||||
// internal logging
|
// internal logging
|
||||||
internal static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
|
public static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
|
||||||
internal static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
|
public static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
|
||||||
internal static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
|
public static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs an info log locally and on the server if possible.
|
/// Logs an info log locally and on the server if possible.
|
||||||
@@ -62,10 +62,9 @@ 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.ServerClientId);
|
||||||
NetworkManager.Singleton.ServerClientId);
|
|
||||||
|
|
||||||
NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.Singleton.ServerClientId, (uint)logType, size);
|
NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.ServerClientId, (uint)logType, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,40 +10,53 @@ 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;
|
||||||
{
|
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
||||||
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, context);
|
var originalOwner = networkObject.OwnerClientId;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (networkObject.OwnerClientId == networkManager.LocalClientId)
|
|
||||||
{
|
|
||||||
//We are current owner.
|
|
||||||
networkObject.InvokeBehaviourOnLostOwnership();
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.OwnerClientId = OwnerClientId;
|
networkObject.OwnerClientId = OwnerClientId;
|
||||||
|
|
||||||
|
// We are current owner.
|
||||||
|
if (originalOwner == networkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
networkObject.InvokeBehaviourOnLostOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are new owner.
|
||||||
if (OwnerClientId == networkManager.LocalClientId)
|
if (OwnerClientId == networkManager.LocalClientId)
|
||||||
{
|
{
|
||||||
//We are new owner.
|
|
||||||
networkObject.InvokeBehaviourOnGainedOwnership();
|
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||||
}
|
}
|
||||||
|
|
||||||
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(senderId, networkObject, messageSize);
|
// For all other clients that are neither the former or current owner, update the behaviours' properties
|
||||||
|
if (OwnerClientId != networkManager.LocalClientId && originalOwner != networkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < networkObject.ChildNetworkBehaviours.Count; i++)
|
||||||
|
{
|
||||||
|
networkObject.ChildNetworkBehaviours[i].UpdateNetworkProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, context.SerializedHeaderSize);
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(Hash, context.SenderId, m_ReceiveData, context.SerializedHeaderSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,27 +16,29 @@ namespace Unity.Netcode
|
|||||||
public ushort NetworkBehaviourIndex;
|
public ushort NetworkBehaviourIndex;
|
||||||
|
|
||||||
public HashSet<int> DeliveryMappedNetworkVariableIndex;
|
public HashSet<int> DeliveryMappedNetworkVariableIndex;
|
||||||
public ulong ClientId;
|
public ulong TargetClientId;
|
||||||
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 i = 0; i < NetworkBehaviour.NetworkVariableFields.Count; i++)
|
||||||
{
|
{
|
||||||
if (!DeliveryMappedNetworkVariableIndex.Contains(k))
|
if (!DeliveryMappedNetworkVariableIndex.Contains(i))
|
||||||
{
|
{
|
||||||
// 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
|
||||||
{
|
{
|
||||||
@@ -46,15 +48,17 @@ namespace Unity.Netcode
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if I'm dirty AND a client, write (server always has all permissions)
|
var startingSize = writer.Length;
|
||||||
// if I'm dirty AND the server AND the client can read me, send.
|
var networkVariable = NetworkBehaviour.NetworkVariableFields[i];
|
||||||
bool shouldWrite = NetworkBehaviour.NetworkVariableFields[k].ShouldWrite(ClientId, NetworkBehaviour.NetworkManager.IsServer);
|
var shouldWrite = networkVariable.IsDirty() &&
|
||||||
|
networkVariable.CanClientRead(TargetClientId) &&
|
||||||
|
(NetworkBehaviour.NetworkManager.IsServer || networkVariable.CanClientWrite(NetworkBehaviour.NetworkManager.LocalClientId));
|
||||||
|
|
||||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
if (!shouldWrite)
|
if (!shouldWrite)
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe((ushort)0);
|
BytePacker.WriteValueBitPacked(writer, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -66,56 +70,62 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, short.MaxValue);
|
var tempWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, MessagingSystem.FRAGMENTED_MESSAGE_MAX_SIZE);
|
||||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(tmpWriter);
|
NetworkBehaviour.NetworkVariableFields[i].WriteDelta(tempWriter);
|
||||||
|
BytePacker.WriteValueBitPacked(writer, tempWriter.Length);
|
||||||
|
|
||||||
writer.WriteValueSafe((ushort)tmpWriter.Length);
|
if (!writer.TryBeginWrite(tempWriter.Length))
|
||||||
tmpWriter.CopyTo(writer);
|
{
|
||||||
|
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
tempWriter.CopyTo(writer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(writer);
|
networkVariable.WriteDelta(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(k))
|
if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(i))
|
||||||
{
|
{
|
||||||
NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(k);
|
NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(i);
|
||||||
NetworkBehaviour.NetworkVariableIndexesToReset.Add(k);
|
NetworkBehaviour.NetworkVariableIndexesToReset.Add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
||||||
ClientId,
|
TargetClientId,
|
||||||
NetworkBehaviour.NetworkObject,
|
NetworkBehaviour.NetworkObject,
|
||||||
NetworkBehaviour.NetworkVariableFields[k].Name,
|
networkVariable.Name,
|
||||||
NetworkBehaviour.__getTypeName(),
|
NetworkBehaviour.__getTypeName(),
|
||||||
writer.Length);
|
writer.Length - startingSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(NetworkBehaviourIndex);
|
||||||
|
|
||||||
if (behaviour == null)
|
if (networkBehaviour == null)
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||||
{
|
{
|
||||||
@@ -124,13 +134,12 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < behaviour.NetworkVariableFields.Count; i++)
|
for (int i = 0; i < networkBehaviour.NetworkVariableFields.Count; i++)
|
||||||
{
|
{
|
||||||
ushort varSize = 0;
|
int varSize = 0;
|
||||||
|
|
||||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out varSize);
|
ByteUnpacker.ReadValueBitPacked(m_ReceivedNetworkVariableData, out varSize);
|
||||||
|
|
||||||
if (varSize == 0)
|
if (varSize == 0)
|
||||||
{
|
{
|
||||||
@@ -139,25 +148,27 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reader.ReadValueSafe(out bool deltaExists);
|
m_ReceivedNetworkVariableData.ReadValueSafe(out bool deltaExists);
|
||||||
if (!deltaExists)
|
if (!deltaExists)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (networkManager.IsServer)
|
var networkVariable = networkBehaviour.NetworkVariableFields[i];
|
||||||
|
|
||||||
|
if (networkManager.IsServer && !networkVariable.CanClientWrite(context.SenderId))
|
||||||
{
|
{
|
||||||
// we are choosing not to fire an exception here, because otherwise a malicious client could use this to crash the server
|
// we are choosing not to fire an exception here, because otherwise a malicious client could use this to crash the server
|
||||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogWarning($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
NetworkLog.LogWarning($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(networkBehaviour)} - VariableIndex: {i}");
|
||||||
NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
NetworkLog.LogError($"[{networkVariable.GetType().Name}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Seek(reader.Position + varSize);
|
m_ReceivedNetworkVariableData.Seek(m_ReceivedNetworkVariableData.Position + varSize);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,47 +179,45 @@ namespace Unity.Netcode
|
|||||||
//A dummy read COULD be added to the interface for this situation, but it's just being too nice.
|
//A dummy read COULD be added to the interface for this situation, but it's just being too nice.
|
||||||
//This is after all a developer fault. A critical error should be fine.
|
//This is after all a developer fault. A critical error should be fine.
|
||||||
// - TwoTen
|
// - TwoTen
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||||
{
|
{
|
||||||
NetworkLog.LogError($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. No more variables can be read. This is critical. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
NetworkLog.LogError($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. No more variables can be read. This is critical. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(networkBehaviour)} - VariableIndex: {i}");
|
||||||
NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
NetworkLog.LogError($"[{networkVariable.GetType().Name}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int readStartPos = reader.Position;
|
int readStartPos = m_ReceivedNetworkVariableData.Position;
|
||||||
|
|
||||||
behaviour.NetworkVariableFields[i].ReadDelta(reader, networkManager.IsServer);
|
networkVariable.ReadDelta(m_ReceivedNetworkVariableData, networkManager.IsServer);
|
||||||
|
|
||||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
||||||
senderId,
|
context.SenderId,
|
||||||
networkObject,
|
networkObject,
|
||||||
behaviour.NetworkVariableFields[i].Name,
|
networkVariable.Name,
|
||||||
behaviour.__getTypeName(),
|
networkBehaviour.__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(networkBehaviour)} - 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(networkBehaviour)} - 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 +225,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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,172 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Unity.Collections;
|
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Unity.Netcode
|
|
||||||
{
|
|
||||||
internal struct SnapshotDataMessage : INetworkMessage
|
|
||||||
{
|
|
||||||
public int CurrentTick;
|
|
||||||
public ushort Sequence;
|
|
||||||
|
|
||||||
public ushort Range;
|
|
||||||
|
|
||||||
public byte[] SendMainBuffer;
|
|
||||||
public NativeArray<byte> ReceiveMainBuffer;
|
|
||||||
|
|
||||||
public struct AckData
|
|
||||||
{
|
|
||||||
public ushort LastReceivedSequence;
|
|
||||||
public ushort ReceivedSequenceMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AckData Ack;
|
|
||||||
|
|
||||||
public struct EntryData
|
|
||||||
{
|
|
||||||
public ulong NetworkObjectId;
|
|
||||||
public ushort BehaviourIndex;
|
|
||||||
public ushort VariableIndex;
|
|
||||||
public int TickWritten;
|
|
||||||
public ushort Position;
|
|
||||||
public ushort Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeList<EntryData> Entries;
|
|
||||||
|
|
||||||
public struct SpawnData
|
|
||||||
{
|
|
||||||
public ulong NetworkObjectId;
|
|
||||||
public uint Hash;
|
|
||||||
public bool IsSceneObject;
|
|
||||||
|
|
||||||
public bool IsPlayerObject;
|
|
||||||
public ulong OwnerClientId;
|
|
||||||
public ulong ParentNetworkId;
|
|
||||||
public Vector3 Position;
|
|
||||||
public Quaternion Rotation;
|
|
||||||
public Vector3 Scale;
|
|
||||||
|
|
||||||
public int TickWritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeList<SpawnData> Spawns;
|
|
||||||
|
|
||||||
public struct DespawnData
|
|
||||||
{
|
|
||||||
public ulong NetworkObjectId;
|
|
||||||
public int TickWritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NativeList<DespawnData> Despawns;
|
|
||||||
|
|
||||||
public unsafe void Serialize(FastBufferWriter writer)
|
|
||||||
{
|
|
||||||
if (!writer.TryBeginWrite(
|
|
||||||
FastBufferWriter.GetWriteSize(CurrentTick) +
|
|
||||||
FastBufferWriter.GetWriteSize(Sequence) +
|
|
||||||
FastBufferWriter.GetWriteSize(Range) + Range +
|
|
||||||
FastBufferWriter.GetWriteSize(Ack) +
|
|
||||||
FastBufferWriter.GetWriteSize<ushort>() +
|
|
||||||
Entries.Length * sizeof(EntryData) +
|
|
||||||
FastBufferWriter.GetWriteSize<ushort>() +
|
|
||||||
Spawns.Length * sizeof(SpawnData) +
|
|
||||||
FastBufferWriter.GetWriteSize<ushort>() +
|
|
||||||
Despawns.Length * sizeof(DespawnData)
|
|
||||||
))
|
|
||||||
{
|
|
||||||
Entries.Dispose();
|
|
||||||
Spawns.Dispose();
|
|
||||||
Despawns.Dispose();
|
|
||||||
throw new OverflowException($"Not enough space to serialize {nameof(SnapshotDataMessage)}");
|
|
||||||
}
|
|
||||||
writer.WriteValue(CurrentTick);
|
|
||||||
writer.WriteValue(Sequence);
|
|
||||||
|
|
||||||
writer.WriteValue(Range);
|
|
||||||
writer.WriteBytes(SendMainBuffer, Range);
|
|
||||||
writer.WriteValue(Ack);
|
|
||||||
|
|
||||||
writer.WriteValue((ushort)Entries.Length);
|
|
||||||
writer.WriteBytes((byte*)Entries.GetUnsafePtr(), Entries.Length * sizeof(EntryData));
|
|
||||||
|
|
||||||
writer.WriteValue((ushort)Spawns.Length);
|
|
||||||
writer.WriteBytes((byte*)Spawns.GetUnsafePtr(), Spawns.Length * sizeof(SpawnData));
|
|
||||||
|
|
||||||
writer.WriteValue((ushort)Despawns.Length);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
var message = new SnapshotDataMessage();
|
|
||||||
if (!reader.TryBeginRead(
|
|
||||||
FastBufferWriter.GetWriteSize(message.CurrentTick) +
|
|
||||||
FastBufferWriter.GetWriteSize(message.Sequence) +
|
|
||||||
FastBufferWriter.GetWriteSize(message.Range)
|
|
||||||
))
|
|
||||||
{
|
|
||||||
throw new OverflowException($"Not enough space to deserialize {nameof(SnapshotDataMessage)}");
|
|
||||||
}
|
|
||||||
reader.ReadValue(out message.CurrentTick);
|
|
||||||
reader.ReadValue(out message.Sequence);
|
|
||||||
|
|
||||||
reader.ReadValue(out message.Range);
|
|
||||||
message.ReceiveMainBuffer = new NativeArray<byte>(message.Range, Allocator.Temp);
|
|
||||||
reader.ReadBytesSafe((byte*)message.ReceiveMainBuffer.GetUnsafePtr(), message.Range);
|
|
||||||
reader.ReadValueSafe(out message.Ack);
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out ushort length);
|
|
||||||
message.Entries = new NativeList<EntryData>(length, Allocator.Temp);
|
|
||||||
message.Entries.Length = length;
|
|
||||||
reader.ReadBytesSafe((byte*)message.Entries.GetUnsafePtr(), message.Entries.Length * sizeof(EntryData));
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out length);
|
|
||||||
message.Spawns = new NativeList<SpawnData>(length, Allocator.Temp);
|
|
||||||
message.Spawns.Length = length;
|
|
||||||
reader.ReadBytesSafe((byte*)message.Spawns.GetUnsafePtr(), message.Spawns.Length * sizeof(SpawnData));
|
|
||||||
|
|
||||||
reader.ReadValueSafe(out length);
|
|
||||||
message.Despawns = new NativeList<DespawnData>(length, Allocator.Temp);
|
|
||||||
message.Despawns.Length = length;
|
|
||||||
reader.ReadBytesSafe((byte*)message.Despawns.GetUnsafePtr(), message.Despawns.Length * sizeof(DespawnData));
|
|
||||||
|
|
||||||
using (message.ReceiveMainBuffer)
|
|
||||||
using (message.Entries)
|
|
||||||
using (message.Spawns)
|
|
||||||
using (message.Despawns)
|
|
||||||
{
|
|
||||||
message.Handle(context.SenderId, context.SystemOwner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(ulong senderId, object systemOwner)
|
|
||||||
{
|
|
||||||
if (systemOwner is NetworkManager)
|
|
||||||
{
|
|
||||||
var networkManager = (NetworkManager)systemOwner;
|
|
||||||
|
|
||||||
// todo: temporary hack around bug
|
|
||||||
if (!networkManager.IsServer)
|
|
||||||
{
|
|
||||||
senderId = networkManager.ServerClientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
var snapshotSystem = networkManager.SnapshotSystem;
|
|
||||||
snapshotSystem.HandleSnapshot(senderId, this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var ownerData = (Tuple<SnapshotSystem, ulong>)systemOwner;
|
|
||||||
var snapshotSystem = ownerData.Item1;
|
|
||||||
snapshotSystem.HandleSnapshot(ownerData.Item2, this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5cf75026c2ab86646aac16b39d7259ad
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -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, context.SerializedHeaderSize);
|
m_ReceivedData = reader;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ref NetworkContext context)
|
||||||
|
{
|
||||||
|
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeUnnamedMessage(context.SenderId, m_ReceivedData, context.SerializedHeaderSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ 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);
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
public const int NON_FRAGMENTED_MESSAGE_MAX_SIZE = 1300;
|
||||||
public const int FRAGMENTED_MESSAGE_MAX_SIZE = int.MaxValue;
|
public const int FRAGMENTED_MESSAGE_MAX_SIZE = BytePacker.BitPackedIntMax;
|
||||||
|
|
||||||
internal struct MessageWithHandler
|
internal struct MessageWithHandler
|
||||||
{
|
{
|
||||||
@@ -118,7 +118,7 @@ namespace Unity.Netcode
|
|||||||
for (var queueIndex = 0; queueIndex < m_IncomingMessageQueue.Length; ++queueIndex)
|
for (var queueIndex = 0; queueIndex < m_IncomingMessageQueue.Length; ++queueIndex)
|
||||||
{
|
{
|
||||||
// Avoid copies...
|
// Avoid copies...
|
||||||
ref var item = ref m_IncomingMessageQueue.GetUnsafeList()->ElementAt(queueIndex);
|
ref var item = ref m_IncomingMessageQueue.ElementAt(queueIndex);
|
||||||
item.Reader.Dispose();
|
item.Reader.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +236,7 @@ namespace Unity.Netcode
|
|||||||
Timestamp = timestamp,
|
Timestamp = timestamp,
|
||||||
Header = header,
|
Header = header,
|
||||||
SerializedHeaderSize = serializedHeaderSize,
|
SerializedHeaderSize = serializedHeaderSize,
|
||||||
|
MessageSize = header.MessageSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
var type = m_ReverseTypeMap[header.MessageType];
|
var type = m_ReverseTypeMap[header.MessageType];
|
||||||
@@ -260,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)
|
||||||
{
|
{
|
||||||
@@ -278,7 +279,7 @@ namespace Unity.Netcode
|
|||||||
for (var index = 0; index < m_IncomingMessageQueue.Length; ++index)
|
for (var index = 0; index < m_IncomingMessageQueue.Length; ++index)
|
||||||
{
|
{
|
||||||
// Avoid copies...
|
// Avoid copies...
|
||||||
ref var item = ref m_IncomingMessageQueue.GetUnsafeList()->ElementAt(index);
|
ref var item = ref m_IncomingMessageQueue.ElementAt(index);
|
||||||
HandleMessage(item.Header, item.Reader, item.SenderId, item.Timestamp, item.MessageHeaderSerializedSize);
|
HandleMessage(item.Header, item.Reader, item.SenderId, item.Timestamp, item.MessageHeaderSerializedSize);
|
||||||
if (m_Disposed)
|
if (m_Disposed)
|
||||||
{
|
{
|
||||||
@@ -313,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)
|
||||||
@@ -332,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>
|
||||||
{
|
{
|
||||||
@@ -347,11 +367,17 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
message.Serialize(tmpSerializer);
|
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);
|
using var headerSerializer = new FastBufferWriter(FastBufferWriter.GetWriteSize<MessageHeader>(), Allocator.Temp);
|
||||||
|
|
||||||
var header = new MessageHeader
|
var header = new MessageHeader
|
||||||
{
|
{
|
||||||
MessageSize = (ushort)tmpSerializer.Length,
|
MessageSize = (uint)tmpSerializer.Length,
|
||||||
MessageType = m_MessageTypes[typeof(TMessageType)],
|
MessageType = m_MessageTypes[typeof(TMessageType)],
|
||||||
};
|
};
|
||||||
BytePacker.WriteValueBitPacked(headerSerializer, header.MessageType);
|
BytePacker.WriteValueBitPacked(headerSerializer, header.MessageType);
|
||||||
@@ -368,7 +394,7 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
m_Hooks[hookIdx].OnBeforeSendMessage(clientId, typeof(TMessageType), delivery);
|
m_Hooks[hookIdx].OnBeforeSendMessage(clientId, ref message, delivery);
|
||||||
}
|
}
|
||||||
|
|
||||||
var sendQueueItem = m_SendQueues[clientId];
|
var sendQueueItem = m_SendQueues[clientId];
|
||||||
@@ -376,22 +402,22 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
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(0).Writer.Seek(sizeof(BatchHeader));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ref var lastQueueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1);
|
ref var lastQueueItem = ref sendQueueItem.ElementAt(sendQueueItem.Length - 1);
|
||||||
if (lastQueueItem.NetworkDelivery != delivery ||
|
if (lastQueueItem.NetworkDelivery != delivery ||
|
||||||
lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position
|
lastQueueItem.Writer.MaxCapacity - lastQueueItem.Writer.Position
|
||||||
< tmpSerializer.Length + headerSerializer.Length)
|
< 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(sendQueueItem.Length - 1).Writer.Seek(sizeof(BatchHeader));
|
sendQueueItem.ElementAt(sendQueueItem.Length - 1).Writer.Seek(sizeof(BatchHeader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var writeQueueItem = ref sendQueueItem.GetUnsafeList()->ElementAt(sendQueueItem.Length - 1);
|
ref var writeQueueItem = ref sendQueueItem.ElementAt(sendQueueItem.Length - 1);
|
||||||
writeQueueItem.Writer.TryBeginWrite(tmpSerializer.Length + headerSerializer.Length);
|
writeQueueItem.Writer.TryBeginWrite(tmpSerializer.Length + headerSerializer.Length);
|
||||||
|
|
||||||
writeQueueItem.Writer.WriteBytes(headerSerializer.GetUnsafePtr(), headerSerializer.Length);
|
writeQueueItem.Writer.WriteBytes(headerSerializer.GetUnsafePtr(), headerSerializer.Length);
|
||||||
@@ -399,13 +425,20 @@ namespace Unity.Netcode
|
|||||||
writeQueueItem.BatchHeader.BatchSize++;
|
writeQueueItem.BatchHeader.BatchSize++;
|
||||||
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
|
||||||
{
|
{
|
||||||
m_Hooks[hookIdx].OnAfterSendMessage(clientId, typeof(TMessageType), delivery, tmpSerializer.Length + headerSerializer.Length);
|
m_Hooks[hookIdx].OnAfterSendMessage(clientId, ref message, delivery, tmpSerializer.Length + headerSerializer.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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>
|
||||||
where T : unmanaged
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
@@ -441,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()
|
||||||
@@ -469,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();
|
||||||
|
|||||||
@@ -30,5 +30,10 @@ namespace Unity.Netcode
|
|||||||
/// The actual serialized size of the header when packed into the buffer
|
/// The actual serialized size of the header when packed into the buffer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int SerializedHeaderSize;
|
public int SerializedHeaderSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the message in the buffer, header excluded
|
||||||
|
/// </summary>
|
||||||
|
public uint MessageSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,18 @@ 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 UpdateRttToServer(int rtt);
|
||||||
|
|
||||||
|
void UpdateNetworkObjectsCount(int count);
|
||||||
|
|
||||||
|
void UpdateConnectionsCount(int count);
|
||||||
|
|
||||||
|
void UpdatePacketLoss(float packetLoss);
|
||||||
|
|
||||||
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,30 @@ 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_7
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
private readonly Gauge m_NetworkObjectsGauge = new Gauge(NetworkMetricTypes.NetworkObjects.Id)
|
||||||
|
{
|
||||||
|
ShouldResetOnDispatch = true,
|
||||||
|
};
|
||||||
|
private readonly Gauge m_ConnectionsGauge = new Gauge(NetworkMetricTypes.ConnectedClients.Id)
|
||||||
|
{
|
||||||
|
ShouldResetOnDispatch = true,
|
||||||
|
};
|
||||||
|
private readonly Gauge m_PacketLossGauge = new Gauge(NetworkMetricTypes.PacketLoss.Id);
|
||||||
|
#endif
|
||||||
|
|
||||||
private ulong m_NumberOfMetricsThisFrame;
|
private ulong m_NumberOfMetricsThisFrame;
|
||||||
|
|
||||||
public NetworkMetrics()
|
public NetworkMetrics()
|
||||||
@@ -79,6 +106,13 @@ 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_7
|
||||||
|
.WithCounters(m_PacketSentCounter, m_PacketReceivedCounter)
|
||||||
|
.WithGauges(m_RttToServerGauge)
|
||||||
|
.WithGauges(m_NetworkObjectsGauge)
|
||||||
|
.WithGauges(m_ConnectionsGauge)
|
||||||
|
.WithGauges(m_PacketLossGauge)
|
||||||
|
#endif
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Dispatcher.RegisterObserver(NetcodeObserver.Observer);
|
Dispatcher.RegisterObserver(NetcodeObserver.Observer);
|
||||||
@@ -404,9 +438,85 @@ namespace Unity.Netcode
|
|||||||
IncrementMetricCount();
|
IncrementMetricCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TrackPacketSent(uint packetCount)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PacketSentCounter.Increment(packetCount);
|
||||||
|
IncrementMetricCount();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TrackPacketReceived(uint packetCount)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PacketReceivedCounter.Increment(packetCount);
|
||||||
|
IncrementMetricCount();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateRttToServer(int rttMilliseconds)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var rttSeconds = rttMilliseconds * 1e-3;
|
||||||
|
m_RttToServerGauge.Set(rttSeconds);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNetworkObjectsCount(int count)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_NetworkObjectsGauge.Set(count);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConnectionsCount(int count)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ConnectionsGauge.Set(count);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePacketLoss(float packetLoss)
|
||||||
|
{
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
if (!CanSendMetrics)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PacketLossGauge.Set(packetLoss);
|
||||||
|
#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 +531,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,30 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TrackPacketSent(uint packetCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TrackPacketReceived(uint packetCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateRttToServer(int rtt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNetworkObjectsCount(int count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateConnectionsCount(int count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePacketLoss(float packetLoss)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void DispatchFrame()
|
public void DispatchFrame()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,12 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event OnListChangedDelegate OnListChanged;
|
public event OnListChangedDelegate OnListChanged;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkList with the default value and settings
|
|
||||||
/// </summary>
|
|
||||||
public NetworkList() { }
|
public NetworkList() { }
|
||||||
|
|
||||||
/// <summary>
|
public NetworkList(IEnumerable<T> values = default,
|
||||||
/// Creates a NetworkList with the default value and custom settings
|
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||||
/// </summary>
|
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||||
/// <param name="readPerm">The read permission to use for the NetworkList</param>
|
: base(readPerm, writePerm)
|
||||||
/// <param name="values">The initial value to use for the NetworkList</param>
|
|
||||||
public NetworkList(NetworkVariableReadPermission readPerm, IEnumerable<T> values) : base(readPerm)
|
|
||||||
{
|
{
|
||||||
foreach (var value in values)
|
foreach (var value in values)
|
||||||
{
|
{
|
||||||
@@ -42,19 +37,6 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkList with a custom value and the default settings
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="values">The initial value to use for the NetworkList</param>
|
|
||||||
public NetworkList(IEnumerable<T> values)
|
|
||||||
{
|
|
||||||
foreach (var value in values)
|
|
||||||
{
|
|
||||||
m_List.Add(value);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ResetDirty()
|
public override void ResetDirty()
|
||||||
{
|
{
|
||||||
@@ -90,18 +72,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 +94,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 +112,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 +123,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 +139,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 +166,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 +193,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,7 +253,7 @@ 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)
|
||||||
{
|
{
|
||||||
throw new Exception("Shouldn't be here, index is higher than list length");
|
throw new Exception("Shouldn't be here, index is higher than list length");
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
|
||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
@@ -10,7 +12,7 @@ namespace Unity.Netcode
|
|||||||
public class NetworkVariable<T> : NetworkVariableBase where T : unmanaged
|
public class NetworkVariable<T> : NetworkVariableBase where T : unmanaged
|
||||||
{
|
{
|
||||||
// Functions that know how to serialize INetworkSerializable
|
// Functions that know how to serialize INetworkSerializable
|
||||||
internal static void WriteNetworkSerializable<TForMethod>(FastBufferWriter writer, ref TForMethod value)
|
internal static void WriteNetworkSerializable<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||||
where TForMethod : INetworkSerializable, new()
|
where TForMethod : INetworkSerializable, new()
|
||||||
{
|
{
|
||||||
writer.WriteNetworkSerializable(value);
|
writer.WriteNetworkSerializable(value);
|
||||||
@@ -22,7 +24,8 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Functions that serialize other types
|
// Functions that serialize other types
|
||||||
private static void WriteValue<TForMethod>(FastBufferWriter writer, ref TForMethod value) where TForMethod : unmanaged
|
private static void WriteValue<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||||
|
where TForMethod : unmanaged
|
||||||
{
|
{
|
||||||
writer.WriteValueSafe(value);
|
writer.WriteValueSafe(value);
|
||||||
}
|
}
|
||||||
@@ -33,20 +36,17 @@ namespace Unity.Netcode
|
|||||||
reader.ReadValueSafe(out value);
|
reader.ReadValueSafe(out value);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal delegate void WriteDelegate<TForMethod>(FastBufferWriter writer, ref TForMethod value);
|
internal delegate void WriteDelegate<TForMethod>(FastBufferWriter writer, in TForMethod value);
|
||||||
|
|
||||||
internal delegate void ReadDelegate<TForMethod>(FastBufferReader reader, out 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
|
// These static delegates provide the right implementation for writing and reading a particular network variable type.
|
||||||
// type.
|
|
||||||
//
|
|
||||||
// For most types, these default to WriteValue() and ReadValue(), which perform simple memcpy operations.
|
// 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()
|
// INetworkSerializableILPP will generate startup code that will set it to WriteNetworkSerializable()
|
||||||
// and ReadNetworkSerializable() for INetworkSerializable types, which will call NetworkSerialize().
|
// 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
|
// In the future we may be able to use this to provide packing implementations for floats and integers to optimize bandwidth usage.
|
||||||
// optimize bandwidth usage.
|
|
||||||
//
|
//
|
||||||
// The reason this is done is to avoid runtime reflection and boxing in NetworkVariable - without this,
|
// 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,
|
// NetworkVariable would need to do a `var is INetworkSerializable` check, and then cast to INetworkSerializable,
|
||||||
@@ -69,38 +69,11 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public OnValueChangedDelegate OnValueChanged;
|
public OnValueChangedDelegate OnValueChanged;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkVariable with the default value and custom read permission
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readPerm">The read permission for the NetworkVariable</param>
|
|
||||||
|
|
||||||
public NetworkVariable()
|
public NetworkVariable(T value = default,
|
||||||
{
|
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||||
}
|
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||||
|
: base(readPerm, writePerm)
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkVariable with the default value and custom read permission
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readPerm">The read permission for the NetworkVariable</param>
|
|
||||||
public NetworkVariable(NetworkVariableReadPermission readPerm) : base(readPerm)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkVariable with a custom value and custom settings
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readPerm">The read permission for the NetworkVariable</param>
|
|
||||||
/// <param name="value">The initial value to use for the NetworkVariable</param>
|
|
||||||
public NetworkVariable(NetworkVariableReadPermission readPerm, T value) : base(readPerm)
|
|
||||||
{
|
|
||||||
m_InternalValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a NetworkVariable with a custom value and the default read permission
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The initial value to use for the NetworkVariable</param>
|
|
||||||
public NetworkVariable(T value)
|
|
||||||
{
|
{
|
||||||
m_InternalValue = value;
|
m_InternalValue = value;
|
||||||
}
|
}
|
||||||
@@ -116,19 +89,36 @@ namespace Unity.Netcode
|
|||||||
get => m_InternalValue;
|
get => m_InternalValue;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// this could be improved. The Networking Manager is not always initialized here
|
// Compare bitwise
|
||||||
// Good place to decouple network manager from the network variable
|
if (ValueEquals(ref m_InternalValue, ref value))
|
||||||
|
|
||||||
// Also, note this is not really very water-tight, if you are running as a host
|
|
||||||
// we cannot tell if a NetworkVariable write is happening inside client-ish code
|
|
||||||
if (m_NetworkBehaviour && (m_NetworkBehaviour.NetworkManager.IsClient && !m_NetworkBehaviour.NetworkManager.IsHost))
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Client can't write to NetworkVariables");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_NetworkBehaviour && !CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Client is not allowed to write to this NetworkVariable");
|
||||||
|
}
|
||||||
|
|
||||||
Set(value);
|
Set(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compares two values of the same unmanaged type by underlying memory
|
||||||
|
// Ignoring any overriden value checks
|
||||||
|
// Size is fixed
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static unsafe bool ValueEquals(ref T a, ref T b)
|
||||||
|
{
|
||||||
|
// get unmanaged pointers
|
||||||
|
var aptr = UnsafeUtility.AddressOf(ref a);
|
||||||
|
var bptr = UnsafeUtility.AddressOf(ref b);
|
||||||
|
|
||||||
|
// compare addresses
|
||||||
|
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(T)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private protected void Set(T value)
|
private protected void Set(T value)
|
||||||
{
|
{
|
||||||
m_IsDirty = true;
|
m_IsDirty = true;
|
||||||
@@ -146,7 +136,6 @@ namespace Unity.Netcode
|
|||||||
WriteField(writer);
|
WriteField(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads value from the reader and applies it
|
/// Reads value from the reader and applies it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -174,7 +163,7 @@ namespace Unity.Netcode
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void WriteField(FastBufferWriter writer)
|
public override void WriteField(FastBufferWriter writer)
|
||||||
{
|
{
|
||||||
Write(writer, ref 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;
|
||||||
|
|
||||||
@@ -19,9 +19,15 @@ namespace Unity.Netcode
|
|||||||
m_NetworkBehaviour = networkBehaviour;
|
m_NetworkBehaviour = networkBehaviour;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected NetworkVariableBase(NetworkVariableReadPermission readPermIn = NetworkVariableReadPermission.Everyone)
|
public const NetworkVariableReadPermission DefaultReadPerm = NetworkVariableReadPermission.Everyone;
|
||||||
|
public const NetworkVariableWritePermission DefaultWritePerm = NetworkVariableWritePermission.Server;
|
||||||
|
|
||||||
|
protected NetworkVariableBase(
|
||||||
|
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||||
|
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||||
{
|
{
|
||||||
ReadPerm = readPermIn;
|
ReadPerm = readPerm;
|
||||||
|
WritePerm = writePerm;
|
||||||
}
|
}
|
||||||
|
|
||||||
private protected bool m_IsDirty;
|
private protected bool m_IsDirty;
|
||||||
@@ -37,6 +43,8 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly NetworkVariableReadPermission ReadPerm;
|
public readonly NetworkVariableReadPermission ReadPerm;
|
||||||
|
|
||||||
|
public readonly NetworkVariableWritePermission WritePerm;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets whether or not the variable needs to be delta synced
|
/// Sets whether or not the variable needs to be delta synced
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -62,26 +70,28 @@ namespace Unity.Netcode
|
|||||||
return m_IsDirty;
|
return m_IsDirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool ShouldWrite(ulong clientId, bool isServer)
|
|
||||||
{
|
|
||||||
return IsDirty() && isServer && CanClientRead(clientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets Whether or not a specific client can read to the varaible
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clientId">The clientId of the remote client</param>
|
|
||||||
/// <returns>Whether or not the client can read to the variable</returns>
|
|
||||||
public bool CanClientRead(ulong clientId)
|
public bool CanClientRead(ulong clientId)
|
||||||
{
|
{
|
||||||
switch (ReadPerm)
|
switch (ReadPerm)
|
||||||
{
|
{
|
||||||
|
default:
|
||||||
case NetworkVariableReadPermission.Everyone:
|
case NetworkVariableReadPermission.Everyone:
|
||||||
return true;
|
return true;
|
||||||
case NetworkVariableReadPermission.OwnerOnly:
|
case NetworkVariableReadPermission.Owner:
|
||||||
return m_NetworkBehaviour.OwnerClientId == clientId;
|
return clientId == m_NetworkBehaviour.NetworkObject.OwnerClientId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanClientWrite(ulong clientId)
|
||||||
|
{
|
||||||
|
switch (WritePerm)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case NetworkVariableWritePermission.Server:
|
||||||
|
return clientId == NetworkManager.ServerClientId;
|
||||||
|
case NetworkVariableWritePermission.Owner:
|
||||||
|
return clientId == m_NetworkBehaviour.NetworkObject.OwnerClientId;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -107,7 +117,6 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reader">The stream to read the delta from</param>
|
/// <param name="reader">The stream to read the delta from</param>
|
||||||
/// <param name="keepDirtyDelta">Whether or not the delta should be kept as dirty or consumed</param>
|
/// <param name="keepDirtyDelta">Whether or not the delta should be kept as dirty or consumed</param>
|
||||||
|
|
||||||
public abstract void ReadDelta(FastBufferReader reader, bool keepDirtyDelta);
|
public abstract void ReadDelta(FastBufferReader reader, bool keepDirtyDelta);
|
||||||
|
|
||||||
public virtual void Dispose()
|
public virtual void Dispose()
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
namespace Unity.Netcode
|
namespace Unity.Netcode
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Permission type
|
|
||||||
/// </summary>
|
|
||||||
public enum NetworkVariableReadPermission
|
public enum NetworkVariableReadPermission
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Everyone
|
|
||||||
/// </summary>
|
|
||||||
Everyone,
|
Everyone,
|
||||||
|
Owner,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
public enum NetworkVariableWritePermission
|
||||||
/// Owner-ownly
|
{
|
||||||
/// </summary>
|
Server,
|
||||||
OwnerOnly,
|
Owner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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: 69c3c1c5a885d4aed99ee2e1fa40f763
|
guid: de907a9fb8151e240800dbcc97f8e745
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -132,20 +132,14 @@ namespace Unity.Netcode
|
|||||||
private const NetworkDelivery k_DeliveryType = NetworkDelivery.ReliableFragmentedSequenced;
|
private const NetworkDelivery k_DeliveryType = NetworkDelivery.ReliableFragmentedSequenced;
|
||||||
internal const int InvalidSceneNameOrPath = -1;
|
internal const int InvalidSceneNameOrPath = -1;
|
||||||
|
|
||||||
// Used to be able to turn re-synchronization off for future snapshot development purposes.
|
// Used to be able to turn re-synchronization off
|
||||||
internal static bool DisableReSynchronization;
|
internal static bool DisableReSynchronization;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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>
|
||||||
@@ -469,8 +488,18 @@ namespace Unity.Netcode
|
|||||||
var scenePath = SceneUtility.GetScenePathByBuildIndex(i);
|
var scenePath = SceneUtility.GetScenePathByBuildIndex(i);
|
||||||
var hash = XXHash.Hash32(scenePath);
|
var hash = XXHash.Hash32(scenePath);
|
||||||
var buildIndex = SceneUtility.GetBuildIndexByScenePath(scenePath);
|
var buildIndex = SceneUtility.GetBuildIndexByScenePath(scenePath);
|
||||||
HashToBuildIndex.Add(hash, buildIndex);
|
|
||||||
BuildIndexToHash.Add(buildIndex, hash);
|
// In the rare-case scenario where a programmatically generated build has duplicate
|
||||||
|
// scene entries, we will log an error and skip the entry
|
||||||
|
if (!HashToBuildIndex.ContainsKey(hash))
|
||||||
|
{
|
||||||
|
HashToBuildIndex.Add(hash, buildIndex);
|
||||||
|
BuildIndexToHash.Add(buildIndex, hash);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError($"{nameof(NetworkSceneManager)} is skipping duplicate scene path entry {scenePath}. Make sure your scenes in build list does not contain duplicates!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +530,8 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception($"Scene Hash {sceneHash} does not exist in the {nameof(HashToBuildIndex)} table!");
|
throw new Exception($"Scene Hash {sceneHash} does not exist in the {nameof(HashToBuildIndex)} table! Verify that all scenes requiring" +
|
||||||
|
$" server to client synchronization are in the scenes in build list.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,10 +753,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>
|
||||||
@@ -789,7 +818,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);
|
||||||
}
|
}
|
||||||
@@ -818,7 +847,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;
|
||||||
@@ -845,12 +874,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
|
||||||
@@ -858,7 +887,7 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
SceneEventType = sceneEventProgress.SceneEventType,
|
SceneEventType = sceneEventProgress.SceneEventType,
|
||||||
SceneName = SceneNameFromHash(sceneEventProgress.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventProgress.SceneHash),
|
||||||
ClientId = m_NetworkManager.ServerClientId,
|
ClientId = NetworkManager.ServerClientId,
|
||||||
LoadSceneMode = sceneEventProgress.LoadSceneMode,
|
LoadSceneMode = sceneEventProgress.LoadSceneMode,
|
||||||
ClientsThatCompleted = sceneEventProgress.DoneClients,
|
ClientsThatCompleted = sceneEventProgress.DoneClients,
|
||||||
ClientsThatTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(),
|
ClientsThatTimedOut = m_NetworkManager.ConnectedClients.Keys.Except(sceneEventProgress.DoneClients).ToList(),
|
||||||
@@ -917,8 +946,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
|
||||||
@@ -928,10 +958,10 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = sceneName,
|
SceneName = sceneName,
|
||||||
ClientId = m_NetworkManager.ServerClientId // Server can only invoke this
|
ClientId = NetworkManager.ServerClientId // Server can only invoke this
|
||||||
});
|
});
|
||||||
|
|
||||||
OnUnload?.Invoke(m_NetworkManager.ServerClientId, sceneName, sceneUnload);
|
OnUnload?.Invoke(NetworkManager.ServerClientId, sceneName, sceneUnload);
|
||||||
|
|
||||||
//Return the status
|
//Return the status
|
||||||
return sceneEventProgress.Status;
|
return sceneEventProgress.Status;
|
||||||
@@ -948,8 +978,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];
|
||||||
@@ -960,22 +992,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
|
||||||
@@ -992,13 +1013,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>
|
||||||
@@ -1014,12 +1028,12 @@ namespace Unity.Netcode
|
|||||||
// Server sends the unload scene notification after unloading because it will despawn all scene relative in-scene NetworkObjects
|
// Server sends the unload scene notification after unloading because it will despawn all scene relative in-scene NetworkObjects
|
||||||
// If we send this event to all clients before the server is finished unloading they will get warning about an object being
|
// If we send this event to all clients before the server is finished unloading they will get warning about an object being
|
||||||
// despawned that no longer exists
|
// despawned that no longer exists
|
||||||
SendSceneEventData(sceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != m_NetworkManager.ServerClientId).ToArray());
|
SendSceneEventData(sceneEventId, m_NetworkManager.ConnectedClientsIds.Where(c => c != NetworkManager.ServerClientId).ToArray());
|
||||||
|
|
||||||
//Only if we are a host do we want register having loaded for the associated SceneEventProgress
|
//Only if we are a host do we want register having loaded for the associated SceneEventProgress
|
||||||
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1032,7 +1046,7 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||||
ClientId = m_NetworkManager.IsServer ? m_NetworkManager.ServerClientId : m_NetworkManager.LocalClientId
|
ClientId = m_NetworkManager.IsServer ? NetworkManager.ServerClientId : m_NetworkManager.LocalClientId
|
||||||
});
|
});
|
||||||
|
|
||||||
OnUnloadComplete?.Invoke(m_NetworkManager.LocalClientId, SceneNameFromHash(sceneEventData.SceneHash));
|
OnUnloadComplete?.Invoke(m_NetworkManager.LocalClientId, SceneNameFromHash(sceneEventData.SceneHash));
|
||||||
@@ -1040,12 +1054,17 @@ namespace Unity.Netcode
|
|||||||
// Clients send a notification back to the server they have completed the unload scene event
|
// Clients send a notification back to the server they have completed the unload scene event
|
||||||
if (!m_NetworkManager.IsServer)
|
if (!m_NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
@@ -1053,21 +1072,25 @@ 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
|
||||||
ClientId = m_NetworkManager.ServerClientId
|
ClientId = NetworkManager.ServerClientId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1103,8 +1126,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;
|
||||||
@@ -1119,12 +1142,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
|
||||||
@@ -1134,10 +1158,10 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = sceneName,
|
SceneName = sceneName,
|
||||||
ClientId = m_NetworkManager.ServerClientId
|
ClientId = NetworkManager.ServerClientId
|
||||||
});
|
});
|
||||||
|
|
||||||
OnLoad?.Invoke(m_NetworkManager.ServerClientId, sceneName, sceneEventData.LoadSceneMode, sceneLoad);
|
OnLoad?.Invoke(NetworkManager.ServerClientId, sceneName, sceneEventData.LoadSceneMode, sceneLoad);
|
||||||
|
|
||||||
//Return our scene progress instance
|
//Return our scene progress instance
|
||||||
return sceneEventProgress.Status;
|
return sceneEventProgress.Status;
|
||||||
@@ -1160,44 +1184,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
|
||||||
@@ -1210,8 +1203,8 @@ namespace Unity.Netcode
|
|||||||
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()
|
||||||
{
|
{
|
||||||
@@ -1230,10 +1223,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!");
|
||||||
@@ -1295,7 +1288,9 @@ namespace Unity.Netcode
|
|||||||
{
|
{
|
||||||
if (!keyValuePairBySceneHandle.Value.IsPlayerObject)
|
if (!keyValuePairBySceneHandle.Value.IsPlayerObject)
|
||||||
{
|
{
|
||||||
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value, m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, null, true);
|
// All in-scene placed NetworkObjects default to being owned by the server
|
||||||
|
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(keyValuePairBySceneHandle.Value,
|
||||||
|
m_NetworkManager.SpawnManager.GetNetworkObjectId(), true, false, NetworkManager.ServerClientId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1307,35 +1302,35 @@ namespace Unity.Netcode
|
|||||||
for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++)
|
for (int j = 0; j < m_NetworkManager.ConnectedClientsList.Count; j++)
|
||||||
{
|
{
|
||||||
var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId;
|
var clientId = m_NetworkManager.ConnectedClientsList[j].ClientId;
|
||||||
if (clientId != m_NetworkManager.ServerClientId)
|
if (clientId != NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
sceneEventData.TargetClientId = clientId;
|
sceneEventData.TargetClientId = clientId;
|
||||||
var message = new SceneEventMessage
|
var message = new SceneEventMessage
|
||||||
{
|
{
|
||||||
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()
|
||||||
{
|
{
|
||||||
SceneEventType = SceneEventType.LoadComplete,
|
SceneEventType = SceneEventType.LoadComplete,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||||
ClientId = m_NetworkManager.ServerClientId,
|
ClientId = NetworkManager.ServerClientId,
|
||||||
Scene = scene,
|
Scene = scene,
|
||||||
});
|
});
|
||||||
|
|
||||||
OnLoadComplete?.Invoke(m_NetworkManager.ServerClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
OnLoadComplete?.Invoke(NetworkManager.ServerClientId, SceneNameFromHash(sceneEventData.SceneHash), sceneEventData.LoadSceneMode);
|
||||||
|
|
||||||
//Second, only if we are a host do we want register having loaded for the associated SceneEventProgress
|
//Second, only if we are a host do we want register having loaded for the associated SceneEventProgress
|
||||||
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
if (SceneEventProgressTracking.ContainsKey(sceneEventData.SceneEventProgressId) && m_NetworkManager.IsHost)
|
||||||
{
|
{
|
||||||
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(m_NetworkManager.ServerClientId);
|
SceneEventProgressTracking[sceneEventData.SceneEventProgressId].AddClientAsDone(NetworkManager.ServerClientId);
|
||||||
}
|
}
|
||||||
EndSceneEvent(sceneEventId);
|
EndSceneEvent(sceneEventId);
|
||||||
}
|
}
|
||||||
@@ -1350,8 +1345,8 @@ namespace Unity.Netcode
|
|||||||
sceneEventData.DeserializeScenePlacedObjects();
|
sceneEventData.DeserializeScenePlacedObjects();
|
||||||
|
|
||||||
sceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
sceneEventData.SceneEventType = SceneEventType.LoadComplete;
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { 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()
|
||||||
@@ -1421,9 +1416,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()
|
||||||
@@ -1474,6 +1468,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;
|
||||||
|
|
||||||
@@ -1485,38 +1483,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1525,10 +1513,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())
|
||||||
@@ -1536,7 +1524,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)
|
||||||
@@ -1544,9 +1532,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
|
||||||
{
|
{
|
||||||
@@ -1561,16 +1549,16 @@ 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, NetworkManager.ServerClientId);
|
||||||
|
|
||||||
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(m_NetworkManager.ServerClientId, (uint)responseSceneEventData.SceneEventType, sceneName, size);
|
m_NetworkManager.NetworkMetrics.TrackSceneEventSent(NetworkManager.ServerClientId, (uint)responseSceneEventData.SceneEventType, sceneName, size);
|
||||||
|
|
||||||
EndSceneEvent(responseSceneEventData.SceneEventId);
|
EndSceneEvent(responseSceneEventData.SceneEventId);
|
||||||
|
|
||||||
@@ -1624,7 +1612,7 @@ namespace Unity.Netcode
|
|||||||
sceneEventData.SynchronizeSceneNetworkObjects(m_NetworkManager);
|
sceneEventData.SynchronizeSceneNetworkObjects(m_NetworkManager);
|
||||||
|
|
||||||
sceneEventData.SceneEventType = SceneEventType.SynchronizeComplete;
|
sceneEventData.SceneEventType = SceneEventType.SynchronizeComplete;
|
||||||
SendSceneEventData(sceneEventId, new ulong[] { m_NetworkManager.ServerClientId });
|
SendSceneEventData(sceneEventId, new ulong[] { NetworkManager.ServerClientId });
|
||||||
|
|
||||||
// All scenes are synchronized, let the server know we are done synchronizing
|
// All scenes are synchronized, let the server know we are done synchronizing
|
||||||
m_NetworkManager.IsConnectedClient = true;
|
m_NetworkManager.IsConnectedClient = true;
|
||||||
@@ -1651,7 +1639,7 @@ namespace Unity.Netcode
|
|||||||
OnSceneEvent?.Invoke(new SceneEvent()
|
OnSceneEvent?.Invoke(new SceneEvent()
|
||||||
{
|
{
|
||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
ClientId = m_NetworkManager.ServerClientId, // Server sent this to client
|
ClientId = NetworkManager.ServerClientId, // Server sent this to client
|
||||||
});
|
});
|
||||||
|
|
||||||
EndSceneEvent(sceneEventId);
|
EndSceneEvent(sceneEventId);
|
||||||
@@ -1666,7 +1654,7 @@ namespace Unity.Netcode
|
|||||||
SceneEventType = sceneEventData.SceneEventType,
|
SceneEventType = sceneEventData.SceneEventType,
|
||||||
LoadSceneMode = sceneEventData.LoadSceneMode,
|
LoadSceneMode = sceneEventData.LoadSceneMode,
|
||||||
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
|
||||||
ClientId = m_NetworkManager.ServerClientId,
|
ClientId = NetworkManager.ServerClientId,
|
||||||
ClientsThatCompleted = sceneEventData.ClientsCompleted,
|
ClientsThatCompleted = sceneEventData.ClientsCompleted,
|
||||||
ClientsThatTimedOut = sceneEventData.ClientsTimedOut,
|
ClientsThatTimedOut = sceneEventData.ClientsTimedOut,
|
||||||
});
|
});
|
||||||
@@ -1758,8 +1746,6 @@ namespace Unity.Netcode
|
|||||||
// NetworkObjects
|
// NetworkObjects
|
||||||
m_NetworkManager.InvokeOnClientConnectedCallback(clientId);
|
m_NetworkManager.InvokeOnClientConnectedCallback(clientId);
|
||||||
|
|
||||||
// TODO: This check and associated code can be removed once we determine all
|
|
||||||
// snapshot destroy messages are being updated until the server receives ACKs
|
|
||||||
if (sceneEventData.ClientNeedsReSynchronization() && !DisableReSynchronization)
|
if (sceneEventData.ClientNeedsReSynchronization() && !DisableReSynchronization)
|
||||||
{
|
{
|
||||||
sceneEventData.SceneEventType = SceneEventType.ReSynchronize;
|
sceneEventData.SceneEventType = SceneEventType.ReSynchronize;
|
||||||
@@ -1824,6 +1810,11 @@ namespace Unity.Netcode
|
|||||||
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 == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!sobj.DestroyWithScene || sobj.gameObject.scene == DontDestroyOnLoadScene)
|
if (!sobj.DestroyWithScene || sobj.gameObject.scene == DontDestroyOnLoadScene)
|
||||||
{
|
{
|
||||||
// Only move dynamically spawned network objects with no parent as child objects will follow
|
// Only move dynamically spawned network objects with no parent as child objects will follow
|
||||||
@@ -1849,7 +1840,7 @@ namespace Unity.Netcode
|
|||||||
/// Using the local scene relative Scene.handle as a sub-key to the root dictionary allows us to
|
/// Using the local scene relative Scene.handle as a sub-key to the root dictionary allows us to
|
||||||
/// distinguish between duplicate in-scene placed NetworkObjects
|
/// distinguish between duplicate in-scene placed NetworkObjects
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true)
|
internal void PopulateScenePlacedObjects(Scene sceneToFilterBy, bool clearScenePlacedObjects = true)
|
||||||
{
|
{
|
||||||
if (clearScenePlacedObjects)
|
if (clearScenePlacedObjects)
|
||||||
{
|
{
|
||||||
@@ -1864,25 +1855,26 @@ 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)
|
var globalObjectIdHash = networkObjectInstance.GlobalObjectIdHash;
|
||||||
if (networkObjectInstance.IsSceneObject == null && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy &&
|
var sceneHandle = networkObjectInstance.gameObject.scene.handle;
|
||||||
networkObjectInstance.gameObject.scene.handle == sceneToFilterBy.handle)
|
// We check to make sure the NetworkManager instance is the same one to be "NetcodeIntegrationTestHelpers" compatible and filter the list on a per scene basis (for additive scenes)
|
||||||
|
if (networkObjectInstance.IsSceneObject != false && networkObjectInstance.NetworkManager == m_NetworkManager && networkObjectInstance.gameObject.scene == sceneToFilterBy &&
|
||||||
|
sceneHandle == sceneToFilterBy.handle)
|
||||||
{
|
{
|
||||||
if (!ScenePlacedObjects.ContainsKey(networkObjectInstance.GlobalObjectIdHash))
|
if (!ScenePlacedObjects.ContainsKey(globalObjectIdHash))
|
||||||
{
|
{
|
||||||
ScenePlacedObjects.Add(networkObjectInstance.GlobalObjectIdHash, new Dictionary<int, NetworkObject>());
|
ScenePlacedObjects.Add(globalObjectIdHash, new Dictionary<int, NetworkObject>());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].ContainsKey(networkObjectInstance.gameObject.scene.handle))
|
if (!ScenePlacedObjects[globalObjectIdHash].ContainsKey(sceneHandle))
|
||||||
{
|
{
|
||||||
ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash].Add(networkObjectInstance.gameObject.scene.handle, networkObjectInstance);
|
ScenePlacedObjects[globalObjectIdHash].Add(sceneHandle, networkObjectInstance);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var exitingEntryName = ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash][networkObjectInstance.gameObject.scene.handle] != null ?
|
var exitingEntryName = ScenePlacedObjects[globalObjectIdHash][sceneHandle] != null ? ScenePlacedObjects[globalObjectIdHash][sceneHandle].name : "Null Entry";
|
||||||
ScenePlacedObjects[networkObjectInstance.GlobalObjectIdHash][networkObjectInstance.gameObject.scene.handle].name : "Null Entry";
|
|
||||||
throw new Exception($"{networkObjectInstance.name} tried to registered with {nameof(ScenePlacedObjects)} which already contains " +
|
throw new Exception($"{networkObjectInstance.name} tried to registered with {nameof(ScenePlacedObjects)} which already contains " +
|
||||||
$"the same {nameof(NetworkObject.GlobalObjectIdHash)} value {networkObjectInstance.GlobalObjectIdHash} for {exitingEntryName}!");
|
$"the same {nameof(NetworkObject.GlobalObjectIdHash)} value {globalObjectIdHash} for {exitingEntryName}!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1899,6 +1891,10 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
foreach (var sobj in objectsToKeep)
|
foreach (var sobj in objectsToKeep)
|
||||||
{
|
{
|
||||||
|
if (sobj == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// If it is in the DDOL then
|
// If it is in the DDOL then
|
||||||
if (sobj.gameObject.scene == DontDestroyOnLoadScene)
|
if (sobj.gameObject.scene == DontDestroyOnLoadScene)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -42,7 +41,6 @@ namespace Unity.Netcode
|
|||||||
/// <b>Invocation:</b> Server Side<br/>
|
/// <b>Invocation:</b> Server Side<br/>
|
||||||
/// <b>Message Flow:</b> Server to client<br/>
|
/// <b>Message Flow:</b> Server to client<br/>
|
||||||
/// <b>Event Notification:</b> Both server and client receive a local notification<br/>
|
/// <b>Event Notification:</b> Both server and client receive a local notification<br/>
|
||||||
/// <em>Note: This will be removed once snapshot and buffered messages are finalized as it will no longer be needed at that point.</em>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ReSynchronize,
|
ReSynchronize,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -100,6 +98,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 +232,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,9 +591,6 @@ namespace Unity.Netcode
|
|||||||
networkObject.IsSpawned = false;
|
networkObject.IsSpawned = false;
|
||||||
if (m_NetworkManager.PrefabHandler.ContainsHandler(networkObject))
|
if (m_NetworkManager.PrefabHandler.ContainsHandler(networkObject))
|
||||||
{
|
{
|
||||||
// Since this is the client side and we have missed the delete message, until the Snapshot system is in place for spawn and despawn handling
|
|
||||||
// we have to remove this from the list of spawned objects manually or when a NetworkObjectId is recycled the client will throw an error
|
|
||||||
// about the id already being assigned.
|
|
||||||
if (m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
|
if (m_NetworkManager.SpawnManager.SpawnedObjects.ContainsKey(networkObjectId))
|
||||||
{
|
{
|
||||||
m_NetworkManager.SpawnManager.SpawnedObjects.Remove(networkObjectId);
|
m_NetworkManager.SpawnManager.SpawnedObjects.Remove(networkObjectId);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Unity.Netcode
|
|||||||
public static class BytePacker
|
public static class BytePacker
|
||||||
{
|
{
|
||||||
#if UNITY_NETCODE_DEBUG_NO_PACKING
|
#if UNITY_NETCODE_DEBUG_NO_PACKING
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteValuePacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
public void WriteValuePacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
||||||
#else
|
#else
|
||||||
@@ -277,10 +277,21 @@ namespace Unity.Netcode
|
|||||||
|
|
||||||
|
|
||||||
#if UNITY_NETCODE_DEBUG_NO_PACKING
|
#if UNITY_NETCODE_DEBUG_NO_PACKING
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
public const ushort BitPackedUshortMax = (1 << 15) - 1;
|
||||||
|
public const short BitPackedShortMax = (1 << 14) - 1;
|
||||||
|
public const short BitPackedShortMin = -(1 << 14);
|
||||||
|
public const uint BitPackedUintMax = (1 << 30) - 1;
|
||||||
|
public const int BitPackedIntMax = (1 << 29) - 1;
|
||||||
|
public const int BitPackedIntMin = -(1 << 29);
|
||||||
|
public const ulong BitPackedULongMax = (1L << 61) - 1;
|
||||||
|
public const long BitPackedLongMax = (1L << 60) - 1;
|
||||||
|
public const long BitPackedLongMin = -(1L << 60);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a 14-bit signed short to the buffer in a bit-encoded packed format.
|
/// Writes a 14-bit signed short to the buffer in a bit-encoded packed format.
|
||||||
/// The first bit indicates whether the value is 1 byte or 2.
|
/// The first bit indicates whether the value is 1 byte or 2.
|
||||||
@@ -307,7 +318,7 @@ namespace Unity.Netcode
|
|||||||
public static void WriteValueBitPacked(FastBufferWriter writer, ushort value)
|
public static void WriteValueBitPacked(FastBufferWriter writer, ushort value)
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (value >= 0b1000_0000_0000_0000)
|
if (value >= BitPackedUshortMax)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("BitPacked ushorts must be <= 15 bits");
|
throw new ArgumentException("BitPacked ushorts must be <= 15 bits");
|
||||||
}
|
}
|
||||||
@@ -356,7 +367,7 @@ namespace Unity.Netcode
|
|||||||
public static void WriteValueBitPacked(FastBufferWriter writer, uint value)
|
public static void WriteValueBitPacked(FastBufferWriter writer, uint value)
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (value >= 0b0100_0000_0000_0000_0000_0000_0000_0000)
|
if (value > BitPackedUintMax)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("BitPacked uints must be <= 30 bits");
|
throw new ArgumentException("BitPacked uints must be <= 30 bits");
|
||||||
}
|
}
|
||||||
@@ -396,7 +407,7 @@ namespace Unity.Netcode
|
|||||||
public static void WriteValueBitPacked(FastBufferWriter writer, ulong value)
|
public static void WriteValueBitPacked(FastBufferWriter writer, ulong value)
|
||||||
{
|
{
|
||||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||||
if (value >= 0b0010_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000)
|
if (value > BitPackedULongMax)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("BitPacked ulongs must be <= 61 bits");
|
throw new ArgumentException("BitPacked ulongs must be <= 61 bits");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Unity.Netcode
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly unsafe ReaderHandle* Handle;
|
internal unsafe ReaderHandle* Handle;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the current read position
|
/// Get the current read position
|
||||||
@@ -39,6 +39,11 @@ namespace Unity.Netcode
|
|||||||
get => Handle->Length;
|
get => Handle->Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the reader has been initialized and a handle allocated.
|
||||||
|
/// </summary>
|
||||||
|
public unsafe bool IsInitialized => Handle != null;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal unsafe void CommitBitwiseReads(int amount)
|
internal unsafe void CommitBitwiseReads(int amount)
|
||||||
{
|
{
|
||||||
@@ -76,7 +81,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 +98,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 +122,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 +147,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 +170,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 +192,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>
|
||||||
@@ -196,6 +201,7 @@ namespace Unity.Netcode
|
|||||||
public unsafe void Dispose()
|
public unsafe void Dispose()
|
||||||
{
|
{
|
||||||
UnsafeUtility.Free(Handle, Handle->Allocator);
|
UnsafeUtility.Free(Handle, Handle->Allocator);
|
||||||
|
Handle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -250,7 +256,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 +290,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
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Unity.Netcode
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly unsafe WriterHandle* Handle;
|
internal unsafe WriterHandle* Handle;
|
||||||
|
|
||||||
private static byte[] s_ByteArrayCache = new byte[65535];
|
private static byte[] s_ByteArrayCache = new byte[65535];
|
||||||
|
|
||||||
@@ -62,6 +62,11 @@ namespace Unity.Netcode
|
|||||||
get => Handle->Position > Handle->Length ? Handle->Position : Handle->Length;
|
get => Handle->Position > Handle->Length ? Handle->Position : Handle->Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the writer has been initialized and a handle allocated.
|
||||||
|
/// </summary>
|
||||||
|
public unsafe bool IsInitialized => Handle != null;
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal unsafe void CommitBitwiseWrites(int amount)
|
internal unsafe void CommitBitwiseWrites(int amount)
|
||||||
@@ -111,6 +116,7 @@ namespace Unity.Netcode
|
|||||||
UnsafeUtility.Free(Handle->BufferPointer, Handle->Allocator);
|
UnsafeUtility.Free(Handle->BufferPointer, Handle->Allocator);
|
||||||
}
|
}
|
||||||
UnsafeUtility.Free(Handle, Handle->Allocator);
|
UnsafeUtility.Free(Handle, Handle->Allocator);
|
||||||
|
Handle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -207,7 +213,7 @@ namespace Unity.Netcode
|
|||||||
/// When you know you will be writing multiple fields back-to-back and you know the total size,
|
/// When you know you will be writing multiple fields back-to-back and you know the total size,
|
||||||
/// you can call TryBeginWrite() once on the total size, and then follow it with calls to
|
/// you can call TryBeginWrite() once on the total size, and then follow it with calls to
|
||||||
/// WriteValue() instead of WriteValueSafe() for faster serialization.
|
/// WriteValue() instead of WriteValueSafe() for faster serialization.
|
||||||
///
|
///
|
||||||
/// Unsafe write operations will throw OverflowException in editor and development builds if you
|
/// Unsafe write operations will throw OverflowException in editor and development builds if you
|
||||||
/// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown
|
/// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown
|
||||||
/// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following
|
/// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following
|
||||||
@@ -253,7 +259,7 @@ namespace Unity.Netcode
|
|||||||
/// When you know you will be writing multiple fields back-to-back and you know the total size,
|
/// When you know you will be writing multiple fields back-to-back and you know the total size,
|
||||||
/// you can call TryBeginWrite() once on the total size, and then follow it with calls to
|
/// you can call TryBeginWrite() once on the total size, and then follow it with calls to
|
||||||
/// WriteValue() instead of WriteValueSafe() for faster serialization.
|
/// WriteValue() instead of WriteValueSafe() for faster serialization.
|
||||||
///
|
///
|
||||||
/// Unsafe write operations will throw OverflowException in editor and development builds if you
|
/// Unsafe write operations will throw OverflowException in editor and development builds if you
|
||||||
/// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown
|
/// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown
|
||||||
/// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following
|
/// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following
|
||||||
|
|||||||
@@ -21,6 +21,122 @@ namespace Unity.Netcode
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly HashSet<NetworkObject> SpawnedObjectsList = new HashSet<NetworkObject>();
|
public readonly HashSet<NetworkObject> SpawnedObjectsList = new HashSet<NetworkObject>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Use to get all NetworkObjects owned by a client
|
||||||
|
/// Ownership to Objects Table Format:
|
||||||
|
/// [ClientId][NetworkObjectId][NetworkObject]
|
||||||
|
/// Server: Keeps track of all clients' ownership
|
||||||
|
/// Client: Keeps track of only its ownership
|
||||||
|
/// </summary>
|
||||||
|
public readonly Dictionary<ulong, Dictionary<ulong, NetworkObject>> OwnershipToObjectsTable = new Dictionary<ulong, Dictionary<ulong, NetworkObject>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Object to Ownership Table:
|
||||||
|
/// [NetworkObjectId][ClientId]
|
||||||
|
/// Used internally to find the client Id that currently owns
|
||||||
|
/// the NetworkObject
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<ulong, ulong> m_ObjectToOwnershipTable = new Dictionary<ulong, ulong>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to update a NetworkObject's ownership
|
||||||
|
/// </summary>
|
||||||
|
internal void UpdateOwnershipTable(NetworkObject networkObject, ulong newOwner, bool isRemoving = false)
|
||||||
|
{
|
||||||
|
var previousOwner = newOwner;
|
||||||
|
|
||||||
|
// Use internal lookup table to see if the NetworkObject has a previous owner
|
||||||
|
if (m_ObjectToOwnershipTable.ContainsKey(networkObject.NetworkObjectId))
|
||||||
|
{
|
||||||
|
// Keep track of the previous owner's ClientId
|
||||||
|
previousOwner = m_ObjectToOwnershipTable[networkObject.NetworkObjectId];
|
||||||
|
|
||||||
|
// We are either despawning (remove) or changing ownership (assign)
|
||||||
|
if (isRemoving)
|
||||||
|
{
|
||||||
|
m_ObjectToOwnershipTable.Remove(networkObject.NetworkObjectId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ObjectToOwnershipTable[networkObject.NetworkObjectId] = newOwner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, just add a new lookup entry
|
||||||
|
m_ObjectToOwnershipTable.Add(networkObject.NetworkObjectId, newOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if we had a previous owner
|
||||||
|
if (previousOwner != newOwner && OwnershipToObjectsTable.ContainsKey(previousOwner))
|
||||||
|
{
|
||||||
|
// Before updating the previous owner, assure this entry exists
|
||||||
|
if (OwnershipToObjectsTable[previousOwner].ContainsKey(networkObject.NetworkObjectId))
|
||||||
|
{
|
||||||
|
// Remove the previous owner's entry
|
||||||
|
OwnershipToObjectsTable[previousOwner].Remove(networkObject.NetworkObjectId);
|
||||||
|
|
||||||
|
// Server or Host alway invokes the lost ownership notification locally
|
||||||
|
if (NetworkManager.IsServer)
|
||||||
|
{
|
||||||
|
networkObject.InvokeBehaviourOnLostOwnership();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are removing the entry (i.e. despawning or client lost ownership)
|
||||||
|
if (isRemoving)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Really, as long as UpdateOwnershipTable is invoked when ownership is gained or lost this should never happen
|
||||||
|
throw new Exception($"Client-ID {previousOwner} had a partial {nameof(m_ObjectToOwnershipTable)} entry! Potentially corrupted {nameof(OwnershipToObjectsTable)}?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the owner doesn't have an entry then create one
|
||||||
|
if (!OwnershipToObjectsTable.ContainsKey(newOwner))
|
||||||
|
{
|
||||||
|
OwnershipToObjectsTable.Add(newOwner, new Dictionary<ulong, NetworkObject>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check to make sure we don't already have this entry (we shouldn't)
|
||||||
|
if (!OwnershipToObjectsTable[newOwner].ContainsKey(networkObject.NetworkObjectId))
|
||||||
|
{
|
||||||
|
// Add the new ownership entry
|
||||||
|
OwnershipToObjectsTable[newOwner].Add(networkObject.NetworkObjectId, networkObject);
|
||||||
|
|
||||||
|
// Server or Host always invokes the gained ownership notification locally
|
||||||
|
if (NetworkManager.IsServer)
|
||||||
|
{
|
||||||
|
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isRemoving)
|
||||||
|
{
|
||||||
|
OwnershipToObjectsTable[previousOwner].Remove(networkObject.NetworkObjectId);
|
||||||
|
}
|
||||||
|
else if (NetworkManager.LogLevel == LogLevel.Developer)
|
||||||
|
{
|
||||||
|
NetworkLog.LogWarning($"Setting ownership twice? Client-ID {previousOwner} already owns NetworkObject ID {networkObject.NetworkObjectId}!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of all NetworkObjects that belong to a client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">the client's id <see cref="NetworkManager.LocalClientId"/></param>
|
||||||
|
public List<NetworkObject> GetClientOwnedObjects(ulong clientId)
|
||||||
|
{
|
||||||
|
if (!OwnershipToObjectsTable.ContainsKey(clientId))
|
||||||
|
{
|
||||||
|
OwnershipToObjectsTable.Add(clientId, new Dictionary<ulong, NetworkObject>());
|
||||||
|
}
|
||||||
|
return OwnershipToObjectsTable[clientId].Values.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private struct TriggerData
|
private struct TriggerData
|
||||||
{
|
{
|
||||||
public FastBufferReader Reader;
|
public FastBufferReader Reader;
|
||||||
@@ -96,13 +212,12 @@ namespace Unity.Netcode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defers processing of a message until the moment a specific networkObjectId is spawned.
|
/// Defers processing of a message until the moment a specific networkObjectId is spawned.
|
||||||
/// This is to handle situations where an RPC or other object-specific message arrives before the spawn does,
|
/// This is to handle situations where an RPC or other object-specific message arrives before the spawn does,
|
||||||
/// either due to it being requested in OnNetworkSpawn before the spawn call has been executed, or with
|
/// either due to it being requested in OnNetworkSpawn before the spawn call has been executed
|
||||||
/// snapshot spawns enabled where the spawn is sent unreliably and not until the end of the frame.
|
|
||||||
///
|
///
|
||||||
/// 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))
|
||||||
{
|
{
|
||||||
@@ -194,37 +309,21 @@ namespace Unity.Netcode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the connected client entry exists before trying to remove ownership.
|
// Server removes the entry and takes over ownership before notifying
|
||||||
if (TryGetNetworkClient(networkObject.OwnerClientId, out NetworkClient networkClient))
|
UpdateOwnershipTable(networkObject, NetworkManager.ServerClientId, true);
|
||||||
|
|
||||||
|
networkObject.OwnerClientId = NetworkManager.ServerClientId;
|
||||||
|
|
||||||
|
var message = new ChangeOwnershipMessage
|
||||||
{
|
{
|
||||||
for (int i = networkClient.OwnedObjects.Count - 1; i > -1; i--)
|
NetworkObjectId = networkObject.NetworkObjectId,
|
||||||
{
|
OwnerClientId = networkObject.OwnerClientId
|
||||||
if (networkClient.OwnedObjects[i] == networkObject)
|
};
|
||||||
{
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
||||||
networkClient.OwnedObjects.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.OwnerClientIdInternal = null;
|
foreach (var client in NetworkManager.ConnectedClients)
|
||||||
|
|
||||||
var message = new ChangeOwnershipMessage
|
|
||||||
{
|
|
||||||
NetworkObjectId = networkObject.NetworkObjectId,
|
|
||||||
OwnerClientId = networkObject.OwnerClientId
|
|
||||||
};
|
|
||||||
var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableSequenced, NetworkManager.ConnectedClientsIds);
|
|
||||||
|
|
||||||
foreach (var client in NetworkManager.ConnectedClients)
|
|
||||||
{
|
|
||||||
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
NetworkManager.NetworkMetrics.TrackOwnershipChangeSent(client.Key, networkObject, size);
|
||||||
{
|
|
||||||
NetworkLog.LogWarning($"No connected clients prior to removing ownership for {networkObject.name}. Make sure you are not initializing or shutting down when removing ownership.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,28 +364,17 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is not spawned");
|
throw new SpawnStateException("Object is not spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryGetNetworkClient(networkObject.OwnerClientId, out NetworkClient networkClient))
|
|
||||||
{
|
|
||||||
for (int i = networkClient.OwnedObjects.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (networkClient.OwnedObjects[i] == networkObject)
|
|
||||||
{
|
|
||||||
networkClient.OwnedObjects.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
networkClient.OwnedObjects.Add(networkObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.OwnerClientId = clientId;
|
networkObject.OwnerClientId = clientId;
|
||||||
|
|
||||||
|
// Server adds entries for all client ownership
|
||||||
|
UpdateOwnershipTable(networkObject, networkObject.OwnerClientId);
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -410,7 +498,7 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ran on both server and client
|
// Ran on both server and client
|
||||||
internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong? ownerClientId, bool destroyWithScene)
|
internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene)
|
||||||
{
|
{
|
||||||
if (networkObject == null)
|
if (networkObject == null)
|
||||||
{
|
{
|
||||||
@@ -422,6 +510,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,15 +536,12 @@ namespace Unity.Netcode
|
|||||||
throw new SpawnStateException("Object is already spawned");
|
throw new SpawnStateException("Object is already spawned");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sceneObject.Header.HasNetworkVariables)
|
networkObject.SetNetworkVariableData(variableData);
|
||||||
{
|
|
||||||
networkObject.SetNetworkVariableData(variableData);
|
|
||||||
}
|
|
||||||
|
|
||||||
SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Header.NetworkObjectId, sceneObject.Header.IsSceneObject, sceneObject.Header.IsPlayerObject, sceneObject.Header.OwnerClientId, destroyWithScene);
|
SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Header.NetworkObjectId, sceneObject.Header.IsSceneObject, sceneObject.Header.IsPlayerObject, sceneObject.Header.OwnerClientId, destroyWithScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong? ownerClientId, bool destroyWithScene)
|
private void SpawnNetworkObjectLocallyCommon(NetworkObject networkObject, ulong networkId, bool sceneObject, bool playerObject, ulong ownerClientId, bool destroyWithScene)
|
||||||
{
|
{
|
||||||
if (SpawnedObjects.ContainsKey(networkId))
|
if (SpawnedObjects.ContainsKey(networkId))
|
||||||
{
|
{
|
||||||
@@ -458,38 +552,45 @@ namespace Unity.Netcode
|
|||||||
// this initialization really should be at the bottom of the function
|
// this initialization really should be at the bottom of the function
|
||||||
networkObject.IsSpawned = true;
|
networkObject.IsSpawned = true;
|
||||||
|
|
||||||
// this initialization really should be at the top of this function. If and when we break the
|
// this initialization really should be at the top of this function. If and when we break the
|
||||||
// NetworkVariable dependency on NetworkBehaviour, this otherwise creates problems because
|
// NetworkVariable dependency on NetworkBehaviour, this otherwise creates problems because
|
||||||
// SetNetworkVariableData above calls InitializeVariables, and the 'baked out' data isn't ready there;
|
// SetNetworkVariableData above calls InitializeVariables, and the 'baked out' data isn't ready there;
|
||||||
// the current design banks on getting the network behaviour set and then only reading from it
|
// the current design banks on getting the network behaviour set and then only reading from it after the
|
||||||
// after the below initialization code. However cowardice compels me to hold off on moving this until
|
// below initialization code. However cowardice compels me to hold off on moving this until that commit
|
||||||
// that commit
|
|
||||||
networkObject.IsSceneObject = sceneObject;
|
networkObject.IsSceneObject = sceneObject;
|
||||||
networkObject.NetworkObjectId = networkId;
|
networkObject.NetworkObjectId = networkId;
|
||||||
|
|
||||||
networkObject.DestroyWithScene = sceneObject || destroyWithScene;
|
networkObject.DestroyWithScene = sceneObject || destroyWithScene;
|
||||||
|
|
||||||
networkObject.OwnerClientIdInternal = ownerClientId;
|
networkObject.OwnerClientId = ownerClientId;
|
||||||
|
|
||||||
networkObject.IsPlayerObject = playerObject;
|
networkObject.IsPlayerObject = playerObject;
|
||||||
|
|
||||||
SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject);
|
SpawnedObjects.Add(networkObject.NetworkObjectId, networkObject);
|
||||||
SpawnedObjectsList.Add(networkObject);
|
SpawnedObjectsList.Add(networkObject);
|
||||||
|
|
||||||
if (ownerClientId != null)
|
if (NetworkManager.IsServer)
|
||||||
{
|
{
|
||||||
if (NetworkManager.IsServer)
|
if (playerObject)
|
||||||
{
|
{
|
||||||
if (playerObject)
|
// If there was an already existing player object for this player, then mark it as no longer
|
||||||
|
// a player object.
|
||||||
|
if (NetworkManager.ConnectedClients[ownerClientId].PlayerObject != null)
|
||||||
{
|
{
|
||||||
NetworkManager.ConnectedClients[ownerClientId.Value].PlayerObject = networkObject;
|
NetworkManager.ConnectedClients[ownerClientId].PlayerObject.IsPlayerObject = false;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NetworkManager.ConnectedClients[ownerClientId.Value].OwnedObjects.Add(networkObject);
|
|
||||||
}
|
}
|
||||||
|
NetworkManager.ConnectedClients[ownerClientId].PlayerObject = networkObject;
|
||||||
}
|
}
|
||||||
else if (playerObject && ownerClientId.Value == NetworkManager.LocalClientId)
|
}
|
||||||
|
else if (ownerClientId == NetworkManager.LocalClientId)
|
||||||
|
{
|
||||||
|
if (playerObject)
|
||||||
{
|
{
|
||||||
|
// If there was an already existing player object for this player, then mark it as no longer a player object.
|
||||||
|
if (NetworkManager.LocalClient.PlayerObject != null)
|
||||||
|
{
|
||||||
|
NetworkManager.LocalClient.PlayerObject.IsPlayerObject = false;
|
||||||
|
}
|
||||||
NetworkManager.LocalClient.PlayerObject = networkObject;
|
NetworkManager.LocalClient.PlayerObject = networkObject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -525,29 +626,32 @@ namespace Unity.Netcode
|
|||||||
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)
|
||||||
{
|
{
|
||||||
if (!NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
//Currently, if this is called and the clientId (destination) is the server's client Id, this case will be checked
|
||||||
|
// within the below Send function. To avoid unwarranted allocation of a PooledNetworkBuffer placing this check here. [NSS]
|
||||||
|
if (NetworkManager.IsServer && clientId == NetworkManager.ServerClientId)
|
||||||
{
|
{
|
||||||
//Currently, if this is called and the clientId (destination) is the server's client Id, this case
|
return;
|
||||||
//will be checked within the below Send function. To avoid unwarranted allocation of a PooledNetworkBuffer
|
|
||||||
//placing this check here. [NSS]
|
|
||||||
if (NetworkManager.IsServer && clientId == NetworkManager.ServerClientId)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = new CreateObjectMessage
|
|
||||||
{
|
|
||||||
ObjectInfo = networkObject.GetMessageSceneObject(clientId)
|
|
||||||
};
|
|
||||||
var size = NetworkManager.SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
|
||||||
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
|
||||||
|
|
||||||
networkObject.MarkVariablesDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var message = new CreateObjectMessage
|
||||||
|
{
|
||||||
|
ObjectInfo = networkObject.GetMessageSceneObject(clientId)
|
||||||
|
};
|
||||||
|
var size = NetworkManager.SendMessage(ref message, NetworkDelivery.ReliableFragmentedSequenced, clientId);
|
||||||
|
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject, size);
|
||||||
|
|
||||||
|
networkObject.MarkVariablesDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ulong? GetSpawnParentId(NetworkObject networkObject)
|
internal ulong? GetSpawnParentId(NetworkObject networkObject)
|
||||||
@@ -585,14 +689,12 @@ namespace Unity.Netcode
|
|||||||
// Makes scene objects ready to be reused
|
// Makes scene objects ready to be reused
|
||||||
internal void ServerResetShudownStateForSceneObjects()
|
internal void ServerResetShudownStateForSceneObjects()
|
||||||
{
|
{
|
||||||
foreach (var sobj in SpawnedObjectsList)
|
var networkObjects = UnityEngine.Object.FindObjectsOfType<NetworkObject>().Where((c) => c.IsSceneObject != null && c.IsSceneObject == true);
|
||||||
|
foreach (var sobj in networkObjects)
|
||||||
{
|
{
|
||||||
if ((sobj.IsSceneObject != null && sobj.IsSceneObject == true) || sobj.DestroyWithScene)
|
sobj.IsSpawned = false;
|
||||||
{
|
sobj.DestroyWithScene = false;
|
||||||
sobj.IsSpawned = false;
|
sobj.IsSceneObject = null;
|
||||||
sobj.DestroyWithScene = false;
|
|
||||||
sobj.IsSceneObject = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,14 +735,12 @@ namespace Unity.Netcode
|
|||||||
else if (networkObjects[i].IsSpawned)
|
else if (networkObjects[i].IsSpawned)
|
||||||
{
|
{
|
||||||
// If it is an in-scene placed NetworkObject then just despawn
|
// If it is an in-scene placed NetworkObject then just despawn
|
||||||
// and let it be destroyed when the scene is unloaded. Otherwise,
|
// and let it be destroyed when the scene is unloaded. Otherwise, despawn and destroy it.
|
||||||
// despawn and destroy it.
|
var shouldDestroy = !(networkObjects[i].IsSceneObject != null && networkObjects[i].IsSceneObject.Value);
|
||||||
var shouldDestroy = !(networkObjects[i].IsSceneObject != null
|
|
||||||
&& networkObjects[i].IsSceneObject.Value);
|
|
||||||
|
|
||||||
OnDespawnObject(networkObjects[i], shouldDestroy);
|
OnDespawnObject(networkObjects[i], shouldDestroy);
|
||||||
}
|
}
|
||||||
else
|
else if (networkObjects[i].IsSceneObject != null && !networkObjects[i].IsSceneObject.Value)
|
||||||
{
|
{
|
||||||
UnityEngine.Object.Destroy(networkObjects[i].gameObject);
|
UnityEngine.Object.Destroy(networkObjects[i].gameObject);
|
||||||
}
|
}
|
||||||
@@ -678,6 +778,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++)
|
||||||
{
|
{
|
||||||
@@ -685,10 +786,16 @@ 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, networkObject.OwnerClientId, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObject)
|
internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObject)
|
||||||
@@ -731,18 +838,6 @@ namespace Unity.Netcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!networkObject.IsOwnedByServer && !networkObject.IsPlayerObject && TryGetNetworkClient(networkObject.OwnerClientId, out NetworkClient networkClient))
|
|
||||||
{
|
|
||||||
//Someone owns it.
|
|
||||||
for (int i = networkClient.OwnedObjects.Count - 1; i > -1; i--)
|
|
||||||
{
|
|
||||||
if (networkClient.OwnedObjects[i].NetworkObjectId == networkObject.NetworkObjectId)
|
|
||||||
{
|
|
||||||
networkClient.OwnedObjects.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
networkObject.InvokeBehaviourNetworkDespawn();
|
networkObject.InvokeBehaviourNetworkDespawn();
|
||||||
|
|
||||||
if (NetworkManager != null && NetworkManager.IsServer)
|
if (NetworkManager != null && NetworkManager.IsServer)
|
||||||
@@ -756,38 +851,31 @@ namespace Unity.Netcode
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn)
|
if (networkObject != null)
|
||||||
{
|
{
|
||||||
networkObject.SnapshotDespawn();
|
// As long as we have any remaining clients, then notify of the object being destroy.
|
||||||
}
|
if (NetworkManager.ConnectedClientsList.Count > 0)
|
||||||
else
|
|
||||||
{
|
|
||||||
if (networkObject != null)
|
|
||||||
{
|
{
|
||||||
// As long as we have any remaining clients, then notify of the object being destroy.
|
m_TargetClientIds.Clear();
|
||||||
if (NetworkManager.ConnectedClientsList.Count > 0)
|
|
||||||
|
// We keep only the client for which the object is visible
|
||||||
|
// as the other clients have them already despawned
|
||||||
|
foreach (var clientId in NetworkManager.ConnectedClientsIds)
|
||||||
{
|
{
|
||||||
m_TargetClientIds.Clear();
|
if (networkObject.IsNetworkVisibleTo(clientId))
|
||||||
|
|
||||||
// We keep only the client for which the object is visible
|
|
||||||
// as the other clients have them already despawned
|
|
||||||
foreach (var clientId in NetworkManager.ConnectedClientsIds)
|
|
||||||
{
|
{
|
||||||
if (networkObject.IsNetworkVisibleTo(clientId))
|
m_TargetClientIds.Add(clientId);
|
||||||
{
|
|
||||||
m_TargetClientIds.Add(clientId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var message = new DestroyObjectMessage
|
var message = new DestroyObjectMessage
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -822,7 +910,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))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,52 +0,0 @@
|
|||||||
using System;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Networking;
|
|
||||||
|
|
||||||
namespace Unity.Netcode.Transports.UNET
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A transport channel used by the netcode
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
public class UNetChannel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the channel
|
|
||||||
/// </summary>
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
[ReadOnly]
|
|
||||||
#endif
|
|
||||||
public byte Id;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The type of channel
|
|
||||||
/// </summary>
|
|
||||||
public QosType Type;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
private class ReadOnlyAttribute : PropertyAttribute { }
|
|
||||||
|
|
||||||
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
|
|
||||||
private class ReadOnlyDrawer : PropertyDrawer
|
|
||||||
{
|
|
||||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
|
||||||
{
|
|
||||||
// Saving previous GUI enabled value
|
|
||||||
var previousGUIState = GUI.enabled;
|
|
||||||
|
|
||||||
// Disabling edit for property
|
|
||||||
GUI.enabled = false;
|
|
||||||
|
|
||||||
// Drawing Property
|
|
||||||
EditorGUI.PropertyField(position, property, label);
|
|
||||||
|
|
||||||
// Setting old GUI enabled value
|
|
||||||
GUI.enabled = previousGUIState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e864534da30ef604992c0ed33c75d3c6
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 4e60372130aba464f9f9ae4a24bb9fe0
|
guid: 81887adf6d9ca40c9b70728b7018b6f5
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
96
Runtime/Transports/UTP/BatchedReceiveQueue.cs
Normal file
96
Runtime/Transports/UTP/BatchedReceiveQueue.cs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
using System;
|
||||||
|
using Unity.Networking.Transport;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Transports.UTP
|
||||||
|
{
|
||||||
|
/// <summary>Queue for batched messages received through UTP.</summary>
|
||||||
|
/// <remarks>This is meant as a companion to <see cref="BatchedSendQueue"/>.</remarks>
|
||||||
|
internal class BatchedReceiveQueue
|
||||||
|
{
|
||||||
|
private byte[] m_Data;
|
||||||
|
private int m_Offset;
|
||||||
|
private int m_Length;
|
||||||
|
|
||||||
|
public bool IsEmpty => m_Length <= 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a new receive queue from a <see cref="DataStreamReader"/> returned by
|
||||||
|
/// <see cref="NetworkDriver"/> when popping a data event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The <see cref="DataStreamReader"/> to construct from.</param>
|
||||||
|
public BatchedReceiveQueue(DataStreamReader reader)
|
||||||
|
{
|
||||||
|
m_Data = new byte[reader.Length];
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* dataPtr = m_Data)
|
||||||
|
{
|
||||||
|
reader.ReadBytes(dataPtr, reader.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Offset = 0;
|
||||||
|
m_Length = reader.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Push the entire data from a <see cref="DataStreamReader"/> (as returned by popping an
|
||||||
|
/// event from a <see cref="NetworkDriver">) to the queue.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The <see cref="DataStreamReader"/> to push the data of.</param>
|
||||||
|
public void PushReader(DataStreamReader reader)
|
||||||
|
{
|
||||||
|
// Resize the array and copy the existing data to the beginning if there's not enough
|
||||||
|
// room to copy the reader's data at the end of the existing data.
|
||||||
|
var available = m_Data.Length - (m_Offset + m_Length);
|
||||||
|
if (available < reader.Length)
|
||||||
|
{
|
||||||
|
if (m_Length > 0)
|
||||||
|
{
|
||||||
|
Array.Copy(m_Data, m_Offset, m_Data, 0, m_Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Offset = 0;
|
||||||
|
|
||||||
|
while (m_Data.Length - m_Length < reader.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref m_Data, m_Data.Length * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* dataPtr = m_Data)
|
||||||
|
{
|
||||||
|
reader.ReadBytes(dataPtr + m_Offset + m_Length, reader.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Length += reader.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Pop the next full message in the queue.</summary>
|
||||||
|
/// <returns>The message, or the default value if no more full messages.</returns>
|
||||||
|
public ArraySegment<byte> PopMessage()
|
||||||
|
{
|
||||||
|
if (m_Length < sizeof(int))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageLength = BitConverter.ToInt32(m_Data, m_Offset);
|
||||||
|
|
||||||
|
if (m_Length - sizeof(int) < messageLength)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = new ArraySegment<byte>(m_Data, m_Offset + sizeof(int), messageLength);
|
||||||
|
|
||||||
|
m_Offset += sizeof(int) + messageLength;
|
||||||
|
m_Length -= sizeof(int) + messageLength;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: c275febadb27c4d18b41218e3353b84b
|
guid: e9ead10b891184bd5b8f2650fd66a5b1
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
233
Runtime/Transports/UTP/BatchedSendQueue.cs
Normal file
233
Runtime/Transports/UTP/BatchedSendQueue.cs
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
using System;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
using Unity.Networking.Transport;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Transports.UTP
|
||||||
|
{
|
||||||
|
/// <summary>Queue for batched messages meant to be sent through UTP.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Messages should be pushed on the queue with <see cref="PushMessage"/>. To send batched
|
||||||
|
/// messages, call <see cref="FillWriter"> with the <see cref="DataStreamWriter"/> obtained from
|
||||||
|
/// <see cref="NetworkDriver.BeginSend"/>. This will fill the writer with as many messages as
|
||||||
|
/// possible. If the send is successful, call <see cref="Consume"/> to remove the data from the
|
||||||
|
/// queue.
|
||||||
|
///
|
||||||
|
/// This is meant as a companion to <see cref="BatchedReceiveQueue"/>, which should be used to
|
||||||
|
/// read messages sent with this queue.
|
||||||
|
/// </remarks>
|
||||||
|
internal struct BatchedSendQueue : IDisposable
|
||||||
|
{
|
||||||
|
private NativeArray<byte> m_Data;
|
||||||
|
private NativeArray<int> m_HeadTailIndices;
|
||||||
|
|
||||||
|
/// <summary>Overhead that is added to each message in the queue.</summary>
|
||||||
|
public const int PerMessageOverhead = sizeof(int);
|
||||||
|
|
||||||
|
// Indices into m_HeadTailIndicies.
|
||||||
|
private const int k_HeadInternalIndex = 0;
|
||||||
|
private const int k_TailInternalIndex = 1;
|
||||||
|
|
||||||
|
/// <summary>Index of the first byte of the oldest data in the queue.</summary>
|
||||||
|
private int HeadIndex
|
||||||
|
{
|
||||||
|
get { return m_HeadTailIndices[k_HeadInternalIndex]; }
|
||||||
|
set { m_HeadTailIndices[k_HeadInternalIndex] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Index one past the last byte of the most recent data in the queue.</summary>
|
||||||
|
private int TailIndex
|
||||||
|
{
|
||||||
|
get { return m_HeadTailIndices[k_TailInternalIndex]; }
|
||||||
|
set { m_HeadTailIndices[k_TailInternalIndex] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Length => TailIndex - HeadIndex;
|
||||||
|
|
||||||
|
public bool IsEmpty => HeadIndex == TailIndex;
|
||||||
|
|
||||||
|
public bool IsCreated => m_Data.IsCreated;
|
||||||
|
|
||||||
|
/// <summary>Construct a new empty send queue.</summary>
|
||||||
|
/// <param name="capacity">Maximum capacity of the send queue.</param>
|
||||||
|
public BatchedSendQueue(int capacity)
|
||||||
|
{
|
||||||
|
m_Data = new NativeArray<byte>(capacity, Allocator.Persistent);
|
||||||
|
m_HeadTailIndices = new NativeArray<int>(2, Allocator.Persistent);
|
||||||
|
|
||||||
|
HeadIndex = 0;
|
||||||
|
TailIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (IsCreated)
|
||||||
|
{
|
||||||
|
m_Data.Dispose();
|
||||||
|
m_HeadTailIndices.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Append data at the tail of the queue. No safety checks.</summary>
|
||||||
|
private void AppendDataAtTail(ArraySegment<byte> data)
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var writer = new DataStreamWriter((byte*)m_Data.GetUnsafePtr() + TailIndex, m_Data.Length - TailIndex);
|
||||||
|
|
||||||
|
writer.WriteInt(data.Count);
|
||||||
|
|
||||||
|
fixed (byte* dataPtr = data.Array)
|
||||||
|
{
|
||||||
|
writer.WriteBytes(dataPtr + data.Offset, data.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TailIndex += sizeof(int) + data.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Append a new message to the queue.</summary>
|
||||||
|
/// <param name="message">Message to append to the queue.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Whether the message was appended successfully. The only way it can fail is if there's
|
||||||
|
/// no more room in the queue. On failure, nothing is written to the queue.
|
||||||
|
/// </returns>
|
||||||
|
public bool PushMessage(ArraySegment<byte> message)
|
||||||
|
{
|
||||||
|
if (!IsCreated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there's enough room after the current tail index.
|
||||||
|
if (m_Data.Length - TailIndex >= sizeof(int) + message.Count)
|
||||||
|
{
|
||||||
|
AppendDataAtTail(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there would be enough room if we moved data at the beginning of m_Data.
|
||||||
|
if (m_Data.Length - TailIndex + HeadIndex >= sizeof(int) + message.Count)
|
||||||
|
{
|
||||||
|
// Move the data back at the beginning of m_Data.
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
UnsafeUtility.MemMove(m_Data.GetUnsafePtr(), (byte*)m_Data.GetUnsafePtr() + HeadIndex, Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
TailIndex = Length;
|
||||||
|
HeadIndex = 0;
|
||||||
|
|
||||||
|
AppendDataAtTail(message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fill as much of a <see cref="DataStreamWriter"/> as possible with data from the head of
|
||||||
|
/// the queue. Only full messages (and their length) are written to the writer.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This does NOT actually consume anything from the queue. That is, calling this method
|
||||||
|
/// does not reduce the length of the queue. Callers are expected to call
|
||||||
|
/// <see cref="Consume"/> with the value returned by this method afterwards if the data can
|
||||||
|
/// be safely removed from the queue (e.g. if it was sent successfully).
|
||||||
|
///
|
||||||
|
/// This method should not be used together with <see cref="FillWriterWithBytes"> since this
|
||||||
|
/// could lead to a corrupted queue.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="writer">The <see cref="DataStreamWriter"/> to write to.</param>
|
||||||
|
/// <returns>How many bytes were written to the writer.</returns>
|
||||||
|
public int FillWriterWithMessages(ref DataStreamWriter writer)
|
||||||
|
{
|
||||||
|
if (!IsCreated || Length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var reader = new DataStreamReader((byte*)m_Data.GetUnsafePtr() + HeadIndex, Length);
|
||||||
|
|
||||||
|
var writerAvailable = writer.Capacity;
|
||||||
|
var readerOffset = 0;
|
||||||
|
|
||||||
|
while (readerOffset < Length)
|
||||||
|
{
|
||||||
|
reader.SeekSet(readerOffset);
|
||||||
|
var messageLength = reader.ReadInt();
|
||||||
|
|
||||||
|
if (writerAvailable < sizeof(int) + messageLength)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteInt(messageLength);
|
||||||
|
|
||||||
|
var messageOffset = HeadIndex + reader.GetBytesRead();
|
||||||
|
writer.WriteBytes((byte*)m_Data.GetUnsafePtr() + messageOffset, messageLength);
|
||||||
|
|
||||||
|
writerAvailable -= sizeof(int) + messageLength;
|
||||||
|
readerOffset += sizeof(int) + messageLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.Capacity - writerAvailable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fill the given <see cref="DataStreamWriter"/> with as many bytes from the queue as
|
||||||
|
/// possible, disregarding message boundaries.
|
||||||
|
/// </summary>
|
||||||
|
///<remarks>
|
||||||
|
/// This does NOT actually consume anything from the queue. That is, calling this method
|
||||||
|
/// does not reduce the length of the queue. Callers are expected to call
|
||||||
|
/// <see cref="Consume"/> with the value returned by this method afterwards if the data can
|
||||||
|
/// be safely removed from the queue (e.g. if it was sent successfully).
|
||||||
|
///
|
||||||
|
/// This method should not be used together with <see cref="FillWriterWithMessages"/> since
|
||||||
|
/// this could lead to reading messages from a corrupted queue.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="writer">The <see cref="DataStreamWriter"/> to write to.</param>
|
||||||
|
/// <returns>How many bytes were written to the writer.</returns>
|
||||||
|
public int FillWriterWithBytes(ref DataStreamWriter writer)
|
||||||
|
{
|
||||||
|
if (!IsCreated || Length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var copyLength = Math.Min(writer.Capacity, Length);
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
writer.WriteBytes((byte*)m_Data.GetUnsafePtr() + HeadIndex, copyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Consume a number of bytes from the head of the queue.</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This should only be called with a size that matches the last value returned by
|
||||||
|
/// <see cref="FillWriter"/>. Anything else will result in a corrupted queue.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="size">Number of bytes to consume from the queue.</param>
|
||||||
|
public void Consume(int size)
|
||||||
|
{
|
||||||
|
if (size >= Length)
|
||||||
|
{
|
||||||
|
HeadIndex = 0;
|
||||||
|
TailIndex = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HeadIndex += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a32aeecf69a2542469927066f5b88005
|
guid: ddf8f97f695d740f297dc42242b76b8c
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
8
Runtime/Transports/UTP/NetworkMetricsContext.cs
Normal file
8
Runtime/Transports/UTP/NetworkMetricsContext.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Unity.Netcode.Transports.UTP
|
||||||
|
{
|
||||||
|
public struct NetworkMetricsContext
|
||||||
|
{
|
||||||
|
public uint PacketSentCount;
|
||||||
|
public uint PacketReceivedCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Runtime/Transports/UTP/NetworkMetricsContext.cs.meta
Normal file
11
Runtime/Transports/UTP/NetworkMetricsContext.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: adb0270501ff1421896ce15cc75bd56a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
70
Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs
Normal file
70
Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#if MULTIPLAYER_TOOLS
|
||||||
|
#if MULTIPLAYER_TOOLS_1_0_0_PRE_7
|
||||||
|
using AOT;
|
||||||
|
using Unity.Burst;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
using Unity.Networking.Transport;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Unity.Netcode.Transports.UTP
|
||||||
|
{
|
||||||
|
[BurstCompile]
|
||||||
|
internal unsafe struct NetworkMetricsPipelineStage : INetworkPipelineStage
|
||||||
|
{
|
||||||
|
static TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate> ReceiveFunction = new TransportFunctionPointer<NetworkPipelineStage.ReceiveDelegate>(Receive);
|
||||||
|
static TransportFunctionPointer<NetworkPipelineStage.SendDelegate> SendFunction = new TransportFunctionPointer<NetworkPipelineStage.SendDelegate>(Send);
|
||||||
|
static TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate> InitializeConnectionFunction = new TransportFunctionPointer<NetworkPipelineStage.InitializeConnectionDelegate>(InitializeConnection);
|
||||||
|
|
||||||
|
public NetworkPipelineStage StaticInitialize(byte* staticInstanceBuffer,
|
||||||
|
int staticInstanceBufferLength,
|
||||||
|
NetworkSettings settings)
|
||||||
|
{
|
||||||
|
return new NetworkPipelineStage(
|
||||||
|
ReceiveFunction,
|
||||||
|
SendFunction,
|
||||||
|
InitializeConnectionFunction,
|
||||||
|
ReceiveCapacity: 0,
|
||||||
|
SendCapacity: 0,
|
||||||
|
HeaderCapacity: 0,
|
||||||
|
SharedStateCapacity: UnsafeUtility.SizeOf<NetworkMetricsContext>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int StaticSize => 0;
|
||||||
|
|
||||||
|
[BurstCompile(DisableDirectCall = true)]
|
||||||
|
[MonoPInvokeCallback(typeof(NetworkPipelineStage.ReceiveDelegate))]
|
||||||
|
private static void Receive(ref NetworkPipelineContext networkPipelineContext,
|
||||||
|
ref InboundRecvBuffer inboundReceiveBuffer,
|
||||||
|
ref NetworkPipelineStage.Requests requests,
|
||||||
|
int systemHeaderSize)
|
||||||
|
{
|
||||||
|
var networkMetricContext = (NetworkMetricsContext*)networkPipelineContext.internalSharedProcessBuffer;
|
||||||
|
networkMetricContext->PacketReceivedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BurstCompile(DisableDirectCall = true)]
|
||||||
|
[MonoPInvokeCallback(typeof(NetworkPipelineStage.SendDelegate))]
|
||||||
|
private static int Send(ref NetworkPipelineContext networkPipelineContext,
|
||||||
|
ref InboundSendBuffer inboundSendBuffer,
|
||||||
|
ref NetworkPipelineStage.Requests requests,
|
||||||
|
int systemHeaderSize)
|
||||||
|
{
|
||||||
|
var networkMetricContext = (NetworkMetricsContext*)networkPipelineContext.internalSharedProcessBuffer;
|
||||||
|
networkMetricContext->PacketSentCount++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BurstCompile(DisableDirectCall = true)]
|
||||||
|
[MonoPInvokeCallback(typeof(NetworkPipelineStage.InitializeConnectionDelegate))]
|
||||||
|
private static void InitializeConnection(byte* staticInstanceBuffer, int staticInstanceBufferLength,
|
||||||
|
byte* sendProcessBuffer, int sendProcessBufferLength, byte* receiveProcessBuffer, int receiveProcessBufferLength,
|
||||||
|
byte* sharedProcessBuffer, int sharedProcessBufferLength)
|
||||||
|
{
|
||||||
|
var networkMetricContext = (NetworkMetricsContext*)sharedProcessBuffer;
|
||||||
|
networkMetricContext->PacketSentCount = 0;
|
||||||
|
networkMetricContext->PacketReceivedCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
11
Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs.meta
Normal file
11
Runtime/Transports/UTP/NetworkMetricsPipelineStage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 52b1ce9f83ce049c59327064bf70cee8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
1222
Runtime/Transports/UTP/UnityTransport.cs
Normal file
1222
Runtime/Transports/UTP/UnityTransport.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Runtime/Transports/UTP/UnityTransport.cs.meta
Normal file
11
Runtime/Transports/UTP/UnityTransport.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6960e84d07fb87f47956e7a81d71c4e6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -6,21 +6,30 @@
|
|||||||
"Unity.Multiplayer.NetStats",
|
"Unity.Multiplayer.NetStats",
|
||||||
"Unity.Multiplayer.NetStatsReporting",
|
"Unity.Multiplayer.NetStatsReporting",
|
||||||
"Unity.Multiplayer.NetworkSolutionInterface",
|
"Unity.Multiplayer.NetworkSolutionInterface",
|
||||||
"Unity.Collections"
|
"Unity.Multiplayer.Tools.MetricTypes",
|
||||||
|
"Unity.Multiplayer.Tools.NetStats",
|
||||||
|
"Unity.Multiplayer.Tools.NetStatsReporting",
|
||||||
|
"Unity.Multiplayer.Tools.NetworkSolutionInterface",
|
||||||
|
"Unity.Networking.Transport",
|
||||||
|
"Unity.Collections",
|
||||||
|
"Unity.Burst"
|
||||||
],
|
],
|
||||||
"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.7",
|
||||||
|
"define": "MULTIPLAYER_TOOLS_1_0_0_PRE_7"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"noEngineReferences": false
|
}
|
||||||
}
|
|
||||||
|
|||||||
8
TestHelpers.meta
Normal file
8
TestHelpers.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d627e2fb516d92242a4930e5cd9291e3
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
TestHelpers/Runtime.meta
Normal file
8
TestHelpers/Runtime.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e9af0202c9057c944b67aad6e4cdac96
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
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")]
|
||||||
11
TestHelpers/Runtime/AssemblyInfo.cs.meta
Normal file
11
TestHelpers/Runtime/AssemblyInfo.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 10ca1ce26995e754599c9eedc2c228d8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
TestHelpers/Runtime/Components.meta
Normal file
8
TestHelpers/Runtime/Components.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 19fbc3f43e13a9144a9c66c68a1c43c1
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user