com.unity.netcode.gameobjects@1.0.0-pre.2
# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.0.0-pre.2] - 2020-12-20 ### Added - Associated Known Issues for the 1.0.0-pre.1 release in the changelog ### Changed - Updated label for `1.0.0-pre.1` changelog section ## [1.0.0-pre.1] - 2020-12-20 ### Added - Added `ClientNetworkTransform` sample to the SDK package (#1168) - Added `Bootstrap` sample to the SDK package (#1140) - Enhanced `NetworkSceneManager` implementation with additive scene loading capabilities (#1080, #955, #913) - `NetworkSceneManager.OnSceneEvent` provides improved scene event notificaitons - Enhanced `NetworkTransform` implementation with per axis/component based and threshold based state replication (#1042, #1055, #1061, #1084, #1101) - Added a jitter-resistent `BufferedLinearInterpolator<T>` for `NetworkTransform` (#1060) - Implemented `NetworkPrefabHandler` that provides support for object pooling and `NetworkPrefab` overrides (#1073, #1004, #977, #905,#749, #727) - Implemented auto `NetworkObject` transform parent synchronization at runtime over the network (#855) - Adopted Unity C# Coding Standards in the codebase with `.editorconfig` ruleset (#666, #670) - When a client tries to spawn a `NetworkObject` an exception is thrown to indicate unsupported behavior. (#981) - Added a `NetworkTime` and `NetworkTickSystem` which allows for improved control over time and ticks. (#845) - Added a `OnNetworkDespawn` function to `NetworkObject` which gets called when a `NetworkObject` gets despawned and can be overriden. (#865) - Added `SnapshotSystem` that would allow variables and spawn/despawn messages to be sent in blocks (#805, #852, #862, #963, #1012, #1013, #1021, #1040, #1062, #1064, #1083, #1091, #1111, #1129, #1166, #1192) - Disabled by default for now, except spawn/despawn messages - Will leverage unreliable messages with eventual consistency - `NetworkBehaviour` and `NetworkObject`'s `NetworkManager` instances can now be overriden (#762) - Added metrics reporting for the new network profiler if the Multiplayer Tools package is present (#1104, #1089, #1096, #1086, #1072, #1058, #960, #897, #891, #878) - `NetworkBehaviour.IsSpawned` a quick (and stable) way to determine if the associated NetworkObject is spawned (#1190) - Added `NetworkRigidbody` and `NetworkRigidbody2D` components to support networking `Rigidbody` and `Rigidbody2D` components (#1202, #1175) - Added `NetworkObjectReference` and `NetworkBehaviourReference` structs which allow to sending `NetworkObject/Behaviours` over RPCs/`NetworkVariable`s (#1173) - Added `NetworkAnimator` component to support networking `Animator` component (#1281, #872) ### Changed - Bumped minimum Unity version, renamed package as "Unity Netcode for GameObjects", replaced `MLAPI` namespace and its variants with `Unity.Netcode` namespace and per asm-def variants (#1007, #1009, #1015, #1017, #1019, #1025, #1026, #1065) - Minimum Unity version: - 2019.4 → 2020.3+ - Package rename: - Display name: `MLAPI Networking Library` → `Netcode for GameObjects` - Name: `com.unity.multiplayer.mlapi` → `com.unity.netcode.gameobjects` - Updated package description - All `MLAPI.x` namespaces are replaced with `Unity.Netcode` - `MLAPI.Messaging` → `Unity.Netcode` - `MLAPI.Connection` → `Unity.Netcode` - `MLAPI.Logging` → `Unity.Netcode` - `MLAPI.SceneManagement` → `Unity.Netcode` - and other `MLAPI.x` variants to `Unity.Netcode` - All assembly definitions are renamed with `Unity.Netcode.x` variants - `Unity.Multiplayer.MLAPI.Runtime` → `Unity.Netcode.Runtime` - `Unity.Multiplayer.MLAPI.Editor` → `Unity.Netcode.Editor` - and other `Unity.Multiplayer.MLAPI.x` variants to `Unity.Netcode.x` variants - Renamed `Prototyping` namespace and assembly definition to `Components` (#1145) - Changed `NetworkObject.Despawn(bool destroy)` API to default to `destroy = true` for better usability (#1217) - Scene registration in `NetworkManager` is now replaced by Build Setttings → Scenes in Build List (#1080) - `NetworkSceneManager.SwitchScene` has been replaced by `NetworkSceneManager.LoadScene` (#955) - `NetworkManager, NetworkConfig, and NetworkSceneManager` scene registration replaced with scenes in build list (#1080) - `GlobalObjectIdHash` replaced `PrefabHash` and `PrefabHashGenerator` for stability and consistency (#698) - `NetworkStart` has been renamed to `OnNetworkSpawn`. (#865) - Network variable cleanup - eliminated shared mode, variables are server-authoritative (#1059, #1074) - `NetworkManager` and other systems are no longer singletons/statics (#696, #705, #706, #737, #738, #739, #746, #747, #763, #765, #766, #783, #784, #785, #786, #787, #788) - Changed `INetworkSerializable.NetworkSerialize` method signature to use `BufferSerializer<T>` instead of `NetworkSerializer` (#1187) - Changed `CustomMessagingManager`'s methods to use `FastBufferWriter` and `FastBufferReader` instead of `Stream` (#1187) - Reduced internal runtime allocations by removing LINQ calls and replacing managed lists/arrays with native collections (#1196) ### Removed - Removed `NetworkNavMeshAgent` (#1150) - Removed `NetworkDictionary`, `NetworkSet` (#1149) - Removed `NetworkVariableSettings` (#1097) - Removed predefined `NetworkVariable<T>` types (#1093) - Removed `NetworkVariableBool`, `NetworkVariableByte`, `NetworkVariableSByte`, `NetworkVariableUShort`, `NetworkVariableShort`, `NetworkVariableUInt`, `NetworkVariableInt`, `NetworkVariableULong`, `NetworkVariableLong`, `NetworkVariableFloat`, `NetworkVariableDouble`, `NetworkVariableVector2`, `NetworkVariableVector3`, `NetworkVariableVector4`, `NetworkVariableColor`, `NetworkVariableColor32`, `NetworkVariableRay`, `NetworkVariableQuaternion` - Removed `NetworkChannel` and `MultiplexTransportAdapter` (#1133) - Removed ILPP backend for 2019.4, minimum required version is 2020.3+ (#895) - `NetworkManager.NetworkConfig` had the following properties removed: (#1080) - Scene Registrations no longer exists - Allow Runtime Scene Changes was no longer needed and was removed - Removed the NetworkObject.Spawn payload parameter (#1005) - Removed `ProfilerCounter`, the original MLAPI network profiler, and the built-in network profiler module (2020.3). A replacement can now be found in the Multiplayer Tools package. (#1048) - Removed UNet RelayTransport and related relay functionality in UNetTransport (#1081) - Removed `UpdateStage` parameter from `ServerRpcSendParams` and `ClientRpcSendParams` (#1187) - Removed `NetworkBuffer`, `NetworkWriter`, `NetworkReader`, `NetworkSerializer`, `PooledNetworkBuffer`, `PooledNetworkWriter`, and `PooledNetworkReader` (#1187) - Removed `EnableNetworkVariable` in `NetworkConfig`, it is always enabled now (#1179) - Removed `NetworkTransform`'s FixedSendsPerSecond, AssumeSyncedSends, InterpolateServer, ExtrapolatePosition, MaxSendsToExtrapolate, Channel, EnableNonProvokedResendChecks, DistanceSendrate (#1060) (#826) (#1042, #1055, #1061, #1084, #1101) - Removed `NetworkManager`'s `StopServer()`, `StopClient()` and `StopHost()` methods and replaced with single `NetworkManager.Shutdown()` method for all (#1108) ### Fixed - Fixed ServerRpc ownership check to `Debug.LogError` instead of `Debug.LogWarning` (#1126) - Fixed `NetworkObject.OwnerClientId` property changing before `NetworkBehaviour.OnGainedOwnership()` callback (#1092) - Fixed `NetworkBehaviourILPP` to iterate over all types in an assembly (#803) - Fixed cross-asmdef RPC ILPP by importing types into external assemblies (#678) - Fixed `NetworkManager` shutdown when quitting the application or switching scenes (#1011) - Now `NetworkManager` shutdowns correctly and despawns existing `NetworkObject`s - Fixed Only one `PlayerPrefab` can be selected on `NetworkManager` inspector UI in the editor (#676) - Fixed connection approval not being triggered for host (#675) - Fixed various situations where messages could be processed in an invalid order, resulting in errors (#948, #1187, #1218) - Fixed `NetworkVariable`s being default-initialized on the client instead of being initialized with the desired value (#1266) - Improved runtime performance and reduced GC pressure (#1187) - Fixed #915 - clients are receiving data from objects not visible to them (#1099) - Fixed `NetworkTransform`'s "late join" issues, `NetworkTransform` now uses `NetworkVariable`s instead of RPCs (#826) - Throw an exception for silent failure when a client tries to get another player's `PlayerObject`, it is now only allowed on the server-side (#844) ### Known Issues - `NetworkVariable` does not serialize `INetworkSerializable` types through their `NetworkSerialize` implementation - `NetworkObjects` marked as `DontDestroyOnLoad` are disabled during some network scene transitions - `NetworkTransform` interpolates from the origin when switching Local Space synchronization - Exceptions thrown in `OnNetworkSpawn` user code for an object will prevent the callback in other objects - Cannot send an array of `INetworkSerializable` in RPCs - ILPP generation fails with special characters in project path ## [0.2.0] - 2021-06-03 WIP version increment to pass package validation checks. Changelog & final version number TBD. ## [0.1.1] - 2021-06-01 This is hotfix v0.1.1 for the initial experimental Unity MLAPI Package. ### Changed - Fixed issue with the Unity Registry package version missing some fixes from the v0.1.0 release. ## [0.1.0] - 2021-03-23 This is the initial experimental Unity MLAPI Package, v0.1.0. ### Added - Refactored a new standard for Remote Procedure Call (RPC) in MLAPI which provides increased performance, significantly reduced boilerplate code, and extensibility for future-proofed code. MLAPI RPC includes `ServerRpc` and `ClientRpc` to execute logic on the server and client-side. This provides a single performant unified RPC solution, replacing MLAPI Convenience and Performance RPC (see [here](#removed-features)). - Added standarized serialization types, including built-in and custom serialization flows. See [RFC #2](https://github.com/Unity-Technologies/com.unity.multiplayer.rfcs/blob/master/text/0002-serializable-types.md) for details. - `INetworkSerializable` interface replaces `IBitWritable`. - Added `NetworkSerializer`..., which is the main aggregator that implements serialization code for built-in supported types and holds `NetworkReader` and `NetworkWriter` instances internally. - Added a Network Update Loop infrastructure that aids Netcode systems to update (such as RPC queue and transport) outside of the standard `MonoBehaviour` event cycle. See [RFC #8](https://github.com/Unity-Technologies/com.unity.multiplayer.rfcs/blob/master/text/0008-network-update-loop.md) and the following details: - It uses Unity's [low-level Player Loop API](https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.html) and allows for registering `INetworkUpdateSystem`s with `NetworkUpdate` methods to be executed at specific `NetworkUpdateStage`s, which may also be before or after `MonoBehaviour`-driven game logic execution. - You will typically interact with `NetworkUpdateLoop` for registration and `INetworkUpdateSystem` for implementation. - `NetworkVariable`s are now tick-based using the `NetworkTickSystem`, tracking time through network interactions and syncs. - Added message batching to handle consecutive RPC requests sent to the same client. `RpcBatcher` sends batches based on requests from the `RpcQueueProcessing`, by batch size threshold or immediately. - [GitHub 494](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/494): Added a constraint to allow one `NetworkObject` per `GameObject`, set through the `DisallowMultipleComponent` attribute. - Integrated MLAPI with the Unity Profiler for versions 2020.2 and later: - Added new profiler modules for MLAPI that report important network data. - Attached the profiler to a remote player to view network data over the wire. - A test project is available for building and experimenting with MLAPI features. This project is available in the MLAPI GitHub [testproject folder](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/tree/release/0.1.0/testproject). - Added a [MLAPI Community Contributions](https://github.com/Unity-Technologies/mlapi-community-contributions/tree/master/com.mlapi.contrib.extensions) new GitHub repository to accept extensions from the MLAPI community. Current extensions include moved MLAPI features for lag compensation (useful for Server Authoritative actions) and `TrackedObject`. ### Changed - [GitHub 520](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/520): MLAPI now uses the Unity Package Manager for installation management. - Added functionality and usability to `NetworkVariable`, previously called `NetworkVar`. Updates enhance options and fully replace the need for `SyncedVar`s. - [GitHub 507](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/507): Reimplemented `NetworkAnimator`, which synchronizes animation states for networked objects. - GitHub [444](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/444) and [455](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/455): Channels are now represented as bytes instead of strings. For users of previous versions of MLAPI, this release renames APIs due to refactoring. All obsolete marked APIs have been removed as per [GitHub 513](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/513) and [GitHub 514](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/514). | Previous MLAPI Versions | V 0.1.0 Name | | -- | -- | | `NetworkingManager` | `NetworkManager` | | `NetworkedObject` | `NetworkObject` | | `NetworkedBehaviour` | `NetworkBehaviour` | | `NetworkedClient` | `NetworkClient` | | `NetworkedPrefab` | `NetworkPrefab` | | `NetworkedVar` | `NetworkVariable` | | `NetworkedTransform` | `NetworkTransform` | | `NetworkedAnimator` | `NetworkAnimator` | | `NetworkedAnimatorEditor` | `NetworkAnimatorEditor` | | `NetworkedNavMeshAgent` | `NetworkNavMeshAgent` | | `SpawnManager` | `NetworkSpawnManager` | | `BitStream` | `NetworkBuffer` | | `BitReader` | `NetworkReader` | | `BitWriter` | `NetworkWriter` | | `NetEventType` | `NetworkEventType` | | `ChannelType` | `NetworkDelivery` | | `Channel` | `NetworkChannel` | | `Transport` | `NetworkTransport` | | `NetworkedDictionary` | `NetworkDictionary` | | `NetworkedList` | `NetworkList` | | `NetworkedSet` | `NetworkSet` | | `MLAPIConstants` | `NetworkConstants` | | `UnetTransport` | `UNetTransport` | ### Fixed - [GitHub 460](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/460): Fixed an issue for RPC where the host-server was not receiving RPCs from the host-client and vice versa without the loopback flag set in `NetworkingManager`. - Fixed an issue where data in the Profiler was incorrectly aggregated and drawn, which caused the profiler data to increment indefinitely instead of resetting each frame. - Fixed an issue the client soft-synced causing PlayMode client-only scene transition issues, caused when running the client in the editor and the host as a release build. Users may have encountered a soft sync of `NetworkedInstanceId` issues in the `SpawnManager.ClientCollectSoftSyncSceneObjectSweep` method. - [GitHub 458](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/458): Fixed serialization issues in `NetworkList` and `NetworkDictionary` when running in Server mode. - [GitHub 498](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/498): Fixed numerical precision issues to prevent not a number (NaN) quaternions. - [GitHub 438](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/438): Fixed booleans by reaching or writing bytes instead of bits. - [GitHub 519](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/519): Fixed an issue where calling `Shutdown()` before making `NetworkManager.Singleton = null` is null on `NetworkManager.OnDestroy()`. ### Removed With a new release of MLAPI in Unity, some features have been removed: - SyncVars have been removed from MLAPI. Use `NetworkVariable`s in place of this functionality. <!-- MTT54 --> - [GitHub 527](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/527): Lag compensation systems and `TrackedObject` have moved to the new [MLAPI Community Contributions](https://github.com/Unity-Technologies/mlapi-community-contributions/tree/master/com.mlapi.contrib.extensions) repo. - [GitHub 509](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/509): Encryption has been removed from MLAPI. The `Encryption` option in `NetworkConfig` on the `NetworkingManager` is not available in this release. This change will not block game creation or running. A current replacement for this functionality is not available, and may be developed in future releases. See the following changes: - Removed `SecuritySendFlags` from all APIs. - Removed encryption, cryptography, and certificate configurations from APIs including `NetworkManager` and `NetworkConfig`. - Removed "hail handshake", including `NetworkManager` implementation and `NetworkConstants` entries. - Modified `RpcQueue` and `RpcBatcher` internals to remove encryption and authentication from reading and writing. - Removed the previous MLAPI Profiler editor window from Unity versions 2020.2 and later. - Removed previous MLAPI Convenience and Performance RPC APIs with the new standard RPC API. See [RFC #1](https://github.com/Unity-Technologies/com.unity.multiplayer.rfcs/blob/master/text/0001-std-rpc-api.md) for details. - [GitHub 520](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/520): Removed the MLAPI Installer. ### Known Issues - `NetworkNavMeshAgent` does not synchronize mesh data, Agent Size, Steering, Obstacle Avoidance, or Path Finding settings. It only synchronizes the destination and velocity, not the path to the destination. - For `RPC`, methods with a `ClientRpc` or `ServerRpc` suffix which are not marked with [ServerRpc] or [ClientRpc] will cause a compiler error. - For `NetworkAnimator`, Animator Overrides are not supported. Triggers do not work. - For `NetworkVariable`, the `NetworkDictionary` `List` and `Set` must use the `reliableSequenced` channel. - `NetworkObjects`s are supported but when spawning a prefab with nested child network objects you have to manually call spawn on them - `NetworkTransform` have the following issues: - Replicated objects may have jitter. - The owner is always authoritative about the object's position. - Scale is not synchronized. - Connection Approval is not called on the host client. - For `NamedMessages`, always use `NetworkBuffer` as the underlying stream for sending named and unnamed messages. - For `NetworkManager`, connection management is limited. Use `IsServer`, `IsClient`, `IsConnectedClient`, or other code to check if MLAPI connected correctly. ## [0.0.1-preview.1] - 2020-12-20 This was an internally-only-used version of the Unity MLAPI Package
This commit is contained in:
12
Runtime/AssemblyInfo.cs
Normal file
12
Runtime/AssemblyInfo.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.Editor.CodeGen")]
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.Editor")]
|
||||
[assembly: InternalsVisibleTo("TestProject.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("TestProject.RuntimeTests")]
|
||||
[assembly: InternalsVisibleTo("TestProject.ToolsIntegration.RuntimeTests")]
|
||||
#endif
|
||||
[assembly: InternalsVisibleTo("Unity.Netcode.RuntimeTests")]
|
||||
|
||||
11
Runtime/AssemblyInfo.cs.meta
Normal file
11
Runtime/AssemblyInfo.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9fd74adf4a0f6e479be3978543dc6a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Collections.meta
Normal file
8
Runtime/Collections.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2ef964afcae91248b2298b479ed1b53
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
77
Runtime/Collections/FixedQueue.cs
Normal file
77
Runtime/Collections/FixedQueue.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Queue with a fixed size
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the queue</typeparam>
|
||||
public sealed class FixedQueue<T>
|
||||
{
|
||||
private readonly T[] m_Queue;
|
||||
private int m_QueueCount = 0;
|
||||
private int m_QueueStart;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of enqueued objects
|
||||
/// </summary>
|
||||
public int Count => m_QueueCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at a given virtual index
|
||||
/// </summary>
|
||||
/// <param name="index">The virtual index to get the item from</param>
|
||||
/// <returns>The element at the virtual index</returns>
|
||||
public T this[int index] => m_Queue[(m_QueueStart + index) % m_Queue.Length];
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new FixedQueue with a given size
|
||||
/// </summary>
|
||||
/// <param name="maxSize">The size of the queue</param>
|
||||
public FixedQueue(int maxSize)
|
||||
{
|
||||
m_Queue = new T[maxSize];
|
||||
m_QueueStart = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues an object
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
/// <returns></returns>
|
||||
public bool Enqueue(T t)
|
||||
{
|
||||
m_Queue[(m_QueueStart + m_QueueCount) % m_Queue.Length] = t;
|
||||
if (++m_QueueCount > m_Queue.Length)
|
||||
{
|
||||
--m_QueueCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues an object
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public T Dequeue()
|
||||
{
|
||||
if (--m_QueueCount == -1)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Cannot dequeue empty queue!");
|
||||
}
|
||||
|
||||
T res = m_Queue[m_QueueStart];
|
||||
m_QueueStart = (m_QueueStart + 1) % m_Queue.Length;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at a given virtual index
|
||||
/// </summary>
|
||||
/// <param name="index">The virtual index to get the item from</param>
|
||||
/// <returns>The element at the virtual index</returns>
|
||||
public T ElementAt(int index) => m_Queue[(m_QueueStart + index) % m_Queue.Length];
|
||||
}
|
||||
}
|
||||
11
Runtime/Collections/FixedQueue.cs.meta
Normal file
11
Runtime/Collections/FixedQueue.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8514b4eca0c7044d9b92faf9407ec93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Configuration.meta
Normal file
8
Runtime/Configuration.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3cc7700dfd03ee4397858710461d179
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Runtime/Configuration/HashSize.cs
Normal file
20
Runtime/Configuration/HashSize.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the length of a var int encoded hash
|
||||
/// Note that the HashSize does not say anything about the actual final output due to the var int encoding
|
||||
/// It just says how many bytes the maximum will be
|
||||
/// </summary>
|
||||
public enum HashSize : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Four byte hash
|
||||
/// </summary>
|
||||
VarIntFourBytes,
|
||||
|
||||
/// <summary>
|
||||
/// Eight byte hash
|
||||
/// </summary>
|
||||
VarIntEightBytes
|
||||
}
|
||||
}
|
||||
11
Runtime/Configuration/HashSize.cs.meta
Normal file
11
Runtime/Configuration/HashSize.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ae94548754e0a0409da85c0e3235bb4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
269
Runtime/Configuration/NetworkConfig.cs
Normal file
269
Runtime/Configuration/NetworkConfig.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// The configuration object used to start server, client and hosts
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class NetworkConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The protocol version. Different versions doesn't talk to each other.
|
||||
/// </summary>
|
||||
[Tooltip("Use this to make two builds incompatible with each other")]
|
||||
public ushort ProtocolVersion = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The transport hosts the sever uses
|
||||
/// </summary>
|
||||
[Tooltip("The NetworkTransport to use")]
|
||||
public NetworkTransport NetworkTransport = null;
|
||||
|
||||
/// <summary>
|
||||
/// The default player prefab
|
||||
/// </summary>
|
||||
[Tooltip("When set, NetworkManager will automatically create and spawn the assigned player prefab. This can be overridden by adding it to the NetworkPrefabs list and selecting override.")]
|
||||
public GameObject PlayerPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// A list of prefabs that can be dynamically spawned.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
[Tooltip("The prefabs that can be spawned across the network")]
|
||||
internal List<NetworkPrefab> NetworkPrefabs = new List<NetworkPrefab>();
|
||||
|
||||
/// <summary>
|
||||
/// This dictionary provides a quick way to check and see if a NetworkPrefab has a NetworkPrefab override.
|
||||
/// Generated at runtime and OnValidate
|
||||
/// </summary>
|
||||
internal Dictionary<uint, NetworkPrefab> NetworkPrefabOverrideLinks = new Dictionary<uint, NetworkPrefab>();
|
||||
|
||||
internal Dictionary<uint, uint> OverrideToNetworkPrefab = new Dictionary<uint, uint>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The tickrate of network ticks. This value controls how often netcode runs user code and sends out data.
|
||||
/// </summary>
|
||||
[Tooltip("The tickrate. This value controls how often netcode runs user code and sends out data. The value is in 'ticks per seconds' which means a value of 50 will result in 50 ticks being executed per second or a fixed delta time of 0.02.")]
|
||||
public uint TickRate = 30;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of seconds to wait for handshake to complete before timing out a client
|
||||
/// </summary>
|
||||
[Tooltip("The amount of seconds to wait for the handshake to complete before the client times out")]
|
||||
public int ClientConnectionBufferTimeout = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to use connection approval
|
||||
/// </summary>
|
||||
[Tooltip("Whether or not to force clients to be approved before they connect")]
|
||||
public bool ConnectionApproval = false;
|
||||
|
||||
/// <summary>
|
||||
/// The data to send during connection which can be used to decide on if a client should get accepted
|
||||
/// </summary>
|
||||
[Tooltip("The connection data sent along with connection requests")]
|
||||
public byte[] ConnectionData = new byte[0];
|
||||
|
||||
/// <summary>
|
||||
/// If your logic uses the NetworkTime, this should probably be turned off. If however it's needed to maximize accuracy, this is recommended to be turned on
|
||||
/// </summary>
|
||||
[Tooltip("Enable this to re-sync the NetworkTime after the initial sync")]
|
||||
public bool EnableTimeResync = false;
|
||||
|
||||
/// <summary>
|
||||
/// If time re-sync is turned on, this specifies the interval between syncs in seconds.
|
||||
/// </summary>
|
||||
[Tooltip("The amount of seconds between re-syncs of NetworkTime, if enabled")]
|
||||
public int TimeResyncInterval = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to ensure that NetworkVariables can be read even if a client accidentally writes where its not allowed to. This costs some CPU and bandwidth.
|
||||
/// </summary>
|
||||
[Tooltip("Ensures that NetworkVariables can be read even if a client accidental writes where its not allowed to. This will cost some CPU time and bandwidth")]
|
||||
public bool EnsureNetworkVariableLengthSafety = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables scene management. This will allow network scene switches and automatic scene difference corrections upon connect.
|
||||
/// SoftSynced scene objects wont work with this disabled. That means that disabling SceneManagement also enables PrefabSync.
|
||||
/// </summary>
|
||||
[Tooltip("Enables scene management. This will allow network scene switches and automatic scene difference corrections upon connect.\n" +
|
||||
"SoftSynced scene objects wont work with this disabled. That means that disabling SceneManagement also enables PrefabSync.")]
|
||||
public bool EnableSceneManagement = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the netcode should check for differences in the prefabs at connection.
|
||||
/// If you dynamically add prefabs at runtime, turn this OFF
|
||||
/// </summary>
|
||||
[Tooltip("Whether or not the netcode should check for differences in the prefab lists at connection")]
|
||||
public bool ForceSamePrefabs = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, NetworkIds will be reused after the NetworkIdRecycleDelay.
|
||||
/// </summary>
|
||||
[Tooltip("If true, NetworkIds will be reused after the NetworkIdRecycleDelay")]
|
||||
public bool RecycleNetworkIds = true;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of seconds a NetworkId has to be unused in order for it to be reused.
|
||||
/// </summary>
|
||||
[Tooltip("The amount of seconds a NetworkId has to unused in order for it to be reused")]
|
||||
public float NetworkIdRecycleDelay = 120f;
|
||||
|
||||
/// <summary>
|
||||
/// Decides how many bytes to use for Rpc messaging. Leave this to 2 bytes unless you are facing hash collisions
|
||||
/// </summary>
|
||||
[Tooltip("The maximum amount of bytes to use for RPC messages.")]
|
||||
public HashSize RpcHashSize = HashSize.VarIntFourBytes;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of seconds to wait for all clients to load or unload a requested scene
|
||||
/// </summary>
|
||||
[Tooltip("The amount of seconds to wait for all clients to load or unload a requested scene (only when EnableSceneManagement is enabled)")]
|
||||
public int LoadSceneTimeOut = 120;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time a message should be buffered for without being consumed. If it is not consumed within this time, it will be dropped.
|
||||
/// </summary>
|
||||
[Tooltip("The amount of time a message should be buffered for without being consumed. If it is not consumed within this time, it will be dropped")]
|
||||
public float MessageBufferTimeout = 20f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to enable network logs.
|
||||
/// </summary>
|
||||
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; } = false;
|
||||
/// <summary>
|
||||
/// Whether or not to enable Snapshot System for spawn and despawn commands. Not supported in this version.
|
||||
/// </summary>
|
||||
public bool UseSnapshotSpawn { get; } = false;
|
||||
/// <summary>
|
||||
/// When Snapshot System spawn is enabled: max size of Snapshot Messages. Meant to fit MTU.
|
||||
/// </summary>
|
||||
public int SnapshotMaxSpawnUsage { get; } = 1200;
|
||||
|
||||
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)
|
||||
/// <summary>
|
||||
/// Returns a base64 encoded version of the configuration
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string ToBase64()
|
||||
{
|
||||
NetworkConfig config = this;
|
||||
var writer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp);
|
||||
using (writer)
|
||||
{
|
||||
writer.WriteValueSafe(config.ProtocolVersion);
|
||||
writer.WriteValueSafe(config.TickRate);
|
||||
writer.WriteValueSafe(config.ClientConnectionBufferTimeout);
|
||||
writer.WriteValueSafe(config.ConnectionApproval);
|
||||
writer.WriteValueSafe(config.LoadSceneTimeOut);
|
||||
writer.WriteValueSafe(config.EnableTimeResync);
|
||||
writer.WriteValueSafe(config.EnsureNetworkVariableLengthSafety);
|
||||
writer.WriteValueSafe(config.RpcHashSize);
|
||||
writer.WriteValueSafe(ForceSamePrefabs);
|
||||
writer.WriteValueSafe(EnableSceneManagement);
|
||||
writer.WriteValueSafe(RecycleNetworkIds);
|
||||
writer.WriteValueSafe(NetworkIdRecycleDelay);
|
||||
writer.WriteValueSafe(EnableNetworkLogs);
|
||||
|
||||
// Allocates
|
||||
return Convert.ToBase64String(writer.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the NetworkConfig data with that from a base64 encoded version
|
||||
/// </summary>
|
||||
/// <param name="base64">The base64 encoded version</param>
|
||||
public void FromBase64(string base64)
|
||||
{
|
||||
NetworkConfig config = this;
|
||||
byte[] binary = Convert.FromBase64String(base64);
|
||||
using var reader = new FastBufferReader(binary, Allocator.Temp);
|
||||
using (reader)
|
||||
{
|
||||
reader.ReadValueSafe(out config.ProtocolVersion);
|
||||
reader.ReadValueSafe(out config.TickRate);
|
||||
reader.ReadValueSafe(out config.ClientConnectionBufferTimeout);
|
||||
reader.ReadValueSafe(out config.ConnectionApproval);
|
||||
reader.ReadValueSafe(out config.LoadSceneTimeOut);
|
||||
reader.ReadValueSafe(out config.EnableTimeResync);
|
||||
reader.ReadValueSafe(out config.EnsureNetworkVariableLengthSafety);
|
||||
reader.ReadValueSafe(out config.RpcHashSize);
|
||||
reader.ReadValueSafe(out config.ForceSamePrefabs);
|
||||
reader.ReadValueSafe(out config.EnableSceneManagement);
|
||||
reader.ReadValueSafe(out config.RecycleNetworkIds);
|
||||
reader.ReadValueSafe(out config.NetworkIdRecycleDelay);
|
||||
reader.ReadValueSafe(out config.EnableNetworkLogs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ulong? m_ConfigHash = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a SHA256 hash of parts of the NetworkConfig instance
|
||||
/// </summary>
|
||||
/// <param name="cache"></param>
|
||||
/// <returns></returns>
|
||||
public ulong GetConfig(bool cache = true)
|
||||
{
|
||||
if (m_ConfigHash != null && cache)
|
||||
{
|
||||
return m_ConfigHash.Value;
|
||||
}
|
||||
|
||||
var writer = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp);
|
||||
using (writer)
|
||||
{
|
||||
writer.WriteValueSafe(ProtocolVersion);
|
||||
writer.WriteValueSafe(NetworkConstants.PROTOCOL_VERSION);
|
||||
|
||||
if (ForceSamePrefabs)
|
||||
{
|
||||
var sortedDictionary = NetworkPrefabOverrideLinks.OrderBy(x => x.Key);
|
||||
foreach (var sortedEntry in sortedDictionary)
|
||||
|
||||
{
|
||||
writer.WriteValueSafe(sortedEntry.Key);
|
||||
}
|
||||
}
|
||||
writer.WriteValueSafe(ConnectionApproval);
|
||||
writer.WriteValueSafe(ForceSamePrefabs);
|
||||
writer.WriteValueSafe(EnableSceneManagement);
|
||||
writer.WriteValueSafe(EnsureNetworkVariableLengthSafety);
|
||||
writer.WriteValueSafe(RpcHashSize);
|
||||
|
||||
if (cache)
|
||||
{
|
||||
m_ConfigHash = XXHash.Hash64(writer.ToArray());
|
||||
return m_ConfigHash.Value;
|
||||
}
|
||||
|
||||
return XXHash.Hash64(writer.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares a SHA256 hash with the current NetworkConfig instances hash
|
||||
/// </summary>
|
||||
/// <param name="hash"></param>
|
||||
/// <returns></returns>
|
||||
public bool CompareConfig(ulong hash)
|
||||
{
|
||||
return hash == GetConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
Runtime/Configuration/NetworkConfig.cs.meta
Normal file
11
Runtime/Configuration/NetworkConfig.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b056e7faa4d1cb4aac7bc304c765c3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
10
Runtime/Configuration/NetworkConstants.cs
Normal file
10
Runtime/Configuration/NetworkConstants.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// A static class containing network constants
|
||||
/// </summary>
|
||||
internal static class NetworkConstants
|
||||
{
|
||||
internal const string PROTOCOL_VERSION = "14.0.0";
|
||||
}
|
||||
}
|
||||
11
Runtime/Configuration/NetworkConstants.cs.meta
Normal file
11
Runtime/Configuration/NetworkConstants.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dae82900f88f6bb4a90c29d431f2b45a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Runtime/Configuration/NetworkPrefab.cs
Normal file
45
Runtime/Configuration/NetworkPrefab.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal enum NetworkPrefabOverride
|
||||
{
|
||||
None,
|
||||
Prefab,
|
||||
Hash
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class that represents a NetworkPrefab
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class NetworkPrefab
|
||||
{
|
||||
/// <summary>
|
||||
/// The override setttings for this NetworkPrefab
|
||||
/// </summary>
|
||||
public NetworkPrefabOverride Override;
|
||||
|
||||
/// <summary>
|
||||
/// Asset reference of the network prefab
|
||||
/// </summary>
|
||||
public GameObject Prefab;
|
||||
|
||||
/// <summary>
|
||||
/// Used when prefab is selected for the source prefab to override value (i.e. direct reference, the prefab is within the same project)
|
||||
/// We keep a separate value as the user might want to have something different than the default Prefab for the SourcePrefabToOverride
|
||||
/// </summary>
|
||||
public GameObject SourcePrefabToOverride;
|
||||
|
||||
/// <summary>
|
||||
/// Used when hash is selected for the source prefab to override value (i.e. a direct reference is not possible such as in a multi-project pattern)
|
||||
/// </summary>
|
||||
public uint SourceHashToOverride;
|
||||
|
||||
/// <summary>
|
||||
/// The prefab to replace (override) the source prefab with
|
||||
/// </summary>
|
||||
public GameObject OverridingTargetPrefab;
|
||||
}
|
||||
}
|
||||
11
Runtime/Configuration/NetworkPrefab.cs.meta
Normal file
11
Runtime/Configuration/NetworkPrefab.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10e0511afda4e7743b2cd7c9cf95e0ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Connection.meta
Normal file
8
Runtime/Connection.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7155f1167eb96846aab2132b37c2815
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
25
Runtime/Connection/NetworkClient.cs
Normal file
25
Runtime/Connection/NetworkClient.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// A NetworkClient
|
||||
/// </summary>
|
||||
public class NetworkClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The ClientId of the NetworkClient
|
||||
/// </summary>
|
||||
public ulong ClientId;
|
||||
|
||||
/// <summary>
|
||||
/// The PlayerObject of the Client
|
||||
/// </summary>
|
||||
public NetworkObject PlayerObject;
|
||||
|
||||
/// <summary>
|
||||
/// The NetworkObject's owned by this Client
|
||||
/// </summary>
|
||||
public readonly List<NetworkObject> OwnedObjects = new List<NetworkObject>();
|
||||
}
|
||||
}
|
||||
11
Runtime/Connection/NetworkClient.cs.meta
Normal file
11
Runtime/Connection/NetworkClient.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23ca4d14911834b41a761c62fb23773e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
33
Runtime/Connection/PendingClient.cs
Normal file
33
Runtime/Connection/PendingClient.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// A class representing a client that is currently in the process of connecting
|
||||
/// </summary>
|
||||
public class PendingClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The ClientId of the client
|
||||
/// </summary>
|
||||
public ulong ClientId { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The state of the connection process for the client
|
||||
/// </summary>
|
||||
public State ConnectionState { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The states of a connection
|
||||
/// </summary>
|
||||
public enum State
|
||||
{
|
||||
/// <summary>
|
||||
/// Waiting for client to send it's initial connection request
|
||||
/// </summary>
|
||||
PendingConnection,
|
||||
/// <summary>
|
||||
/// Waiting for client connection request to be approved
|
||||
/// </summary>
|
||||
PendingApproval
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Connection/PendingClient.cs.meta
Normal file
11
Runtime/Connection/PendingClient.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ffbc5b303de84a4196e24f503752218
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Core.meta
Normal file
8
Runtime/Core.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb92f4010a5924b408aca753b55bd5ae
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
388
Runtime/Core/IndexAllocator.cs
Normal file
388
Runtime/Core/IndexAllocator.cs
Normal file
@@ -0,0 +1,388 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Core/IndexAllocator.cs.meta
Normal file
11
Runtime/Core/IndexAllocator.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd9e1475e8c8e4a6d935fe2409e3bd26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
676
Runtime/Core/NetworkBehaviour.cs
Normal file
676
Runtime/Core/NetworkBehaviour.cs
Normal file
@@ -0,0 +1,676 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// The base class to override to write network code. Inherits MonoBehaviour
|
||||
/// </summary>
|
||||
public abstract class NetworkBehaviour : MonoBehaviour
|
||||
{
|
||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||
// RuntimeAccessModifiersILPP will make this `protected`
|
||||
internal enum __RpcExecStage
|
||||
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||
{
|
||||
None = 0,
|
||||
Server = 1,
|
||||
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
|
||||
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]
|
||||
// RuntimeAccessModifiersILPP will make this `protected`
|
||||
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 disable 414 // disable assigned but its value is never used
|
||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||
// RuntimeAccessModifiersILPP will make this `protected`
|
||||
internal void __sendServerRpc(FastBufferWriter writer, uint rpcMethodId, ServerRpcParams rpcParams, RpcDelivery delivery)
|
||||
#pragma warning restore 414 // restore assigned but its value is never used
|
||||
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||
{
|
||||
NetworkDelivery networkDelivery = NetworkDelivery.Reliable;
|
||||
switch (delivery)
|
||||
{
|
||||
case RpcDelivery.Reliable:
|
||||
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
||||
break;
|
||||
case RpcDelivery.Unreliable:
|
||||
if (writer.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort))
|
||||
{
|
||||
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
||||
}
|
||||
networkDelivery = NetworkDelivery.Unreliable;
|
||||
break;
|
||||
}
|
||||
|
||||
var message = new RpcMessage
|
||||
{
|
||||
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 (IsHost || IsServer)
|
||||
{
|
||||
using var tempBuffer = new FastBufferReader(writer, Allocator.Temp);
|
||||
var context = new NetworkContext
|
||||
{
|
||||
SenderId = NetworkManager.ServerClientId,
|
||||
Timestamp = Time.realtimeSinceStartup,
|
||||
SystemOwner = NetworkManager,
|
||||
// header information isn't valid since it's not a real message.
|
||||
// Passing false to canDefer prevents it being accessed.
|
||||
Header = new MessageHeader()
|
||||
};
|
||||
message.Handle(tempBuffer, context, NetworkManager, NetworkManager.ServerClientId, false);
|
||||
rpcMessageSize = tempBuffer.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
rpcMessageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ServerClientId);
|
||||
}
|
||||
|
||||
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
||||
{
|
||||
NetworkManager.NetworkMetrics.TrackRpcSent(
|
||||
NetworkManager.ServerClientId,
|
||||
NetworkObject,
|
||||
rpcMethodName,
|
||||
__getTypeName(),
|
||||
rpcMessageSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma warning disable 414 // disable assigned but its value is never used
|
||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||
// RuntimeAccessModifiersILPP will make this `protected`
|
||||
internal unsafe void __sendClientRpc(FastBufferWriter writer, uint rpcMethodId, ClientRpcParams rpcParams, RpcDelivery delivery)
|
||||
#pragma warning disable 414 // disable assigned but its value is never used
|
||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||
{
|
||||
NetworkDelivery networkDelivery = NetworkDelivery.Reliable;
|
||||
switch (delivery)
|
||||
{
|
||||
case RpcDelivery.Reliable:
|
||||
networkDelivery = NetworkDelivery.ReliableFragmentedSequenced;
|
||||
break;
|
||||
case RpcDelivery.Unreliable:
|
||||
if (writer.Length > MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE - sizeof(RpcMessage.RpcType) - sizeof(ulong) - sizeof(uint) - sizeof(ushort))
|
||||
{
|
||||
throw new OverflowException("RPC parameters are too large for unreliable delivery.");
|
||||
}
|
||||
networkDelivery = NetworkDelivery.Unreliable;
|
||||
break;
|
||||
}
|
||||
|
||||
var message = new RpcMessage
|
||||
{
|
||||
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
|
||||
// to ourself. Sadly we have to figure that out from the list of clientIds :(
|
||||
bool shouldSendToHost = false;
|
||||
|
||||
if (rpcParams.Send.TargetClientIds != null)
|
||||
{
|
||||
foreach (var clientId in rpcParams.Send.TargetClientIds)
|
||||
{
|
||||
if (clientId == NetworkManager.ServerClientId)
|
||||
{
|
||||
shouldSendToHost = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
messageSize = NetworkManager.SendMessage(message, networkDelivery, in rpcParams.Send.TargetClientIds);
|
||||
}
|
||||
else if (rpcParams.Send.TargetClientIdsNativeArray != null)
|
||||
{
|
||||
foreach (var clientId in rpcParams.Send.TargetClientIdsNativeArray)
|
||||
{
|
||||
if (clientId == NetworkManager.ServerClientId)
|
||||
{
|
||||
shouldSendToHost = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
messageSize = NetworkManager.SendMessage(message, networkDelivery, rpcParams.Send.TargetClientIdsNativeArray.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldSendToHost = IsHost;
|
||||
messageSize = NetworkManager.SendMessage(message, networkDelivery, NetworkManager.ConnectedClientsIds);
|
||||
}
|
||||
|
||||
// If we are a server/host then we just no op and send to ourself
|
||||
if (shouldSendToHost)
|
||||
{
|
||||
using var tempBuffer = new FastBufferReader(writer, Allocator.Temp);
|
||||
var context = new NetworkContext
|
||||
{
|
||||
SenderId = NetworkManager.ServerClientId,
|
||||
Timestamp = Time.realtimeSinceStartup,
|
||||
SystemOwner = NetworkManager,
|
||||
// header information isn't valid since it's not a real message.
|
||||
// Passing false to canDefer prevents it being accessed.
|
||||
Header = new MessageHeader()
|
||||
};
|
||||
message.Handle(tempBuffer, context, NetworkManager, NetworkManager.ServerClientId, false);
|
||||
messageSize = tempBuffer.Length;
|
||||
}
|
||||
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
|
||||
{
|
||||
foreach (var client in NetworkManager.ConnectedClients)
|
||||
{
|
||||
NetworkManager.NetworkMetrics.TrackRpcSent(
|
||||
client.Key,
|
||||
NetworkObject,
|
||||
rpcMethodName,
|
||||
__getTypeName(),
|
||||
messageSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public NetworkManager NetworkManager => NetworkObject.NetworkManager;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the object is the the personal clients player object
|
||||
/// </summary>
|
||||
public bool IsLocalPlayer => NetworkObject.IsLocalPlayer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if the object is owned by the local player or if the object is the local player object
|
||||
/// </summary>
|
||||
public bool IsOwner => NetworkObject.IsOwner;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if we are executing as server
|
||||
/// </summary>
|
||||
protected bool IsServer => IsRunning && NetworkManager.IsServer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if we are executing as client
|
||||
/// </summary>
|
||||
protected bool IsClient => IsRunning && NetworkManager.IsClient;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if we are executing as Host, I.E Server and Client
|
||||
/// </summary>
|
||||
protected bool IsHost => IsRunning && NetworkManager.IsHost;
|
||||
|
||||
private bool IsRunning => NetworkManager && NetworkManager.IsListening;
|
||||
|
||||
/// <summary>
|
||||
/// Gets Whether or not the object has a owner
|
||||
/// </summary>
|
||||
public bool IsOwnedByServer => NetworkObject.IsOwnedByServer;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public bool IsSpawned => HasNetworkObject ? NetworkObject.IsSpawned : false;
|
||||
|
||||
internal bool IsBehaviourEditable()
|
||||
{
|
||||
// Only server can MODIFY. So allow modification if network is either not running or we are server
|
||||
return !m_NetworkObject ||
|
||||
(m_NetworkObject.NetworkManager == null ||
|
||||
!m_NetworkObject.NetworkManager.IsListening ||
|
||||
m_NetworkObject.NetworkManager.IsServer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the NetworkObject that owns this NetworkBehaviour instance
|
||||
/// TODO: this needs an overhaul. It's expensive, it's ja little naive in how it looks for networkObject in
|
||||
/// its parent and worst, it creates a puzzle if you are a NetworkBehaviour wanting to see if you're live or not
|
||||
/// (e.g. editor code). All you want to do is find out if NetworkManager is null, but to do that you
|
||||
/// need NetworkObject, but if you try and grab NetworkObject and NetworkManager isn't up you'll get
|
||||
/// the warning below. This is why IsBehaviourEditable had to be created. Matt was going to re-do
|
||||
/// how NetworkObject works but it was close to the release and too risky to change
|
||||
///
|
||||
/// </summary>
|
||||
public NetworkObject NetworkObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_NetworkObject == null)
|
||||
{
|
||||
m_NetworkObject = GetComponentInParent<NetworkObject>();
|
||||
}
|
||||
|
||||
if (m_NetworkObject == null && NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"Could not get {nameof(NetworkObject)} for the {nameof(NetworkBehaviour)}. Are you missing a {nameof(NetworkObject)} component?");
|
||||
}
|
||||
|
||||
return m_NetworkObject;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not this NetworkBehaviour instance has a NetworkObject owner.
|
||||
/// </summary>
|
||||
public bool HasNetworkObject => NetworkObject != null;
|
||||
|
||||
private NetworkObject m_NetworkObject = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the NetworkId of the NetworkObject that owns this NetworkBehaviour
|
||||
/// </summary>
|
||||
public ulong NetworkObjectId => NetworkObject.NetworkObjectId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets NetworkId for this NetworkBehaviour from the owner NetworkObject
|
||||
/// </summary>
|
||||
public ushort NetworkBehaviourId => NetworkObject.GetNetworkBehaviourOrderIndex(this);
|
||||
|
||||
/// <summary>
|
||||
/// Internally caches the Id of this behaviour in a NetworkObject. Makes look-up faster
|
||||
/// </summary>
|
||||
internal ushort NetworkBehaviourIdCache = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a the NetworkBehaviour with a given BehaviourId for the current NetworkObject
|
||||
/// </summary>
|
||||
/// <param name="behaviourId">The behaviourId to return</param>
|
||||
/// <returns>Returns NetworkBehaviour with given behaviourId</returns>
|
||||
protected NetworkBehaviour GetNetworkBehaviour(ushort behaviourId)
|
||||
{
|
||||
return NetworkObject.GetNetworkBehaviourAtOrderIndex(behaviourId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ClientId that owns the NetworkObject
|
||||
/// </summary>
|
||||
public ulong OwnerClientId => NetworkObject.OwnerClientId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when the <see cref="NetworkObject"/> gets spawned, message handlers are ready to be registered and the network is setup.
|
||||
/// </summary>
|
||||
public virtual void OnNetworkSpawn() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when the <see cref="NetworkObject"/> gets despawned. Is called both on the server and clients.
|
||||
/// </summary>
|
||||
public virtual void OnNetworkDespawn() { }
|
||||
|
||||
internal void InternalOnNetworkSpawn()
|
||||
{
|
||||
InitializeVariables();
|
||||
}
|
||||
|
||||
internal void InternalOnNetworkDespawn()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when the local client gains ownership of this object
|
||||
/// </summary>
|
||||
public virtual void OnGainedOwnership() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when we loose ownership of this object
|
||||
/// </summary>
|
||||
public virtual void OnLostOwnership() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
|
||||
/// </summary>
|
||||
public virtual void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject) { }
|
||||
|
||||
private bool m_VarInit = false;
|
||||
|
||||
private readonly List<HashSet<int>> m_DeliveryMappedNetworkVariableIndices = new List<HashSet<int>>();
|
||||
private readonly List<NetworkDelivery> m_DeliveryTypesForNetworkVariableGroups = new List<NetworkDelivery>();
|
||||
internal readonly List<NetworkVariableBase> NetworkVariableFields = new List<NetworkVariableBase>();
|
||||
|
||||
private static Dictionary<Type, FieldInfo[]> s_FieldTypes = new Dictionary<Type, FieldInfo[]>();
|
||||
|
||||
private static FieldInfo[] GetFieldInfoForType(Type type)
|
||||
{
|
||||
if (!s_FieldTypes.ContainsKey(type))
|
||||
{
|
||||
s_FieldTypes.Add(type, GetFieldInfoForTypeRecursive(type));
|
||||
}
|
||||
|
||||
return s_FieldTypes[type];
|
||||
}
|
||||
|
||||
private static FieldInfo[] GetFieldInfoForTypeRecursive(Type type, List<FieldInfo> list = null)
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
list = new List<FieldInfo>();
|
||||
list.AddRange(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.AddRange(type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance));
|
||||
}
|
||||
|
||||
if (type.BaseType != null && type.BaseType != typeof(NetworkBehaviour))
|
||||
{
|
||||
return GetFieldInfoForTypeRecursive(type.BaseType, list);
|
||||
}
|
||||
|
||||
return list.OrderBy(x => x.Name, StringComparer.Ordinal).ToArray();
|
||||
}
|
||||
|
||||
internal void InitializeVariables()
|
||||
{
|
||||
if (m_VarInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_VarInit = true;
|
||||
|
||||
FieldInfo[] sortedFields = GetFieldInfoForType(GetType());
|
||||
|
||||
for (int i = 0; i < sortedFields.Length; i++)
|
||||
{
|
||||
Type fieldType = sortedFields[i].FieldType;
|
||||
|
||||
if (fieldType.IsSubclassOf(typeof(NetworkVariableBase)))
|
||||
{
|
||||
var instance = (NetworkVariableBase)sortedFields[i].GetValue(this);
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
instance = (NetworkVariableBase)Activator.CreateInstance(fieldType, true);
|
||||
sortedFields[i].SetValue(this, instance);
|
||||
}
|
||||
|
||||
instance.Initialize(this);
|
||||
|
||||
var instanceNameProperty = fieldType.GetProperty(nameof(NetworkVariableBase.Name));
|
||||
var sanitizedVariableName = sortedFields[i].Name.Replace("<", string.Empty).Replace(">k__BackingField", string.Empty);
|
||||
instanceNameProperty?.SetValue(instance, sanitizedVariableName);
|
||||
|
||||
NetworkVariableFields.Add(instance);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Create index map for delivery types
|
||||
var firstLevelIndex = new Dictionary<NetworkDelivery, int>();
|
||||
int secondLevelCounter = 0;
|
||||
|
||||
for (int i = 0; i < NetworkVariableFields.Count; i++)
|
||||
{
|
||||
var networkDelivery = NetworkVariableBase.Delivery;
|
||||
if (!firstLevelIndex.ContainsKey(networkDelivery))
|
||||
{
|
||||
firstLevelIndex.Add(networkDelivery, secondLevelCounter);
|
||||
m_DeliveryTypesForNetworkVariableGroups.Add(networkDelivery);
|
||||
secondLevelCounter++;
|
||||
}
|
||||
|
||||
if (firstLevelIndex[networkDelivery] >= m_DeliveryMappedNetworkVariableIndices.Count)
|
||||
{
|
||||
m_DeliveryMappedNetworkVariableIndices.Add(new HashSet<int>());
|
||||
}
|
||||
|
||||
m_DeliveryMappedNetworkVariableIndices[firstLevelIndex[networkDelivery]].Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void PreNetworkVariableWrite()
|
||||
{
|
||||
// reset our "which variables got written" data
|
||||
NetworkVariableIndexesToReset.Clear();
|
||||
NetworkVariableIndexesToResetSet.Clear();
|
||||
}
|
||||
|
||||
internal void PostNetworkVariableWrite()
|
||||
{
|
||||
// mark any variables we wrote as no longer dirty
|
||||
for (int i = 0; i < NetworkVariableIndexesToReset.Count; i++)
|
||||
{
|
||||
NetworkVariableFields[NetworkVariableIndexesToReset[i]].ResetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
internal void VariableUpdate(ulong clientId)
|
||||
{
|
||||
if (!m_VarInit)
|
||||
{
|
||||
InitializeVariables();
|
||||
}
|
||||
|
||||
PreNetworkVariableWrite();
|
||||
NetworkVariableUpdate(clientId, NetworkBehaviourId);
|
||||
}
|
||||
|
||||
internal readonly List<int> NetworkVariableIndexesToReset = new List<int>();
|
||||
internal readonly HashSet<int> NetworkVariableIndexesToResetSet = new HashSet<int>();
|
||||
|
||||
private void NetworkVariableUpdate(ulong clientId, int behaviourIndex)
|
||||
{
|
||||
if (!CouldHaveDirtyNetworkVariables())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetworkManager.NetworkConfig.UseSnapshotDelta)
|
||||
{
|
||||
for (int k = 0; k < NetworkVariableFields.Count; k++)
|
||||
{
|
||||
NetworkManager.SnapshotSystem.Store(NetworkObjectId, behaviourIndex, k, NetworkVariableFields[k]);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSend)
|
||||
{
|
||||
var message = new NetworkVariableDeltaMessage
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CouldHaveDirtyNetworkVariables()
|
||||
{
|
||||
// TODO: There should be a better way by reading one dirty variable vs. 'n'
|
||||
for (int i = 0; i < NetworkVariableFields.Count; i++)
|
||||
{
|
||||
if (NetworkVariableFields[i].IsDirty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void MarkVariablesDirty()
|
||||
{
|
||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||
{
|
||||
NetworkVariableFields[j].SetDirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong clientId)
|
||||
{
|
||||
if (NetworkVariableFields.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||
{
|
||||
bool canClientRead = NetworkVariableFields[j].CanClientRead(clientId);
|
||||
|
||||
if (canClientRead)
|
||||
{
|
||||
var writePos = writer.Position;
|
||||
writer.WriteValueSafe((ushort)0);
|
||||
var startPos = writer.Position;
|
||||
NetworkVariableFields[j].WriteField(writer);
|
||||
var size = writer.Position - startPos;
|
||||
writer.Seek(writePos);
|
||||
writer.WriteValueSafe((ushort)size);
|
||||
writer.Seek(startPos + size);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValueSafe((ushort)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetNetworkVariableData(FastBufferReader reader)
|
||||
{
|
||||
if (NetworkVariableFields.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int j = 0; j < NetworkVariableFields.Count; j++)
|
||||
{
|
||||
reader.ReadValueSafe(out ushort varSize);
|
||||
if (varSize == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var readStartPos = reader.Position;
|
||||
NetworkVariableFields[j].ReadField(reader);
|
||||
|
||||
if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
if (reader.Position > (readStartPos + varSize))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"Var data read too far. {reader.Position - (readStartPos + varSize)} bytes.");
|
||||
}
|
||||
|
||||
reader.Seek(readStartPos + varSize);
|
||||
}
|
||||
else if (reader.Position < (readStartPos + varSize))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"Var data read too little. {(readStartPos + varSize) - reader.Position} bytes.");
|
||||
}
|
||||
|
||||
reader.Seek(readStartPos + varSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local instance of a object with a given NetworkId
|
||||
/// </summary>
|
||||
/// <param name="networkId"></param>
|
||||
/// <returns></returns>
|
||||
protected NetworkObject GetNetworkObject(ulong networkId)
|
||||
{
|
||||
return NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkId, out NetworkObject networkObject) ? networkObject : null;
|
||||
}
|
||||
|
||||
public virtual void OnDestroy()
|
||||
{
|
||||
// this seems odd to do here, but in fact especially in tests we can find ourselves
|
||||
// here without having called InitializedVariables, which causes problems if any
|
||||
// of those variables use native containers (e.g. NetworkList) as they won't be
|
||||
// registered here and therefore won't be cleaned up.
|
||||
//
|
||||
// we should study to understand the initialization patterns
|
||||
if (!m_VarInit)
|
||||
{
|
||||
InitializeVariables();
|
||||
}
|
||||
|
||||
for (int i = 0; i < NetworkVariableFields.Count; i++)
|
||||
{
|
||||
NetworkVariableFields[i].Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Core/NetworkBehaviour.cs.meta
Normal file
11
Runtime/Core/NetworkBehaviour.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8ea6ec00590bd44a983c228bcaee727
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
84
Runtime/Core/NetworkBehaviourUpdater.cs
Normal file
84
Runtime/Core/NetworkBehaviourUpdater.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
public class NetworkBehaviourUpdater
|
||||
{
|
||||
private HashSet<NetworkObject> m_Touched = new HashSet<NetworkObject>();
|
||||
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
private ProfilerMarker m_NetworkBehaviourUpdate = new ProfilerMarker($"{nameof(NetworkBehaviour)}.{nameof(NetworkBehaviourUpdate)}");
|
||||
#endif
|
||||
|
||||
internal void NetworkBehaviourUpdate(NetworkManager networkManager)
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
m_NetworkBehaviourUpdate.Begin();
|
||||
#endif
|
||||
try
|
||||
{
|
||||
if (networkManager.IsServer)
|
||||
{
|
||||
m_Touched.Clear();
|
||||
for (int i = 0; i < networkManager.ConnectedClientsList.Count; i++)
|
||||
{
|
||||
var client = networkManager.ConnectedClientsList[i];
|
||||
var spawnedObjs = networkManager.SpawnManager.SpawnedObjectsList;
|
||||
m_Touched.UnionWith(spawnedObjs);
|
||||
foreach (var sobj in spawnedObjs)
|
||||
{
|
||||
if (sobj.IsNetworkVisibleTo(client.ClientId))
|
||||
{
|
||||
// Sync just the variables for just the objects this client sees
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, reset all the no-longer-dirty variables
|
||||
foreach (var sobj in m_Touched)
|
||||
{
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// when client updates the server, it tells it about all its objects
|
||||
foreach (var sobj in networkManager.SpawnManager.SpawnedObjectsList)
|
||||
{
|
||||
if (sobj.IsOwner)
|
||||
{
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, reset all the no-longer-dirty variables
|
||||
foreach (var sobj in networkManager.SpawnManager.SpawnedObjectsList)
|
||||
{
|
||||
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
|
||||
{
|
||||
sobj.ChildNetworkBehaviours[k].PostNetworkVariableWrite();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
||||
m_NetworkBehaviourUpdate.End();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
3
Runtime/Core/NetworkBehaviourUpdater.cs.meta
Normal file
3
Runtime/Core/NetworkBehaviourUpdater.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d084c01093b446878bcb76e5d7f3221e
|
||||
timeCreated: 1622225163
|
||||
1618
Runtime/Core/NetworkManager.cs
Normal file
1618
Runtime/Core/NetworkManager.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Runtime/Core/NetworkManager.cs.meta
Normal file
11
Runtime/Core/NetworkManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 593a2fe42fa9d37498c96f9a383b6521
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1155
Runtime/Core/NetworkObject.cs
Normal file
1155
Runtime/Core/NetworkObject.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Runtime/Core/NetworkObject.cs.meta
Normal file
11
Runtime/Core/NetworkObject.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5a57f767e5e46a458fc5d3c628d0cbb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
417
Runtime/Core/NetworkUpdateLoop.cs
Normal file
417
Runtime/Core/NetworkUpdateLoop.cs
Normal file
@@ -0,0 +1,417 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.LowLevel;
|
||||
using UnityEngine.PlayerLoop;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the required interface of a network update system being executed by the network update loop.
|
||||
/// </summary>
|
||||
public interface INetworkUpdateSystem
|
||||
{
|
||||
void NetworkUpdate(NetworkUpdateStage updateStage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines network update stages being executed by the network update loop.
|
||||
/// </summary>
|
||||
public enum NetworkUpdateStage : byte
|
||||
{
|
||||
Unset = 0, // Default
|
||||
Initialization = 1,
|
||||
EarlyUpdate = 2,
|
||||
FixedUpdate = 3,
|
||||
PreUpdate = 4,
|
||||
Update = 5,
|
||||
PreLateUpdate = 6,
|
||||
PostLateUpdate = 7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the network update loop injected into low-level player loop in Unity.
|
||||
/// </summary>
|
||||
public static class NetworkUpdateLoop
|
||||
{
|
||||
private static Dictionary<NetworkUpdateStage, HashSet<INetworkUpdateSystem>> s_UpdateSystem_Sets;
|
||||
private static Dictionary<NetworkUpdateStage, INetworkUpdateSystem[]> s_UpdateSystem_Arrays;
|
||||
private const int k_UpdateSystem_InitialArrayCapacity = 1024;
|
||||
|
||||
static NetworkUpdateLoop()
|
||||
{
|
||||
s_UpdateSystem_Sets = new Dictionary<NetworkUpdateStage, HashSet<INetworkUpdateSystem>>();
|
||||
s_UpdateSystem_Arrays = new Dictionary<NetworkUpdateStage, INetworkUpdateSystem[]>();
|
||||
|
||||
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
|
||||
{
|
||||
s_UpdateSystem_Sets.Add(updateStage, new HashSet<INetworkUpdateSystem>());
|
||||
s_UpdateSystem_Arrays.Add(updateStage, new INetworkUpdateSystem[k_UpdateSystem_InitialArrayCapacity]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a network update system to be executed in all network update stages.
|
||||
/// </summary>
|
||||
public static void RegisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
|
||||
{
|
||||
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
|
||||
{
|
||||
RegisterNetworkUpdate(updateSystem, updateStage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a network update system to be executed in a specific network update stage.
|
||||
/// </summary>
|
||||
public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
|
||||
{
|
||||
var sysSet = s_UpdateSystem_Sets[updateStage];
|
||||
if (!sysSet.Contains(updateSystem))
|
||||
{
|
||||
sysSet.Add(updateSystem);
|
||||
|
||||
int setLen = sysSet.Count;
|
||||
var sysArr = s_UpdateSystem_Arrays[updateStage];
|
||||
int arrLen = sysArr.Length;
|
||||
|
||||
if (setLen > arrLen)
|
||||
{
|
||||
// double capacity
|
||||
sysArr = s_UpdateSystem_Arrays[updateStage] = new INetworkUpdateSystem[arrLen *= 2];
|
||||
}
|
||||
|
||||
sysSet.CopyTo(sysArr);
|
||||
|
||||
if (setLen < arrLen)
|
||||
{
|
||||
// null terminator
|
||||
sysArr[setLen] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a network update system from all network update stages.
|
||||
/// </summary>
|
||||
public static void UnregisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
|
||||
{
|
||||
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
|
||||
{
|
||||
UnregisterNetworkUpdate(updateSystem, updateStage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a network update system from a specific network update stage.
|
||||
/// </summary>
|
||||
public static void UnregisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
|
||||
{
|
||||
var sysSet = s_UpdateSystem_Sets[updateStage];
|
||||
if (sysSet.Contains(updateSystem))
|
||||
{
|
||||
sysSet.Remove(updateSystem);
|
||||
|
||||
int setLen = sysSet.Count;
|
||||
var sysArr = s_UpdateSystem_Arrays[updateStage];
|
||||
int arrLen = sysArr.Length;
|
||||
|
||||
sysSet.CopyTo(sysArr);
|
||||
|
||||
if (setLen < arrLen)
|
||||
{
|
||||
// null terminator
|
||||
sysArr[setLen] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current network update stage being executed.
|
||||
/// </summary>
|
||||
public static NetworkUpdateStage UpdateStage;
|
||||
|
||||
private static void RunNetworkUpdateStage(NetworkUpdateStage updateStage)
|
||||
{
|
||||
UpdateStage = updateStage;
|
||||
|
||||
var sysArr = s_UpdateSystem_Arrays[updateStage];
|
||||
int arrLen = sysArr.Length;
|
||||
for (int curIdx = 0; curIdx < arrLen; curIdx++)
|
||||
{
|
||||
var curSys = sysArr[curIdx];
|
||||
if (curSys == null)
|
||||
{
|
||||
// null terminator
|
||||
break;
|
||||
}
|
||||
|
||||
curSys.NetworkUpdate(updateStage);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct NetworkInitialization
|
||||
{
|
||||
public static PlayerLoopSystem CreateLoopSystem()
|
||||
{
|
||||
return new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(NetworkInitialization),
|
||||
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.Initialization)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal struct NetworkEarlyUpdate
|
||||
{
|
||||
public static PlayerLoopSystem CreateLoopSystem()
|
||||
{
|
||||
return new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(NetworkEarlyUpdate),
|
||||
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.EarlyUpdate)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal struct NetworkFixedUpdate
|
||||
{
|
||||
public static PlayerLoopSystem CreateLoopSystem()
|
||||
{
|
||||
return new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(NetworkFixedUpdate),
|
||||
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.FixedUpdate)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal struct NetworkPreUpdate
|
||||
{
|
||||
public static PlayerLoopSystem CreateLoopSystem()
|
||||
{
|
||||
return new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(NetworkPreUpdate),
|
||||
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PreUpdate)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal struct NetworkUpdate
|
||||
{
|
||||
public static PlayerLoopSystem CreateLoopSystem()
|
||||
{
|
||||
return new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(NetworkUpdate),
|
||||
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.Update)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal struct NetworkPreLateUpdate
|
||||
{
|
||||
public static PlayerLoopSystem CreateLoopSystem()
|
||||
{
|
||||
return new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(NetworkPreLateUpdate),
|
||||
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PreLateUpdate)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal struct NetworkPostLateUpdate
|
||||
{
|
||||
public static PlayerLoopSystem CreateLoopSystem()
|
||||
{
|
||||
return new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(NetworkPostLateUpdate),
|
||||
updateDelegate = () => RunNetworkUpdateStage(NetworkUpdateStage.PostLateUpdate)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void Initialize()
|
||||
{
|
||||
UnregisterLoopSystems();
|
||||
RegisterLoopSystems();
|
||||
}
|
||||
|
||||
private enum LoopSystemPosition
|
||||
{
|
||||
After,
|
||||
Before
|
||||
}
|
||||
|
||||
private static bool TryAddLoopSystem(ref PlayerLoopSystem parentLoopSystem, PlayerLoopSystem childLoopSystem, Type anchorSystemType, LoopSystemPosition loopSystemPosition)
|
||||
{
|
||||
int systemPosition = -1;
|
||||
if (anchorSystemType != null)
|
||||
{
|
||||
for (int i = 0; i < parentLoopSystem.subSystemList.Length; i++)
|
||||
{
|
||||
var subsystem = parentLoopSystem.subSystemList[i];
|
||||
if (subsystem.type == anchorSystemType)
|
||||
{
|
||||
systemPosition = loopSystemPosition == LoopSystemPosition.After ? i + 1 : i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
systemPosition = loopSystemPosition == LoopSystemPosition.After ? parentLoopSystem.subSystemList.Length : 0;
|
||||
}
|
||||
|
||||
if (systemPosition == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var newSubsystemList = new PlayerLoopSystem[parentLoopSystem.subSystemList.Length + 1];
|
||||
|
||||
// begin = systemsBefore + systemsAfter
|
||||
// + systemsBefore
|
||||
if (systemPosition > 0)
|
||||
{
|
||||
Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition);
|
||||
}
|
||||
// + childSystem
|
||||
newSubsystemList[systemPosition] = childLoopSystem;
|
||||
// + systemsAfter
|
||||
if (systemPosition < parentLoopSystem.subSystemList.Length)
|
||||
{
|
||||
Array.Copy(parentLoopSystem.subSystemList, systemPosition, newSubsystemList, systemPosition + 1, parentLoopSystem.subSystemList.Length - systemPosition);
|
||||
}
|
||||
// end = systemsBefore + childSystem + systemsAfter
|
||||
|
||||
parentLoopSystem.subSystemList = newSubsystemList;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryRemoveLoopSystem(ref PlayerLoopSystem parentLoopSystem, Type childSystemType)
|
||||
{
|
||||
int systemPosition = -1;
|
||||
for (int i = 0; i < parentLoopSystem.subSystemList.Length; i++)
|
||||
{
|
||||
var subsystem = parentLoopSystem.subSystemList[i];
|
||||
if (subsystem.type == childSystemType)
|
||||
{
|
||||
systemPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (systemPosition == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var newSubsystemList = new PlayerLoopSystem[parentLoopSystem.subSystemList.Length - 1];
|
||||
|
||||
// begin = systemsBefore + childSystem + systemsAfter
|
||||
// + systemsBefore
|
||||
if (systemPosition > 0)
|
||||
{
|
||||
Array.Copy(parentLoopSystem.subSystemList, newSubsystemList, systemPosition);
|
||||
}
|
||||
// + systemsAfter
|
||||
if (systemPosition < parentLoopSystem.subSystemList.Length - 1)
|
||||
{
|
||||
Array.Copy(parentLoopSystem.subSystemList, systemPosition + 1, newSubsystemList, systemPosition, parentLoopSystem.subSystemList.Length - systemPosition - 1);
|
||||
}
|
||||
// end = systemsBefore + systemsAfter
|
||||
|
||||
parentLoopSystem.subSystemList = newSubsystemList;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static void RegisterLoopSystems()
|
||||
{
|
||||
var rootPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
|
||||
for (int i = 0; i < rootPlayerLoop.subSystemList.Length; i++)
|
||||
{
|
||||
ref var currentSystem = ref rootPlayerLoop.subSystemList[i];
|
||||
|
||||
if (currentSystem.type == typeof(Initialization))
|
||||
{
|
||||
TryAddLoopSystem(ref currentSystem, NetworkInitialization.CreateLoopSystem(), null, LoopSystemPosition.After);
|
||||
}
|
||||
else if (currentSystem.type == typeof(EarlyUpdate))
|
||||
{
|
||||
TryAddLoopSystem(ref currentSystem, NetworkEarlyUpdate.CreateLoopSystem(), typeof(EarlyUpdate.ScriptRunDelayedStartupFrame), LoopSystemPosition.Before);
|
||||
}
|
||||
else if (currentSystem.type == typeof(FixedUpdate))
|
||||
{
|
||||
TryAddLoopSystem(ref currentSystem, NetworkFixedUpdate.CreateLoopSystem(), typeof(FixedUpdate.ScriptRunBehaviourFixedUpdate), LoopSystemPosition.Before);
|
||||
}
|
||||
else if (currentSystem.type == typeof(PreUpdate))
|
||||
{
|
||||
TryAddLoopSystem(ref currentSystem, NetworkPreUpdate.CreateLoopSystem(), typeof(PreUpdate.PhysicsUpdate), LoopSystemPosition.Before);
|
||||
}
|
||||
else if (currentSystem.type == typeof(Update))
|
||||
{
|
||||
TryAddLoopSystem(ref currentSystem, NetworkUpdate.CreateLoopSystem(), typeof(Update.ScriptRunBehaviourUpdate), LoopSystemPosition.Before);
|
||||
}
|
||||
else if (currentSystem.type == typeof(PreLateUpdate))
|
||||
{
|
||||
TryAddLoopSystem(ref currentSystem, NetworkPreLateUpdate.CreateLoopSystem(), typeof(PreLateUpdate.ScriptRunBehaviourLateUpdate), LoopSystemPosition.Before);
|
||||
}
|
||||
else if (currentSystem.type == typeof(PostLateUpdate))
|
||||
{
|
||||
TryAddLoopSystem(ref currentSystem, NetworkPostLateUpdate.CreateLoopSystem(), typeof(PostLateUpdate.PlayerSendFrameComplete), LoopSystemPosition.After);
|
||||
}
|
||||
}
|
||||
|
||||
PlayerLoop.SetPlayerLoop(rootPlayerLoop);
|
||||
}
|
||||
|
||||
internal static void UnregisterLoopSystems()
|
||||
{
|
||||
var rootPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
|
||||
for (int i = 0; i < rootPlayerLoop.subSystemList.Length; i++)
|
||||
{
|
||||
ref var currentSystem = ref rootPlayerLoop.subSystemList[i];
|
||||
|
||||
if (currentSystem.type == typeof(Initialization))
|
||||
{
|
||||
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkInitialization));
|
||||
}
|
||||
else if (currentSystem.type == typeof(EarlyUpdate))
|
||||
{
|
||||
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkEarlyUpdate));
|
||||
}
|
||||
else if (currentSystem.type == typeof(FixedUpdate))
|
||||
{
|
||||
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkFixedUpdate));
|
||||
}
|
||||
else if (currentSystem.type == typeof(PreUpdate))
|
||||
{
|
||||
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPreUpdate));
|
||||
}
|
||||
else if (currentSystem.type == typeof(Update))
|
||||
{
|
||||
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkUpdate));
|
||||
}
|
||||
else if (currentSystem.type == typeof(PreLateUpdate))
|
||||
{
|
||||
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPreLateUpdate));
|
||||
}
|
||||
else if (currentSystem.type == typeof(PostLateUpdate))
|
||||
{
|
||||
TryRemoveLoopSystem(ref currentSystem, typeof(NetworkPostLateUpdate));
|
||||
}
|
||||
}
|
||||
|
||||
PlayerLoop.SetPlayerLoop(rootPlayerLoop);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Core/NetworkUpdateLoop.cs.meta
Normal file
11
Runtime/Core/NetworkUpdateLoop.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0cd9c24b9acfd4e82a71c795f37235c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
93
Runtime/Core/SnapshotRTT.cs
Normal file
93
Runtime/Core/SnapshotRTT.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Core/SnapshotRTT.cs.meta
Normal file
11
Runtime/Core/SnapshotRTT.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69c3c1c5a885d4aed99ee2e1fa40f763
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1062
Runtime/Core/SnapshotSystem.cs
Normal file
1062
Runtime/Core/SnapshotSystem.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Runtime/Core/SnapshotSystem.cs.meta
Normal file
11
Runtime/Core/SnapshotSystem.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c275febadb27c4d18b41218e3353b84b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Exceptions.meta
Normal file
8
Runtime/Exceptions.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2643fec0e8509fd409e5aceeba931323
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Runtime/Exceptions/InvalidParentException.cs
Normal file
14
Runtime/Exceptions/InvalidParentException.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown when the new parent candidate of the NetworkObject is not valid
|
||||
/// </summary>
|
||||
public class InvalidParentException : Exception
|
||||
{
|
||||
public InvalidParentException() { }
|
||||
public InvalidParentException(string message) : base(message) { }
|
||||
public InvalidParentException(string message, Exception innerException) : base(message, innerException) { }
|
||||
}
|
||||
}
|
||||
11
Runtime/Exceptions/InvalidParentException.cs.meta
Normal file
11
Runtime/Exceptions/InvalidParentException.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abe1766318991412cb1aba96c4dbcc4e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Runtime/Exceptions/NetworkConfigurationException.cs
Normal file
28
Runtime/Exceptions/NetworkConfigurationException.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown when a change to a configuration is wrong
|
||||
/// </summary>
|
||||
public class NetworkConfigurationException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a NetworkConfigurationException
|
||||
/// </summary>
|
||||
public NetworkConfigurationException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a NetworkConfigurationException with a message
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
public NetworkConfigurationException(string message) : base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a NetworkConfigurationException with a message and a inner exception
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
/// <param name="inner">The inner exception</param>
|
||||
public NetworkConfigurationException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
}
|
||||
11
Runtime/Exceptions/NetworkConfigurationException.cs.meta
Normal file
11
Runtime/Exceptions/NetworkConfigurationException.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f50fae9dd20afbd438766cc330b188a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Runtime/Exceptions/NotListeningException.cs
Normal file
28
Runtime/Exceptions/NotListeningException.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown when the operation require NetworkManager to be listening.
|
||||
/// </summary>
|
||||
public class NotListeningException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a NotListeningException
|
||||
/// </summary>
|
||||
public NotListeningException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a NotListeningException with a message
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
public NotListeningException(string message) : base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a NotListeningException with a message and a inner exception
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
/// <param name="inner">The inner exception</param>
|
||||
public NotListeningException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
}
|
||||
11
Runtime/Exceptions/NotListeningException.cs.meta
Normal file
11
Runtime/Exceptions/NotListeningException.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc58b328162929140aeb42e6a4a9354b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Runtime/Exceptions/NotServerException.cs
Normal file
28
Runtime/Exceptions/NotServerException.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown when the operation can only be done on the server
|
||||
/// </summary>
|
||||
public class NotServerException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a NotServerException
|
||||
/// </summary>
|
||||
public NotServerException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a NotServerException with a message
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
public NotServerException(string message) : base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a NotServerException with a message and a inner exception
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
/// <param name="inner">The inner exception</param>
|
||||
public NotServerException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
}
|
||||
11
Runtime/Exceptions/NotServerException.cs.meta
Normal file
11
Runtime/Exceptions/NotServerException.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 751e55ce1418ecd4e9b08fef9d8e9591
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
33
Runtime/Exceptions/SpawnStateException.cs
Normal file
33
Runtime/Exceptions/SpawnStateException.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown when an object is not yet spawned
|
||||
/// </summary>
|
||||
public class SpawnStateException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a SpawnStateException
|
||||
/// </summary>
|
||||
public SpawnStateException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a SpawnStateException with a message
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
public SpawnStateException(string message) : base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a SpawnStateException with a message and a inner exception
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
/// <param name="inner">The inner exception</param>
|
||||
public SpawnStateException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
|
||||
public class InvalidChannelException : Exception
|
||||
{
|
||||
public InvalidChannelException(string message) : base(message) { }
|
||||
}
|
||||
}
|
||||
11
Runtime/Exceptions/SpawnStateException.cs.meta
Normal file
11
Runtime/Exceptions/SpawnStateException.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70c2a851ae78c6e49b34c7e4ca46305f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Runtime/Exceptions/VisibilityChangeException.cs
Normal file
28
Runtime/Exceptions/VisibilityChangeException.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown when a visibility change fails
|
||||
/// </summary>
|
||||
public class VisibilityChangeException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a VisibilityChangeException
|
||||
/// </summary>
|
||||
public VisibilityChangeException() { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a VisibilityChangeException with a message
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
public VisibilityChangeException(string message) : base(message) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a VisibilityChangeException with a message and a inner exception
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message</param>
|
||||
/// <param name="inner">The inner exception</param>
|
||||
public VisibilityChangeException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
}
|
||||
11
Runtime/Exceptions/VisibilityChangeException.cs.meta
Normal file
11
Runtime/Exceptions/VisibilityChangeException.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85d754bbea7d74a48918ebe05833e7ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Hashing.meta
Normal file
8
Runtime/Hashing.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0b5218305f20f9419d00c4ab2adabef
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Hashing/XXHash.meta
Normal file
8
Runtime/Hashing/XXHash.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c61e8fe9a68a486fbbc3128d233ded2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Runtime/Hashing/XXHash/LICENSE
Normal file
21
Runtime/Hashing/XXHash/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015, 2016 Sedat Kapanoglu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
7
Runtime/Hashing/XXHash/LICENSE.meta
Normal file
7
Runtime/Hashing/XXHash/LICENSE.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf89ecbf6f9954c8ea6d0848b1e79d87
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
318
Runtime/Hashing/XXHash/XXHash.cs
Normal file
318
Runtime/Hashing/XXHash/XXHash.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
// <copyright file="XXHash.cs" company="Sedat Kapanoglu">
|
||||
// Copyright (c) 2015-2019 Sedat Kapanoglu
|
||||
// MIT License (see LICENSE file for details)
|
||||
// </copyright>
|
||||
|
||||
// @mfatihmar (Unity): Modified for Unity support
|
||||
|
||||
using System.Text;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// XXHash implementation.
|
||||
/// </summary>
|
||||
internal static class XXHash
|
||||
{
|
||||
private const ulong k_Prime64v1 = 11400714785074694791ul;
|
||||
private const ulong k_Prime64v2 = 14029467366897019727ul;
|
||||
private const ulong k_Prime64v3 = 1609587929392839161ul;
|
||||
private const ulong k_Prime64v4 = 9650029242287828579ul;
|
||||
private const ulong k_Prime64v5 = 2870177450012600261ul;
|
||||
|
||||
private const uint k_Prime32v1 = 2654435761u;
|
||||
private const uint k_Prime32v2 = 2246822519u;
|
||||
private const uint k_Prime32v3 = 3266489917u;
|
||||
private const uint k_Prime32v4 = 668265263u;
|
||||
private const uint k_Prime32v5 = 374761393u;
|
||||
|
||||
public static uint Hash32(string text) => Hash32(text, Encoding.UTF8);
|
||||
public static uint Hash32(string text, Encoding encoding) => Hash32(encoding.GetBytes(text));
|
||||
public static uint Hash32(byte[] buffer)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = buffer)
|
||||
{
|
||||
return Hash32(ptr, buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a 32-bit xxHash value.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Input buffer.</param>
|
||||
/// <param name="bufferLength">Input buffer length.</param>
|
||||
/// <param name="seed">Optional seed.</param>
|
||||
/// <returns>32-bit hash value.</returns>
|
||||
public static unsafe uint Hash32(byte* buffer, int bufferLength, uint seed = 0)
|
||||
{
|
||||
const int stripeLength = 16;
|
||||
|
||||
int len = bufferLength;
|
||||
int remainingLen = len;
|
||||
uint acc;
|
||||
|
||||
byte* pInput = buffer;
|
||||
if (len >= stripeLength)
|
||||
{
|
||||
uint acc1 = seed + k_Prime32v1 + k_Prime32v2;
|
||||
uint acc2 = seed + k_Prime32v2;
|
||||
uint acc3 = seed;
|
||||
uint acc4 = seed - k_Prime32v1;
|
||||
|
||||
do
|
||||
{
|
||||
acc = processStripe32(ref pInput, ref acc1, ref acc2, ref acc3, ref acc4);
|
||||
remainingLen -= stripeLength;
|
||||
} while (remainingLen >= stripeLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
acc = seed + k_Prime32v5;
|
||||
}
|
||||
|
||||
acc += (uint)len;
|
||||
acc = processRemaining32(pInput, acc, remainingLen);
|
||||
|
||||
return avalanche32(acc);
|
||||
}
|
||||
|
||||
public static ulong Hash64(string text) => Hash64(text, Encoding.UTF8);
|
||||
public static ulong Hash64(string text, Encoding encoding) => Hash64(encoding.GetBytes(text));
|
||||
public static ulong Hash64(byte[] buffer)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = buffer)
|
||||
{
|
||||
return Hash64(ptr, buffer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a 64-bit xxHash value.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Input buffer.</param>
|
||||
/// <param name="bufferLength">Input buffer length.</param>
|
||||
/// <param name="seed">Optional seed.</param>
|
||||
/// <returns>Computed 64-bit hash value.</returns>
|
||||
public static unsafe ulong Hash64(byte* buffer, int bufferLength, ulong seed = 0)
|
||||
{
|
||||
const int stripeLength = 32;
|
||||
|
||||
int len = bufferLength;
|
||||
int remainingLen = len;
|
||||
ulong acc;
|
||||
|
||||
byte* pInput = buffer;
|
||||
if (len >= stripeLength)
|
||||
{
|
||||
ulong acc1 = seed + k_Prime64v1 + k_Prime64v2;
|
||||
ulong acc2 = seed + k_Prime64v2;
|
||||
ulong acc3 = seed;
|
||||
ulong acc4 = seed - k_Prime64v1;
|
||||
|
||||
do
|
||||
{
|
||||
acc = processStripe64(ref pInput, ref acc1, ref acc2, ref acc3, ref acc4);
|
||||
remainingLen -= stripeLength;
|
||||
} while (remainingLen >= stripeLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
acc = seed + k_Prime64v5;
|
||||
}
|
||||
|
||||
acc += (ulong)len;
|
||||
acc = processRemaining64(pInput, acc, remainingLen);
|
||||
|
||||
|
||||
return avalanche64(acc);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong processStripe64(
|
||||
ref byte* pInput,
|
||||
ref ulong acc1,
|
||||
ref ulong acc2,
|
||||
ref ulong acc3,
|
||||
ref ulong acc4)
|
||||
{
|
||||
processLane64(ref acc1, ref pInput);
|
||||
processLane64(ref acc2, ref pInput);
|
||||
processLane64(ref acc3, ref pInput);
|
||||
processLane64(ref acc4, ref pInput);
|
||||
|
||||
ulong acc = Bits.RotateLeft(acc1, 1)
|
||||
+ Bits.RotateLeft(acc2, 7)
|
||||
+ Bits.RotateLeft(acc3, 12)
|
||||
+ Bits.RotateLeft(acc4, 18);
|
||||
|
||||
mergeAccumulator64(ref acc, acc1);
|
||||
mergeAccumulator64(ref acc, acc2);
|
||||
mergeAccumulator64(ref acc, acc3);
|
||||
mergeAccumulator64(ref acc, acc4);
|
||||
return acc;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe void processLane64(ref ulong accn, ref byte* pInput)
|
||||
{
|
||||
ulong lane = *(ulong*)pInput;
|
||||
accn = round64(accn, lane);
|
||||
pInput += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe ulong processRemaining64(
|
||||
byte* pInput,
|
||||
ulong acc,
|
||||
int remainingLen)
|
||||
{
|
||||
for (ulong lane; remainingLen >= 8; remainingLen -= 8, pInput += 8)
|
||||
{
|
||||
lane = *(ulong*)pInput;
|
||||
|
||||
acc ^= round64(0, lane);
|
||||
acc = Bits.RotateLeft(acc, 27) * k_Prime64v1;
|
||||
acc += k_Prime64v4;
|
||||
}
|
||||
|
||||
for (uint lane32; remainingLen >= 4; remainingLen -= 4, pInput += 4)
|
||||
{
|
||||
lane32 = *(uint*)pInput;
|
||||
|
||||
acc ^= lane32 * k_Prime64v1;
|
||||
acc = Bits.RotateLeft(acc, 23) * k_Prime64v2;
|
||||
acc += k_Prime64v3;
|
||||
}
|
||||
|
||||
for (byte lane8; remainingLen >= 1; remainingLen--, pInput++)
|
||||
{
|
||||
lane8 = *pInput;
|
||||
acc ^= lane8 * k_Prime64v5;
|
||||
acc = Bits.RotateLeft(acc, 11) * k_Prime64v1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong avalanche64(ulong acc)
|
||||
{
|
||||
acc ^= acc >> 33;
|
||||
acc *= k_Prime64v2;
|
||||
acc ^= acc >> 29;
|
||||
acc *= k_Prime64v3;
|
||||
acc ^= acc >> 32;
|
||||
return acc;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong round64(ulong accn, ulong lane)
|
||||
{
|
||||
accn += lane * k_Prime64v2;
|
||||
return Bits.RotateLeft(accn, 31) * k_Prime64v1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void mergeAccumulator64(ref ulong acc, ulong accn)
|
||||
{
|
||||
acc ^= round64(0, accn);
|
||||
acc *= k_Prime64v1;
|
||||
acc += k_Prime64v4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe uint processStripe32(
|
||||
ref byte* pInput,
|
||||
ref uint acc1,
|
||||
ref uint acc2,
|
||||
ref uint acc3,
|
||||
ref uint acc4)
|
||||
{
|
||||
processLane32(ref pInput, ref acc1);
|
||||
processLane32(ref pInput, ref acc2);
|
||||
processLane32(ref pInput, ref acc3);
|
||||
processLane32(ref pInput, ref acc4);
|
||||
|
||||
return Bits.RotateLeft(acc1, 1)
|
||||
+ Bits.RotateLeft(acc2, 7)
|
||||
+ Bits.RotateLeft(acc3, 12)
|
||||
+ Bits.RotateLeft(acc4, 18);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe void processLane32(ref byte* pInput, ref uint accn)
|
||||
{
|
||||
uint lane = *(uint*)pInput;
|
||||
accn = round32(accn, lane);
|
||||
pInput += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe uint processRemaining32(
|
||||
byte* pInput,
|
||||
uint acc,
|
||||
int remainingLen)
|
||||
{
|
||||
for (uint lane; remainingLen >= 4; remainingLen -= 4, pInput += 4)
|
||||
{
|
||||
lane = *(uint*)pInput;
|
||||
acc += lane * k_Prime32v3;
|
||||
acc = Bits.RotateLeft(acc, 17) * k_Prime32v4;
|
||||
}
|
||||
|
||||
for (byte lane; remainingLen >= 1; remainingLen--, pInput++)
|
||||
{
|
||||
lane = *pInput;
|
||||
acc += lane * k_Prime32v5;
|
||||
acc = Bits.RotateLeft(acc, 11) * k_Prime32v1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint round32(uint accn, uint lane)
|
||||
{
|
||||
accn += lane * k_Prime32v2;
|
||||
accn = Bits.RotateLeft(accn, 13);
|
||||
accn *= k_Prime32v1;
|
||||
return accn;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static uint avalanche32(uint acc)
|
||||
{
|
||||
acc ^= acc >> 15;
|
||||
acc *= k_Prime32v2;
|
||||
acc ^= acc >> 13;
|
||||
acc *= k_Prime32v3;
|
||||
acc ^= acc >> 16;
|
||||
return acc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bit operations.
|
||||
/// </summary>
|
||||
private static class Bits
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static ulong RotateLeft(ulong value, int bits)
|
||||
{
|
||||
return (value << bits) | (value >> (64 - bits));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static uint RotateLeft(uint value, int bits)
|
||||
{
|
||||
return (value << bits) | (value >> (32 - bits));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Hashing/XXHash/XXHash.cs.meta
Normal file
11
Runtime/Hashing/XXHash/XXHash.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5aa7a49e9e694f148d810d34577546b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Logging.meta
Normal file
8
Runtime/Logging.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb2e0b0239cebdd44b534f09be21f1e6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Runtime/Logging/LogLevel.cs
Normal file
28
Runtime/Logging/LogLevel.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Log level
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Developer logging level, most verbose
|
||||
/// </summary>
|
||||
Developer,
|
||||
|
||||
/// <summary>
|
||||
/// Normal logging level, medium verbose
|
||||
/// </summary>
|
||||
Normal,
|
||||
|
||||
/// <summary>
|
||||
/// Error logging level, very quiet
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// Nothing logging level, no logging will be done
|
||||
/// </summary>
|
||||
Nothing
|
||||
}
|
||||
}
|
||||
11
Runtime/Logging/LogLevel.cs.meta
Normal file
11
Runtime/Logging/LogLevel.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b97845a27cb50d44da22aae51800abfe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
84
Runtime/Logging/NetworkLog.cs
Normal file
84
Runtime/Logging/NetworkLog.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for logging
|
||||
/// </summary>
|
||||
public static class NetworkLog
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current log level.
|
||||
/// </summary>
|
||||
/// <value>The current log level.</value>
|
||||
public static LogLevel CurrentLogLevel => NetworkManager.Singleton == null ? LogLevel.Normal : NetworkManager.Singleton.LogLevel;
|
||||
|
||||
// internal logging
|
||||
internal static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
|
||||
internal static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
|
||||
internal static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
|
||||
|
||||
/// <summary>
|
||||
/// Logs an info log locally and on the server if possible.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log</param>
|
||||
public static void LogInfoServer(string message) => LogServer(message, LogType.Info);
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning log locally and on the server if possible.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log</param>
|
||||
public static void LogWarningServer(string message) => LogServer(message, LogType.Warning);
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error log locally and on the server if possible.
|
||||
/// </summary>
|
||||
/// <param name="message">The message to log</param>
|
||||
public static void LogErrorServer(string message) => LogServer(message, LogType.Error);
|
||||
|
||||
private static void LogServer(string message, LogType logType)
|
||||
{
|
||||
// Get the sender of the local log
|
||||
ulong localId = NetworkManager.Singleton != null ? NetworkManager.Singleton.LocalClientId : 0;
|
||||
|
||||
switch (logType)
|
||||
{
|
||||
case LogType.Info:
|
||||
LogInfoServerLocal(message, localId);
|
||||
break;
|
||||
case LogType.Warning:
|
||||
LogWarningServerLocal(message, localId);
|
||||
break;
|
||||
case LogType.Error:
|
||||
LogErrorServerLocal(message, localId);
|
||||
break;
|
||||
}
|
||||
|
||||
if (NetworkManager.Singleton != null && !NetworkManager.Singleton.IsServer && NetworkManager.Singleton.NetworkConfig.EnableNetworkLogs)
|
||||
{
|
||||
|
||||
var networkMessage = new ServerLogMessage
|
||||
{
|
||||
LogType = logType,
|
||||
Message = message
|
||||
};
|
||||
var size = NetworkManager.Singleton.SendMessage(networkMessage, NetworkDelivery.ReliableFragmentedSequenced,
|
||||
NetworkManager.Singleton.ServerClientId);
|
||||
|
||||
NetworkManager.Singleton.NetworkMetrics.TrackServerLogSent(NetworkManager.Singleton.ServerClientId, (uint)logType, size);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogInfoServerLocal(string message, ulong sender) => Debug.Log($"[Netcode-Server Sender={sender}] {message}");
|
||||
internal static void LogWarningServerLocal(string message, ulong sender) => Debug.LogWarning($"[Netcode-Server Sender={sender}] {message}");
|
||||
internal static void LogErrorServerLocal(string message, ulong sender) => Debug.LogError($"[Netcode-Server Sender={sender}] {message}");
|
||||
|
||||
internal enum LogType : byte
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Logging/NetworkLog.cs.meta
Normal file
11
Runtime/Logging/NetworkLog.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b33d7f8ddec3b140a4ff09659174e8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Messaging.meta
Normal file
8
Runtime/Messaging.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 563c514253a555b48a68707decc5c088
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
Runtime/Messaging/BatchHeader.cs
Normal file
13
Runtime/Messaging/BatchHeader.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Header placed at the start of each message batch
|
||||
/// </summary>
|
||||
internal struct BatchHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Total number of messages in the batch.
|
||||
/// </summary>
|
||||
public ushort BatchSize;
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/BatchHeader.cs.meta
Normal file
11
Runtime/Messaging/BatchHeader.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 941fcfe2222f8734ab5bfb9bc4787717
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
281
Runtime/Messaging/CustomMessageManager.cs
Normal file
281
Runtime/Messaging/CustomMessageManager.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// The manager class to manage custom messages, note that this is different from the NetworkManager custom messages.
|
||||
/// These are named and are much easier to use.
|
||||
/// </summary>
|
||||
public class CustomMessagingManager
|
||||
{
|
||||
private readonly NetworkManager m_NetworkManager;
|
||||
|
||||
internal CustomMessagingManager(NetworkManager networkManager)
|
||||
{
|
||||
m_NetworkManager = networkManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate used for incoming unnamed messages
|
||||
/// </summary>
|
||||
/// <param name="clientId">The clientId that sent the message</param>
|
||||
/// <param name="reader">The stream containing the message data</param>
|
||||
public delegate void UnnamedMessageDelegate(ulong clientId, FastBufferReader reader);
|
||||
|
||||
/// <summary>
|
||||
/// Event invoked when unnamed messages arrive
|
||||
/// </summary>
|
||||
public event UnnamedMessageDelegate OnUnnamedMessage;
|
||||
|
||||
internal void InvokeUnnamedMessage(ulong clientId, FastBufferReader reader)
|
||||
{
|
||||
if (OnUnnamedMessage != null)
|
||||
{
|
||||
var pos = reader.Position;
|
||||
var delegates = OnUnnamedMessage.GetInvocationList();
|
||||
foreach (var handler in delegates)
|
||||
{
|
||||
reader.Seek(pos);
|
||||
((UnnamedMessageDelegate)handler).Invoke(clientId, reader);
|
||||
}
|
||||
}
|
||||
m_NetworkManager.NetworkMetrics.TrackUnnamedMessageReceived(clientId, reader.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends unnamed message to all clients
|
||||
/// </summary>
|
||||
/// <param name="messageBuffer">The message stream containing the data</param>
|
||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||
public void SendUnnamedMessageToAll(FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
||||
{
|
||||
SendUnnamedMessage(m_NetworkManager.ConnectedClientsIds, messageBuffer, networkDelivery);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends unnamed message to a list of clients
|
||||
/// </summary>
|
||||
/// <param name="clientIds">The clients to send to, sends to everyone if null</param>
|
||||
/// <param name="messageBuffer">The message stream containing the data</param>
|
||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||
public void SendUnnamedMessage(IReadOnlyList<ulong> clientIds, FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
||||
{
|
||||
if (!m_NetworkManager.IsServer)
|
||||
{
|
||||
throw new InvalidOperationException("Can not send unnamed messages to multiple users as a client");
|
||||
}
|
||||
|
||||
if (clientIds == null)
|
||||
{
|
||||
throw new ArgumentNullException("You must pass in a valid clientId List");
|
||||
}
|
||||
|
||||
var message = new UnnamedMessage
|
||||
{
|
||||
Data = messageBuffer
|
||||
};
|
||||
var size = m_NetworkManager.SendMessage(message, networkDelivery, clientIds);
|
||||
|
||||
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
||||
if (size != 0)
|
||||
{
|
||||
m_NetworkManager.NetworkMetrics.TrackUnnamedMessageSent(clientIds, size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a unnamed message to a specific client
|
||||
/// </summary>
|
||||
/// <param name="clientId">The client to send the message to</param>
|
||||
/// <param name="messageBuffer">The message stream containing the data</param>
|
||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||
public void SendUnnamedMessage(ulong clientId, FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
||||
{
|
||||
var message = new UnnamedMessage
|
||||
{
|
||||
Data = messageBuffer
|
||||
};
|
||||
var size = m_NetworkManager.SendMessage(message, networkDelivery, clientId);
|
||||
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
||||
if (size != 0)
|
||||
{
|
||||
m_NetworkManager.NetworkMetrics.TrackUnnamedMessageSent(clientId, size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate used to handle named messages
|
||||
/// </summary>
|
||||
public delegate void HandleNamedMessageDelegate(ulong senderClientId, FastBufferReader messagePayload);
|
||||
|
||||
private Dictionary<ulong, HandleNamedMessageDelegate> m_NamedMessageHandlers32 = new Dictionary<ulong, HandleNamedMessageDelegate>();
|
||||
private Dictionary<ulong, HandleNamedMessageDelegate> m_NamedMessageHandlers64 = new Dictionary<ulong, HandleNamedMessageDelegate>();
|
||||
|
||||
private Dictionary<ulong, string> m_MessageHandlerNameLookup32 = new Dictionary<ulong, string>();
|
||||
private Dictionary<ulong, string> m_MessageHandlerNameLookup64 = new Dictionary<ulong, string>();
|
||||
|
||||
internal void InvokeNamedMessage(ulong hash, ulong sender, FastBufferReader reader)
|
||||
{
|
||||
var bytesCount = reader.Length;
|
||||
|
||||
if (m_NetworkManager == null)
|
||||
{
|
||||
// We dont know what size to use. Try every (more collision prone)
|
||||
if (m_NamedMessageHandlers32.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler32))
|
||||
{
|
||||
messageHandler32(sender, reader);
|
||||
m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup32[hash], bytesCount);
|
||||
}
|
||||
|
||||
if (m_NamedMessageHandlers64.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler64))
|
||||
{
|
||||
messageHandler64(sender, reader);
|
||||
m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup64[hash], bytesCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only check the right size.
|
||||
switch (m_NetworkManager.NetworkConfig.RpcHashSize)
|
||||
{
|
||||
case HashSize.VarIntFourBytes:
|
||||
if (m_NamedMessageHandlers32.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler32))
|
||||
{
|
||||
messageHandler32(sender, reader);
|
||||
m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup32[hash], bytesCount);
|
||||
}
|
||||
break;
|
||||
case HashSize.VarIntEightBytes:
|
||||
if (m_NamedMessageHandlers64.TryGetValue(hash, out HandleNamedMessageDelegate messageHandler64))
|
||||
{
|
||||
messageHandler64(sender, reader);
|
||||
m_NetworkManager.NetworkMetrics.TrackNamedMessageReceived(sender, m_MessageHandlerNameLookup64[hash], bytesCount);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a named message handler delegate.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the message.</param>
|
||||
/// <param name="callback">The callback to run when a named message is received.</param>
|
||||
public void RegisterNamedMessageHandler(string name, HandleNamedMessageDelegate callback)
|
||||
{
|
||||
var hash32 = XXHash.Hash32(name);
|
||||
var hash64 = XXHash.Hash64(name);
|
||||
|
||||
m_NamedMessageHandlers32[hash32] = callback;
|
||||
m_NamedMessageHandlers64[hash64] = callback;
|
||||
|
||||
m_MessageHandlerNameLookup32[hash32] = name;
|
||||
m_MessageHandlerNameLookup64[hash64] = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a named message handler.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the message.</param>
|
||||
public void UnregisterNamedMessageHandler(string name)
|
||||
{
|
||||
var hash32 = XXHash.Hash32(name);
|
||||
var hash64 = XXHash.Hash64(name);
|
||||
|
||||
m_NamedMessageHandlers32.Remove(hash32);
|
||||
m_NamedMessageHandlers64.Remove(hash64);
|
||||
|
||||
m_MessageHandlerNameLookup32.Remove(hash32);
|
||||
m_MessageHandlerNameLookup64.Remove(hash64);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a named message to all clients
|
||||
/// </summary>
|
||||
/// <param name="messageStream">The message stream containing the data</param>
|
||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||
public void SendNamedMessageToAll(string messageName, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
||||
{
|
||||
SendNamedMessage(messageName, m_NetworkManager.ConnectedClientsIds, messageStream, networkDelivery);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a named message
|
||||
/// </summary>
|
||||
/// <param name="messageName">The message name to send</param>
|
||||
/// <param name="clientId">The client to send the message to</param>
|
||||
/// <param name="messageStream">The message stream containing the data</param>
|
||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||
public void SendNamedMessage(string messageName, ulong clientId, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
||||
{
|
||||
ulong hash = 0;
|
||||
switch (m_NetworkManager.NetworkConfig.RpcHashSize)
|
||||
{
|
||||
case HashSize.VarIntFourBytes:
|
||||
hash = XXHash.Hash32(messageName);
|
||||
break;
|
||||
case HashSize.VarIntEightBytes:
|
||||
hash = XXHash.Hash64(messageName);
|
||||
break;
|
||||
}
|
||||
|
||||
var message = new NamedMessage
|
||||
{
|
||||
Hash = hash,
|
||||
Data = messageStream
|
||||
};
|
||||
var size = m_NetworkManager.SendMessage(message, networkDelivery, clientId);
|
||||
|
||||
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
||||
if (size != 0)
|
||||
{
|
||||
m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientId, messageName, size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the named message
|
||||
/// </summary>
|
||||
/// <param name="messageName">The message name to send</param>
|
||||
/// <param name="clientIds">The clients to send to, sends to everyone if null</param>
|
||||
/// <param name="messageStream">The message stream containing the data</param>
|
||||
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
|
||||
public void SendNamedMessage(string messageName, IReadOnlyList<ulong> clientIds, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
|
||||
{
|
||||
if (!m_NetworkManager.IsServer)
|
||||
{
|
||||
throw new InvalidOperationException("Can not send unnamed messages to multiple users as a client");
|
||||
}
|
||||
|
||||
if (clientIds == null)
|
||||
{
|
||||
throw new ArgumentNullException("You must pass in a valid clientId List");
|
||||
}
|
||||
|
||||
ulong hash = 0;
|
||||
switch (m_NetworkManager.NetworkConfig.RpcHashSize)
|
||||
{
|
||||
case HashSize.VarIntFourBytes:
|
||||
hash = XXHash.Hash32(messageName);
|
||||
break;
|
||||
case HashSize.VarIntEightBytes:
|
||||
hash = XXHash.Hash64(messageName);
|
||||
break;
|
||||
}
|
||||
var message = new NamedMessage
|
||||
{
|
||||
Hash = hash,
|
||||
Data = messageStream
|
||||
};
|
||||
var size = m_NetworkManager.SendMessage(message, networkDelivery, clientIds);
|
||||
|
||||
// Size is zero if we were only sending the message to ourself in which case it isn't sent.
|
||||
if (size != 0)
|
||||
{
|
||||
m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientIds, messageName, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/CustomMessageManager.cs.meta
Normal file
11
Runtime/Messaging/CustomMessageManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15c647dad40a44d46886dca112cfd524
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
Runtime/Messaging/ILPPMessageProvider.cs
Normal file
17
Runtime/Messaging/ILPPMessageProvider.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct ILPPMessageProvider : IMessageProvider
|
||||
{
|
||||
#pragma warning disable IDE1006 // disable naming rule violation check
|
||||
// This is NOT modified by RuntimeAccessModifiersILPP right now, but is populated by ILPP.
|
||||
internal static readonly List<MessagingSystem.MessageWithHandler> __network_message_types = new List<MessagingSystem.MessageWithHandler>();
|
||||
#pragma warning restore IDE1006 // restore naming rule violation check
|
||||
|
||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||
{
|
||||
return __network_message_types;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/ILPPMessageProvider.cs.meta
Normal file
11
Runtime/Messaging/ILPPMessageProvider.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5bfc2b13811eb5429a0eef59dd88b71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Runtime/Messaging/IMessageProvider.cs
Normal file
9
Runtime/Messaging/IMessageProvider.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal interface IMessageProvider
|
||||
{
|
||||
List<MessagingSystem.MessageWithHandler> GetMessages();
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/IMessageProvider.cs.meta
Normal file
11
Runtime/Messaging/IMessageProvider.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ed41def88d712c4b9c115310e6b0127
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Runtime/Messaging/IMessageSender.cs
Normal file
7
Runtime/Messaging/IMessageSender.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal interface IMessageSender
|
||||
{
|
||||
void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData);
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/IMessageSender.cs.meta
Normal file
11
Runtime/Messaging/IMessageSender.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15b54cd88eba22648ade4240523b8c65
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
97
Runtime/Messaging/INetworkHooks.cs
Normal file
97
Runtime/Messaging/INetworkHooks.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to react to different events in the messaging system. Primary use case is for
|
||||
/// collecting profiling data and metrics data. Additionally, it provides OnVerifyCanSend and OnVerifyCanReceive
|
||||
/// to allow for networking implementations to put limits on when certain messages can or can't be sent or received.
|
||||
/// </summary>
|
||||
internal interface INetworkHooks
|
||||
{
|
||||
/// <summary>
|
||||
/// Called before an individual message is sent.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The destination clientId</param>
|
||||
/// <param name="messageType">The type of the message being sent</param>
|
||||
/// <param name="delivery"></param>
|
||||
void OnBeforeSendMessage(ulong clientId, Type messageType, NetworkDelivery delivery);
|
||||
|
||||
/// <summary>
|
||||
/// Called after an individual message is sent.
|
||||
/// </summary>
|
||||
/// <param name="clientId">The destination clientId</param>
|
||||
/// <param name="messageType">The type of the message being sent</param>
|
||||
/// <param name="delivery"></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);
|
||||
|
||||
/// <summary>
|
||||
/// Called before an individual message is received.
|
||||
/// </summary>
|
||||
/// <param name="senderId">The source clientId</param>
|
||||
/// <param name="messageType">The type of the message being sent</param>
|
||||
/// <param name="messageSizeBytes">Number of bytes in the message, not including the message header</param>
|
||||
void OnBeforeReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes);
|
||||
|
||||
/// <summary>
|
||||
/// Called after an individual message is received.
|
||||
/// </summary>
|
||||
/// <param name="senderId">The source clientId</param>
|
||||
/// <param name="messageType">The type of the message being sent</param>
|
||||
/// <param name="messageSizeBytes">Number of bytes in the message, not including the message header</param>
|
||||
void OnAfterReceiveMessage(ulong senderId, Type messageType, int messageSizeBytes);
|
||||
|
||||
/// <summary>
|
||||
/// Called before a batch of messages is sent
|
||||
/// </summary>
|
||||
/// <param name="clientId">The destination clientId</param>
|
||||
/// <param name="messageCount">Number of messages in the batch</param>
|
||||
/// <param name="batchSizeInBytes">Number of bytes in the batch, including the batch header</param>
|
||||
/// <param name="delivery"></param>
|
||||
void OnBeforeSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery);
|
||||
|
||||
/// <summary>
|
||||
/// Called after a batch of messages is sent
|
||||
/// </summary>
|
||||
/// <param name="clientId">The destination clientId</param>
|
||||
/// <param name="messageCount">Number of messages in the batch</param>
|
||||
/// <param name="batchSizeInBytes">Number of bytes in the batch, including the batch header</param>
|
||||
/// <param name="delivery"></param>
|
||||
void OnAfterSendBatch(ulong clientId, int messageCount, int batchSizeInBytes, NetworkDelivery delivery);
|
||||
|
||||
/// <summary>
|
||||
/// Called before a batch of messages is received
|
||||
/// </summary>
|
||||
/// <param name="senderId">The source clientId</param>
|
||||
/// <param name="messageCount">Number of messages in the batch</param>
|
||||
/// <param name="batchSizeInBytes">Number of bytes in the batch, including the batch header</param>
|
||||
void OnBeforeReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes);
|
||||
|
||||
/// <summary>
|
||||
/// Called after a batch of messages is received
|
||||
/// </summary>
|
||||
/// <param name="senderId">The source clientId</param>
|
||||
/// <param name="messageCount">Number of messages in the batch</param>
|
||||
/// <param name="batchSizeInBytes">Number of bytes in the batch, including the batch header</param>
|
||||
void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeInBytes);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called before a message is sent. If this returns false, the message will be discarded.
|
||||
/// </summary>
|
||||
/// <param name="destinationId">The destination clientId</param>
|
||||
/// <param name="messageType">The type of the message</param>
|
||||
/// <param name="delivery"></param>
|
||||
/// <returns></returns>
|
||||
bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery);
|
||||
|
||||
/// <summary>
|
||||
/// Called before a message is received. If this returns false, the message will be discarded.
|
||||
/// </summary>
|
||||
/// <param name="senderId">The source clientId</param>
|
||||
/// <param name="messageType">The type of the message</param>
|
||||
/// <returns></returns>
|
||||
bool OnVerifyCanReceive(ulong senderId, Type messageType);
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/INetworkHooks.cs.meta
Normal file
11
Runtime/Messaging/INetworkHooks.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b199c5a160beabb47bd6b0e4f06cfcd2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
49
Runtime/Messaging/INetworkMessage.cs
Normal file
49
Runtime/Messaging/INetworkMessage.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Base building block for creating a message. Any struct (or class) that implements INetworkMessage
|
||||
/// will automatically be found by the system and all the proper mechanisms for sending and receiving
|
||||
/// that message will be hooked up automatically.
|
||||
///
|
||||
/// It's generally recommended to implement INetworkMessage types as structs, and define your messages
|
||||
/// as close as you can to the network transport format. For messages with no dynamic-length or optional
|
||||
/// data, FastBufferWriter allows for serializing the entire struct at once via writer.WriteValue(this)
|
||||
///
|
||||
/// In addition to the specified Serialize method, all INetworkMessage types must also have a
|
||||
/// static message handler for receiving messages of the following name and signature:
|
||||
///
|
||||
/// <code>
|
||||
/// public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
/// </code>
|
||||
///
|
||||
/// It is the responsibility of the Serialize and Receive methods to ensure there is enough buffer space
|
||||
/// to perform the serialization/deserialization, either via <see cref="FastBufferWriter.TryBeginWrite"/> and
|
||||
/// <see cref="FastBufferReader.TryBeginRead"/>, or via <see cref="FastBufferWriter.WriteValueSafe{T}(T)"/> and
|
||||
/// <see cref="FastBufferReader.ReadValueSafe{T}(T)"/>. The former is more efficient when it can be used
|
||||
/// for bounds checking for multiple values at once.
|
||||
///
|
||||
/// When bandwidth is a bigger concern than CPU usage, values can be packed with <see cref="BytePacker"/>
|
||||
/// and <see cref="ByteUnpacker"/>.
|
||||
///
|
||||
/// Note that for messages sent using non-fragmenting delivery modes (anything other than
|
||||
/// <see cref="NetworkDelivery.ReliableFragmentedSequenced"/>), there is a hard limit of 1300 bytes per message.
|
||||
/// With the fragmenting delivery mode, the limit is 64000 bytes per message. If your messages exceed that limit,
|
||||
/// you will have to split them into multiple smaller messages.
|
||||
///
|
||||
/// Messages are sent with:
|
||||
/// <see cref="NetworkManager.SendMessage{T}(T, NetworkDelivery, ulong, bool)"/>
|
||||
/// <see cref="NetworkManager.SendMessage{T}(T, NetworkDelivery, ulong*, int, bool)"/>
|
||||
/// <see cref="NetworkManager.SendMessage{T, U}(T, NetworkDelivery, U, bool)"/>
|
||||
/// <see cref="NetworkManager.SendMessage{T}(T, NetworkDelivery, NativeArray{ulong}, bool)"/>
|
||||
/// </summary>
|
||||
internal interface INetworkMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to serialize the message.
|
||||
/// </summary>
|
||||
/// <param name="writer"></param>
|
||||
void Serialize(FastBufferWriter writer);
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/INetworkMessage.cs.meta
Normal file
11
Runtime/Messaging/INetworkMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16079de8d8821a24c91db930bc892b5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Runtime/Messaging/MessageHeader.cs
Normal file
21
Runtime/Messaging/MessageHeader.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the header data that's serialized to the network when sending an <see cref="INetworkMessage"/>
|
||||
/// </summary>
|
||||
internal struct MessageHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// The byte representation of the message type. This is automatically assigned to each message
|
||||
/// by the MessagingSystem. This value is deterministic only so long as the list of messages remains
|
||||
/// unchanged - if new messages are added or messages are removed, MessageType assignments may be
|
||||
/// calculated differently.
|
||||
/// </summary>
|
||||
public byte MessageType;
|
||||
|
||||
/// <summary>
|
||||
/// The total size of the message, NOT including the header.
|
||||
/// </summary>
|
||||
public ushort MessageSize;
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/MessageHeader.cs.meta
Normal file
11
Runtime/Messaging/MessageHeader.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74fa727ddec342c48ab49156a32b977b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Runtime/Messaging/Messages.meta
Normal file
8
Runtime/Messaging/Messages.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd834639d7f09614fa4f3296921871d8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
49
Runtime/Messaging/Messages/ChangeOwnershipMessage.cs
Normal file
49
Runtime/Messaging/Messages/ChangeOwnershipMessage.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct ChangeOwnershipMessage : INetworkMessage
|
||||
{
|
||||
public ulong NetworkObjectId;
|
||||
public ulong OwnerClientId;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
writer.WriteValueSafe(this);
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
if (!networkManager.IsClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
reader.ReadValueSafe(out ChangeOwnershipMessage message);
|
||||
message.Handle(reader, context, context.SenderId, networkManager, reader.Length);
|
||||
}
|
||||
|
||||
public void Handle(FastBufferReader reader, in NetworkContext context, ulong senderId, NetworkManager networkManager, int messageSize)
|
||||
{
|
||||
if (!networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject))
|
||||
{
|
||||
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (networkObject.OwnerClientId == networkManager.LocalClientId)
|
||||
{
|
||||
//We are current owner.
|
||||
networkObject.InvokeBehaviourOnLostOwnership();
|
||||
}
|
||||
|
||||
networkObject.OwnerClientId = OwnerClientId;
|
||||
|
||||
if (OwnerClientId == networkManager.LocalClientId)
|
||||
{
|
||||
//We are new owner.
|
||||
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||
}
|
||||
|
||||
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(senderId, networkObject, messageSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/ChangeOwnershipMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/ChangeOwnershipMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 841becdc46b20d5408a81bc30ac950f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
94
Runtime/Messaging/Messages/ConnectionApprovedMessage.cs
Normal file
94
Runtime/Messaging/Messages/ConnectionApprovedMessage.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct ConnectionApprovedMessage : INetworkMessage
|
||||
{
|
||||
public ulong OwnerClientId;
|
||||
public int NetworkTick;
|
||||
public int SceneObjectCount;
|
||||
|
||||
// Not serialized, held as references to serialize NetworkVariable data
|
||||
public HashSet<NetworkObject> SpawnedObjectsList;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
if (!writer.TryBeginWrite(sizeof(ulong) + sizeof(int) + sizeof(int)))
|
||||
{
|
||||
throw new OverflowException(
|
||||
$"Not enough space in the write buffer to serialize {nameof(ConnectionApprovedMessage)}");
|
||||
}
|
||||
writer.WriteValue(OwnerClientId);
|
||||
writer.WriteValue(NetworkTick);
|
||||
writer.WriteValue(SceneObjectCount);
|
||||
|
||||
if (SceneObjectCount != 0)
|
||||
{
|
||||
// Serialize NetworkVariable data
|
||||
foreach (var sobj in SpawnedObjectsList)
|
||||
{
|
||||
if (sobj.CheckObjectVisibility == null || sobj.CheckObjectVisibility(OwnerClientId))
|
||||
{
|
||||
sobj.Observers.Add(OwnerClientId);
|
||||
var sceneObject = sobj.GetMessageSceneObject(OwnerClientId);
|
||||
sceneObject.Serialize(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
if (!networkManager.IsClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reader.TryBeginRead(sizeof(ulong) + sizeof(int) + sizeof(int)))
|
||||
{
|
||||
throw new OverflowException(
|
||||
$"Not enough space in the buffer to read {nameof(ConnectionApprovedMessage)}");
|
||||
}
|
||||
|
||||
var message = new ConnectionApprovedMessage();
|
||||
reader.ReadValue(out message.OwnerClientId);
|
||||
reader.ReadValue(out message.NetworkTick);
|
||||
reader.ReadValue(out message.SceneObjectCount);
|
||||
message.Handle(reader, context.SenderId, networkManager);
|
||||
}
|
||||
|
||||
public void Handle(FastBufferReader reader, ulong clientId, NetworkManager networkManager)
|
||||
{
|
||||
networkManager.LocalClientId = OwnerClientId;
|
||||
networkManager.NetworkMetrics.SetConnectionId(networkManager.LocalClientId);
|
||||
|
||||
var time = new NetworkTime(networkManager.NetworkTickSystem.TickRate, NetworkTick);
|
||||
networkManager.NetworkTimeSystem.Reset(time.Time, 0.15f); // Start with a constant RTT of 150 until we receive values from the transport.
|
||||
networkManager.NetworkTickSystem.Reset(networkManager.NetworkTimeSystem.LocalTime, networkManager.NetworkTimeSystem.ServerTime);
|
||||
|
||||
networkManager.LocalClient = new NetworkClient() { ClientId = networkManager.LocalClientId };
|
||||
|
||||
// Only if scene management is disabled do we handle NetworkObject synchronization at this point
|
||||
if (!networkManager.NetworkConfig.EnableSceneManagement)
|
||||
{
|
||||
networkManager.SpawnManager.DestroySceneObjects();
|
||||
|
||||
// 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.
|
||||
for (ushort i = 0; i < SceneObjectCount; i++)
|
||||
{
|
||||
var sceneObject = new NetworkObject.SceneObject();
|
||||
sceneObject.Deserialize(reader);
|
||||
NetworkObject.AddSceneObject(sceneObject, reader, networkManager);
|
||||
}
|
||||
|
||||
// Mark the client being connected
|
||||
networkManager.IsConnectedClient = true;
|
||||
// When scene management is disabled we notify after everything is synchronized
|
||||
networkManager.InvokeOnClientConnectedCallback(clientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/ConnectionApprovedMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/ConnectionApprovedMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebbc74ce01b073340aa445f3bd59ff62
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
115
Runtime/Messaging/Messages/ConnectionRequestMessage.cs
Normal file
115
Runtime/Messaging/Messages/ConnectionRequestMessage.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct ConnectionRequestMessage : INetworkMessage
|
||||
{
|
||||
public ulong ConfigHash;
|
||||
|
||||
public byte[] ConnectionData;
|
||||
|
||||
public bool ShouldSendConnectionData;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
if (ShouldSendConnectionData)
|
||||
{
|
||||
writer.WriteValueSafe(ConfigHash);
|
||||
writer.WriteValueSafe(ConnectionData);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValueSafe(ConfigHash);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
if (!networkManager.IsServer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var message = new ConnectionRequestMessage();
|
||||
if (networkManager.NetworkConfig.ConnectionApproval)
|
||||
{
|
||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.ConfigHash) +
|
||||
FastBufferWriter.GetWriteSize<int>()))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"Incomplete connection request message given config - possible {nameof(NetworkConfig)} mismatch.");
|
||||
}
|
||||
|
||||
networkManager.DisconnectClient(context.SenderId);
|
||||
return;
|
||||
}
|
||||
reader.ReadValue(out message.ConfigHash);
|
||||
|
||||
if (!networkManager.NetworkConfig.CompareConfig(message.ConfigHash))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"{nameof(NetworkConfig)} mismatch. The configuration between the server and client does not match");
|
||||
}
|
||||
|
||||
networkManager.DisconnectClient(context.SenderId);
|
||||
return;
|
||||
}
|
||||
|
||||
reader.ReadValueSafe(out message.ConnectionData);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!reader.TryBeginRead(FastBufferWriter.GetWriteSize(message.ConfigHash)))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"Incomplete connection request message.");
|
||||
}
|
||||
|
||||
networkManager.DisconnectClient(context.SenderId);
|
||||
return;
|
||||
}
|
||||
reader.ReadValue(out message.ConfigHash);
|
||||
|
||||
if (!networkManager.NetworkConfig.CompareConfig(message.ConfigHash))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"{nameof(NetworkConfig)} mismatch. The configuration between the server and client does not match");
|
||||
}
|
||||
|
||||
networkManager.DisconnectClient(context.SenderId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
message.Handle(networkManager, context.SenderId);
|
||||
}
|
||||
|
||||
public void Handle(NetworkManager networkManager, ulong senderId)
|
||||
{
|
||||
if (networkManager.PendingClients.TryGetValue(senderId, out PendingClient client))
|
||||
{
|
||||
// Set to pending approval to prevent future connection requests from being approved
|
||||
client.ConnectionState = PendingClient.State.PendingApproval;
|
||||
}
|
||||
|
||||
if (networkManager.NetworkConfig.ConnectionApproval)
|
||||
{
|
||||
// Note: Delegate creation allocates.
|
||||
// Note: ToArray() also allocates. :(
|
||||
networkManager.InvokeConnectionApproval(ConnectionData, senderId,
|
||||
(createPlayerObject, playerPrefabHash, approved, position, rotation) =>
|
||||
{
|
||||
var localCreatePlayerObject = createPlayerObject;
|
||||
networkManager.HandleApproval(senderId, localCreatePlayerObject, playerPrefabHash, approved,
|
||||
position, rotation);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
networkManager.HandleApproval(senderId, networkManager.NetworkConfig.PlayerPrefab != null, null, true, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/ConnectionRequestMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/ConnectionRequestMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd160468676e06049ad81dcfb22c3dc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
30
Runtime/Messaging/Messages/CreateObjectMessage.cs
Normal file
30
Runtime/Messaging/Messages/CreateObjectMessage.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct CreateObjectMessage : INetworkMessage
|
||||
{
|
||||
public NetworkObject.SceneObject ObjectInfo;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
ObjectInfo.Serialize(writer);
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
if (!networkManager.IsClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var message = new CreateObjectMessage();
|
||||
message.ObjectInfo.Deserialize(reader);
|
||||
message.Handle(context.SenderId, reader, networkManager);
|
||||
}
|
||||
|
||||
public void Handle(ulong senderId, FastBufferReader reader, NetworkManager networkManager)
|
||||
{
|
||||
var networkObject = NetworkObject.AddSceneObject(ObjectInfo, reader, networkManager);
|
||||
networkManager.NetworkMetrics.TrackObjectSpawnReceived(senderId, networkObject, reader.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/CreateObjectMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/CreateObjectMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff075de988adf5a4294620aa2d85fcd0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
41
Runtime/Messaging/Messages/DestroyObjectMessage.cs
Normal file
41
Runtime/Messaging/Messages/DestroyObjectMessage.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct DestroyObjectMessage : INetworkMessage
|
||||
{
|
||||
public ulong NetworkObjectId;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
writer.WriteValueSafe(this);
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
if (!networkManager.IsClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
reader.ReadValueSafe(out DestroyObjectMessage message);
|
||||
message.Handle(context.SenderId, networkManager, reader.Length);
|
||||
}
|
||||
|
||||
public void Handle(ulong senderId, NetworkManager networkManager, int messageSize)
|
||||
{
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
|
||||
networkManager.NetworkMetrics.TrackObjectDestroyReceived(senderId, networkObject, messageSize);
|
||||
networkManager.SpawnManager.OnDespawnObject(networkObject, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/DestroyObjectMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/DestroyObjectMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18473ed11c97e7241aecb0c72d4bffb2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Runtime/Messaging/Messages/NamedMessage.cs
Normal file
22
Runtime/Messaging/Messages/NamedMessage.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct NamedMessage : INetworkMessage
|
||||
{
|
||||
public ulong Hash;
|
||||
public FastBufferWriter Data;
|
||||
|
||||
public unsafe void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
writer.WriteValueSafe(Hash);
|
||||
writer.WriteBytesSafe(Data.GetUnsafePtr(), Data.Length);
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
var message = new NamedMessage();
|
||||
reader.ReadValueSafe(out message.Hash);
|
||||
|
||||
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeNamedMessage(message.Hash, context.SenderId, reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/NamedMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/NamedMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b6bcd41dcce65743ba387fc4e2c6fa8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
223
Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs
Normal file
223
Runtime/Messaging/Messages/NetworkVariableDeltaMessage.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// This particular struct is a little weird because it doesn't actually contain the data
|
||||
/// it's serializing. Instead, it contains references to the data it needs to do the
|
||||
/// serialization. This is due to the generally amorphous nature of network variable
|
||||
/// deltas, since they're all driven by custom virtual method overloads.
|
||||
/// </summary>
|
||||
internal struct NetworkVariableDeltaMessage : INetworkMessage
|
||||
{
|
||||
public ulong NetworkObjectId;
|
||||
public ushort NetworkBehaviourIndex;
|
||||
|
||||
public HashSet<int> DeliveryMappedNetworkVariableIndex;
|
||||
public ulong ClientId;
|
||||
public NetworkBehaviour NetworkBehaviour;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) +
|
||||
FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
|
||||
{
|
||||
throw new OverflowException(
|
||||
$"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
||||
}
|
||||
writer.WriteValue(NetworkObjectId);
|
||||
writer.WriteValue(NetworkBehaviourIndex);
|
||||
for (int k = 0; k < NetworkBehaviour.NetworkVariableFields.Count; k++)
|
||||
{
|
||||
if (!DeliveryMappedNetworkVariableIndex.Contains(k))
|
||||
{
|
||||
// This var does not belong to the currently iterating delivery group.
|
||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
writer.WriteValueSafe((short)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValueSafe(false);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// if I'm dirty AND a client, write (server always has all permissions)
|
||||
// if I'm dirty AND the server AND the client can read me, send.
|
||||
bool shouldWrite = NetworkBehaviour.NetworkVariableFields[k].ShouldWrite(ClientId, NetworkBehaviour.NetworkManager.IsServer);
|
||||
|
||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
if (!shouldWrite)
|
||||
{
|
||||
writer.WriteValueSafe((ushort)0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValueSafe(shouldWrite);
|
||||
}
|
||||
|
||||
if (shouldWrite)
|
||||
{
|
||||
if (NetworkBehaviour.NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
var tmpWriter = new FastBufferWriter(MessagingSystem.NON_FRAGMENTED_MESSAGE_MAX_SIZE, Allocator.Temp, short.MaxValue);
|
||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(tmpWriter);
|
||||
|
||||
writer.WriteValueSafe((ushort)tmpWriter.Length);
|
||||
tmpWriter.CopyTo(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkBehaviour.NetworkVariableFields[k].WriteDelta(writer);
|
||||
}
|
||||
|
||||
if (!NetworkBehaviour.NetworkVariableIndexesToResetSet.Contains(k))
|
||||
{
|
||||
NetworkBehaviour.NetworkVariableIndexesToResetSet.Add(k);
|
||||
NetworkBehaviour.NetworkVariableIndexesToReset.Add(k);
|
||||
}
|
||||
|
||||
NetworkBehaviour.NetworkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
||||
ClientId,
|
||||
NetworkBehaviour.NetworkObject,
|
||||
NetworkBehaviour.NetworkVariableFields[k].Name,
|
||||
NetworkBehaviour.__getTypeName(),
|
||||
writer.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
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))
|
||||
{
|
||||
NetworkBehaviour behaviour = networkObject.GetNetworkBehaviourAtOrderIndex(NetworkBehaviourIndex);
|
||||
|
||||
if (behaviour == null)
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"Network variable delta message received for a non-existent behaviour. {nameof(NetworkObjectId)}: {NetworkObjectId}, {nameof(NetworkBehaviourIndex)}: {NetworkBehaviourIndex}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < behaviour.NetworkVariableFields.Count; i++)
|
||||
{
|
||||
ushort varSize = 0;
|
||||
|
||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
reader.ReadValueSafe(out varSize);
|
||||
|
||||
if (varSize == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.ReadValueSafe(out bool deltaExists);
|
||||
if (!deltaExists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (networkManager.IsServer)
|
||||
{
|
||||
// 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 (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(behaviour)} - VariableIndex: {i}");
|
||||
NetworkLog.LogError($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
||||
}
|
||||
|
||||
reader.Seek(reader.Position + varSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
//This client wrote somewhere they are not allowed. This is critical
|
||||
//We can't just skip this field. Because we don't actually know how to dummy read
|
||||
//That is, we don't know how many bytes to skip. Because the interface doesn't have a
|
||||
//Read that gives us the value. Only a Read that applies the value straight away
|
||||
//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.
|
||||
// - TwoTen
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
||||
{
|
||||
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($"[{behaviour.NetworkVariableFields[i].GetType().Name}]");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
int readStartPos = reader.Position;
|
||||
|
||||
behaviour.NetworkVariableFields[i].ReadDelta(reader, networkManager.IsServer);
|
||||
|
||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
||||
senderId,
|
||||
networkObject,
|
||||
behaviour.NetworkVariableFields[i].Name,
|
||||
behaviour.__getTypeName(),
|
||||
reader.Length);
|
||||
|
||||
|
||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
if (reader.Position > (readStartPos + varSize))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning(
|
||||
$"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);
|
||||
}
|
||||
else if (reader.Position < (readStartPos + varSize))
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogWarning(
|
||||
$"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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfa1a454cc9fdb647ba89479fa6b8299
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
67
Runtime/Messaging/Messages/ParentSyncMessage.cs
Normal file
67
Runtime/Messaging/Messages/ParentSyncMessage.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct ParentSyncMessage : INetworkMessage
|
||||
{
|
||||
public ulong NetworkObjectId;
|
||||
|
||||
public bool IsReparented;
|
||||
|
||||
//If(Metadata.IsReparented)
|
||||
public bool IsLatestParentSet;
|
||||
|
||||
//If(IsLatestParentSet)
|
||||
public ulong? LatestParent;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
writer.WriteValueSafe(NetworkObjectId);
|
||||
writer.WriteValueSafe(IsReparented);
|
||||
if (IsReparented)
|
||||
{
|
||||
writer.WriteValueSafe(IsLatestParentSet);
|
||||
if (IsLatestParentSet)
|
||||
{
|
||||
writer.WriteValueSafe((ulong)LatestParent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
if (!networkManager.IsClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var message = new ParentSyncMessage();
|
||||
reader.ReadValueSafe(out message.NetworkObjectId);
|
||||
reader.ReadValueSafe(out message.IsReparented);
|
||||
if (message.IsReparented)
|
||||
{
|
||||
reader.ReadValueSafe(out message.IsLatestParentSet);
|
||||
if (message.IsLatestParentSet)
|
||||
{
|
||||
reader.ReadValueSafe(out ulong latestParent);
|
||||
message.LatestParent = latestParent;
|
||||
}
|
||||
}
|
||||
|
||||
message.Handle(reader, context, networkManager);
|
||||
}
|
||||
|
||||
public void Handle(FastBufferReader reader, in NetworkContext context, NetworkManager networkManager)
|
||||
{
|
||||
if (networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
|
||||
{
|
||||
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
||||
networkObject.SetNetworkParenting(IsReparented, LatestParent);
|
||||
networkObject.ApplyNetworkParenting();
|
||||
}
|
||||
else
|
||||
{
|
||||
networkManager.SpawnManager.TriggerOnSpawn(NetworkObjectId, reader, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/ParentSyncMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/ParentSyncMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 237bfa46868f1ff48863f3a6df2f5506
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
109
Runtime/Messaging/Messages/RpcMessage.cs
Normal file
109
Runtime/Messaging/Messages/RpcMessage.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/RpcMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/RpcMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8fc9f8cca6a18b428460b62bce2d8f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
Runtime/Messaging/Messages/SceneEventMessage.cs
Normal file
19
Runtime/Messaging/Messages/SceneEventMessage.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
// Todo: Would be lovely to get this one nicely formatted with all the data it sends in the struct
|
||||
// like most of the other messages when we have some more time and can come back and refactor this.
|
||||
internal struct SceneEventMessage : INetworkMessage
|
||||
{
|
||||
public SceneEventData EventData;
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
EventData.Serialize(writer);
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
((NetworkManager)context.SystemOwner).SceneManager.HandleSceneEvent(context.SenderId, reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/SceneEventMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/SceneEventMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 152f6ccef0320cf4abcf19099c1997e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
50
Runtime/Messaging/Messages/ServerLogMessage.cs
Normal file
50
Runtime/Messaging/Messages/ServerLogMessage.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct ServerLogMessage : INetworkMessage
|
||||
{
|
||||
public NetworkLog.LogType LogType;
|
||||
// It'd be lovely to be able to replace this with FixedString or NativeArray...
|
||||
// But it's not really practical. On the sending side, the user is likely to want
|
||||
// to work with strings and would need to convert, and on the receiving side,
|
||||
// we'd have to convert it to a string to be able to pass it to the log system.
|
||||
// So an allocation is unavoidable here on both sides.
|
||||
public string Message;
|
||||
|
||||
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
writer.WriteValueSafe(LogType);
|
||||
BytePacker.WriteValuePacked(writer, Message);
|
||||
}
|
||||
|
||||
public static void Receive(FastBufferReader reader, in NetworkContext context)
|
||||
{
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
if (networkManager.IsServer && networkManager.NetworkConfig.EnableNetworkLogs)
|
||||
{
|
||||
var message = new ServerLogMessage();
|
||||
reader.ReadValueSafe(out message.LogType);
|
||||
ByteUnpacker.ReadValuePacked(reader, out message.Message);
|
||||
message.Handle(context.SenderId, networkManager, reader.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(ulong senderId, NetworkManager networkManager, int messageSize)
|
||||
{
|
||||
networkManager.NetworkMetrics.TrackServerLogReceived(senderId, (uint)LogType, messageSize);
|
||||
|
||||
switch (LogType)
|
||||
{
|
||||
case NetworkLog.LogType.Info:
|
||||
NetworkLog.LogInfoServerLocal(Message, senderId);
|
||||
break;
|
||||
case NetworkLog.LogType.Warning:
|
||||
NetworkLog.LogWarningServerLocal(Message, senderId);
|
||||
break;
|
||||
case NetworkLog.LogType.Error:
|
||||
NetworkLog.LogErrorServerLocal(Message, senderId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Runtime/Messaging/Messages/ServerLogMessage.cs.meta
Normal file
11
Runtime/Messaging/Messages/ServerLogMessage.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d1c8aae1f7b7194eb3ab1cab260f34f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user