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:
Unity Technologies
2020-12-20 00:00:00 +00:00
commit 22d877d1b2
489 changed files with 43246 additions and 0 deletions

8
Tests/Editor.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d656bc8bb2502584ab6883d254d64782
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,31 @@
using NUnit.Framework;
namespace Unity.Netcode.EditorTests
{
public class ArithmeticTests
{
[Test]
public void TestCeil()
{
Assert.That(Arithmetic.CeilingExact(10, 5), Is.EqualTo(2));
Assert.That(Arithmetic.CeilingExact(11, 5), Is.EqualTo(3));
Assert.That(Arithmetic.CeilingExact(0, 5), Is.EqualTo(0));
Assert.That(Arithmetic.CeilingExact(1, 5), Is.EqualTo(1));
Assert.That(Arithmetic.CeilingExact(2, 5), Is.EqualTo(1));
Assert.That(Arithmetic.CeilingExact(3, 5), Is.EqualTo(1));
Assert.That(Arithmetic.CeilingExact(4, 5), Is.EqualTo(1));
Assert.That(Arithmetic.CeilingExact(5, 5), Is.EqualTo(1));
Assert.That(Arithmetic.CeilingExact(6, 5), Is.EqualTo(2));
}
[Test]
public void TestZigZag()
{
Assert.That(Arithmetic.ZigZagDecode(Arithmetic.ZigZagEncode(1234)), Is.EqualTo(1234));
Assert.That(Arithmetic.ZigZagDecode(Arithmetic.ZigZagEncode(-1)), Is.EqualTo(-1));
Assert.That(Arithmetic.ZigZagDecode(Arithmetic.ZigZagEncode(0)), Is.EqualTo(0));
Assert.That(Arithmetic.ZigZagDecode(Arithmetic.ZigZagEncode(long.MaxValue)), Is.EqualTo(long.MaxValue));
Assert.That(Arithmetic.ZigZagDecode(Arithmetic.ZigZagEncode(long.MinValue)), Is.EqualTo(long.MinValue));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cc0dc1cbf78a5486db1ccbc245d90992
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Tests/Editor/Build.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2a46bbc7e63044f498612ed996afa274
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,371 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_LightingSettings: {fileID: 0}
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &1290761662
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1290761665}
- component: {fileID: 1290761664}
- component: {fileID: 1290761663}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &1290761663
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1290761662}
m_Enabled: 1
--- !u!20 &1290761664
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1290761662}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &1290761665
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1290761662}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1858588911
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1858588913}
- component: {fileID: 1858588912}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!108 &1858588912
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858588911}
m_Enabled: 1
serializedVersion: 10
m_Type: 1
m_Shape: 0
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
m_Intensity: 1
m_Range: 10
m_SpotAngle: 30
m_InnerSpotAngle: 21.80208
m_CookieSize: 10
m_Shadows:
m_Type: 2
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 6570
m_UseColorTemperature: 0
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ShadowRadius: 0
m_ShadowAngle: 0
--- !u!4 &1858588913
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1858588911}
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!1 &1896907459
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1896907460}
- component: {fileID: 1896907461}
m_Layer: 0
m_Name: '[NetworkManager]'
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1896907460
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1896907459}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1896907461
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1896907459}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3}
m_Name:
m_EditorClassIdentifier:
DontDestroy: 1
RunInBackground: 1
LogLevel: 1
NetworkConfig:
ProtocolVersion: 0
NetworkTransport: {fileID: 0}
RegisteredScenes:
- BuildTestScene
AllowRuntimeSceneChanges: 0
PlayerPrefab: {fileID: 0}
NetworkPrefabs: []
TickRate: 30
ClientConnectionBufferTimeout: 10
ConnectionApproval: 0
ConnectionData:
EnableTimeResync: 0
TimeResyncInterval: 30
EnableNetworkVariable: 1
EnsureNetworkVariableLengthSafety: 0
EnableSceneManagement: 1
ForceSamePrefabs: 1
RecycleNetworkIds: 1
NetworkIdRecycleDelay: 120
RpcHashSize: 0
LoadSceneTimeOut: 120
MessageBufferTimeout: 20
EnableNetworkLogs: 1

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d45c09a54b1da466ab6c385ee45fd3e4
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,28 @@
using System.IO;
using System.Reflection;
using NUnit.Framework;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
public class BuildTests
{
public const string DefaultBuildScenePath = "Tests/Editor/Build/BuildTestScene.unity";
[Test]
public void BasicBuildTest()
{
var execAssembly = Assembly.GetExecutingAssembly();
var packagePath = UnityEditor.PackageManager.PackageInfo.FindForAssembly(execAssembly).assetPath;
var buildReport = BuildPipeline.BuildPlayer(
new[] { Path.Combine(packagePath, DefaultBuildScenePath) },
Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", nameof(BuildTests)),
EditorUserBuildSettings.activeBuildTarget,
BuildOptions.None
);
Assert.AreEqual(BuildResult.Succeeded, buildReport.summary.result);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: efb549c2173d5478e93a71fbd05c0392
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,116 @@
using NUnit.Framework;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
public class FixedAllocatorTest
{
[Test]
public void SimpleTest()
{
int pos;
var allocator = new IndexAllocator(20000, 200);
allocator.DebugDisplay();
// allocate 20 bytes
Assert.IsTrue(allocator.Allocate(0, 20, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// can't ask for negative amount of memory
Assert.IsFalse(allocator.Allocate(1, -20, out pos));
Assert.IsTrue(allocator.Verify());
// can't ask for deallocation of negative index
Assert.IsFalse(allocator.Deallocate(-1));
Assert.IsTrue(allocator.Verify());
// can't ask for the same index twice
Assert.IsFalse(allocator.Allocate(0, 20, out pos));
Assert.IsTrue(allocator.Verify());
// allocate another 20 bytes
Assert.IsTrue(allocator.Allocate(1, 20, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate a third 20 bytes
Assert.IsTrue(allocator.Allocate(2, 20, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// deallocate 0
Assert.IsTrue(allocator.Deallocate(0));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// deallocate 1
allocator.Deallocate(1);
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// deallocate 2
allocator.Deallocate(2);
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate 50 bytes
Assert.IsTrue(allocator.Allocate(0, 50, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate another 50 bytes
Assert.IsTrue(allocator.Allocate(1, 50, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate a third 50 bytes
Assert.IsTrue(allocator.Allocate(2, 50, out pos));
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// deallocate 1, a block in the middle this time
allocator.Deallocate(1);
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
// allocate a smaller one in its place
allocator.Allocate(1, 25, out pos);
allocator.DebugDisplay();
Assert.IsTrue(allocator.Verify());
}
[Test]
public void ReuseTest()
{
int count = 100;
bool[] used = new bool[count];
int[] pos = new int[count];
int iterations = 10000;
var allocator = new IndexAllocator(20000, 200);
for (int i = 0; i < iterations; i++)
{
int index = Random.Range(0, count);
if (used[index])
{
Assert.IsTrue(allocator.Deallocate(index));
used[index] = false;
}
else
{
int position;
int length = 10 * Random.Range(1, 10);
Assert.IsTrue(allocator.Allocate(index, length, out position));
pos[index] = position;
used[index] = true;
}
Assert.IsTrue(allocator.Verify());
}
allocator.DebugDisplay();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 85ac488e1432d49668c711fa625a0743
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,359 @@
using System;
using NUnit.Framework;
namespace Unity.Netcode.EditorTests
{
public class InterpolatorTests
{
private const float k_Precision = 0.00000001f;
private const int k_MockTickRate = 1;
private NetworkTime T(float time, uint tickRate = k_MockTickRate)
{
return new NetworkTime(tickRate, timeSec: time);
}
[Test]
public void TestReset()
{
var interpolator = new BufferedLinearInterpolatorFloat();
var serverTime = new NetworkTime(k_MockTickRate, 100f);
interpolator.AddMeasurement(5, 1.0f);
var initVal = interpolator.Update(10f, serverTime.Time, serverTime.TimeTicksAgo(1).Time); // big value
Assert.That(initVal, Is.EqualTo(5f));
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(5f));
interpolator.ResetTo(100f, serverTime.Time);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(100f));
var val = interpolator.Update(1f, serverTime.Time, serverTime.TimeTicksAgo(1).Time);
Assert.That(val, Is.EqualTo(100f));
}
[Test]
public void NormalUsage()
{
// Testing float instead of Vector3. The only difference with Vector3 is the lerp method used.
var interpolator = new BufferedLinearInterpolatorFloat();
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(0f));
interpolator.AddMeasurement(0f, 1.0f);
interpolator.AddMeasurement(1f, 2.0f);
// too small update, nothing happens, doesn't consume from buffer yet
var serverTime = new NetworkTime(k_MockTickRate, 0.01d); // t = 0.1d
interpolator.Update(.01f, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(0f));
// consume first measurement, still can't interpolate with just one tick consumed
serverTime += 1.0d; // t = 1.01
interpolator.Update(1.0f, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(0f));
// consume second measurement, start to interpolate
serverTime += 1.0d; // t = 2.01
var valueFromUpdate = interpolator.Update(1.0f, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(0.01f).Within(k_Precision));
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(0.01f).Within(k_Precision)); // test a second time, to make sure the get doesn't update the value
Assert.That(valueFromUpdate, Is.EqualTo(interpolator.GetInterpolatedValue()).Within(k_Precision));
// continue interpolation
serverTime = new NetworkTime(k_MockTickRate, 2.5d); // t = 2.5d
interpolator.Update(2.5f - 2.01f, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(0.5f).Within(k_Precision));
// check when reaching end
serverTime += 0.5d; // t = 3
interpolator.Update(0.5f, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(1f).Within(k_Precision));
}
/// <summary>
/// Out of order or 'ACB' problem
/// Given two measurements have already arrived A and C, if a new measurement B arrives, the interpolation shouldn't go to B, but continue
/// to C.
/// Adding B should be ignored if interpolation is already interpolating between A and C
/// </summary>
[Test]
public void OutOfOrderShouldStillWork()
{
var serverTime = new NetworkTime(k_MockTickRate, 0.01d);
var interpolator = new BufferedLinearInterpolatorFloat();
double timeStep = 0.5d;
interpolator.AddMeasurement(0f, 0d);
interpolator.AddMeasurement(2f, 2d);
serverTime = new NetworkTime(k_MockTickRate, 1.5d);
interpolator.Update(1.5f, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(0f).Within(k_Precision));
serverTime += timeStep; // t = 2.0
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(1f).Within(k_Precision));
serverTime += timeStep; // t = 2.5
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(1.5f).Within(k_Precision));
// makes sure that interpolation still continues in right direction
interpolator.AddMeasurement(1, 1d);
serverTime += timeStep; // t = 3
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(2f).Within(k_Precision));
}
[Test]
public void MessageLoss()
{
var serverTime = new NetworkTime(k_MockTickRate, 0.01d);
var interpolator = new BufferedLinearInterpolatorFloat();
double timeStep = 0.5d;
interpolator.AddMeasurement(1f, 1d);
interpolator.AddMeasurement(2f, 2d);
// message time=3 was lost
interpolator.AddMeasurement(4f, 4d);
interpolator.AddMeasurement(5f, 5d);
// message time=6 was lost
interpolator.AddMeasurement(100f, 7d); // high value to produce a misprediction
// first value teleports interpolator
serverTime = new NetworkTime(k_MockTickRate, 1d);
interpolator.Update(1f, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(1f));
// nothing happens, not ready to consume second value yet
serverTime += timeStep; // t = 1.5
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(1f));
// beginning of interpolation, second value consumed, currently at start
serverTime += timeStep; // t = 2
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(1f));
// interpolation starts
serverTime += timeStep; // t = 2.5
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(1.5f));
serverTime += timeStep; // t = 3
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(2f));
// extrapolating to 2.5
serverTime += timeStep; // t = 3.5d
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(2.5f));
// next value skips to where it was supposed to be once buffer time is showing the next value
serverTime += timeStep; // t = 4
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(3f));
// interpolation continues as expected
serverTime += timeStep; // t = 4.5
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(3.5f));
serverTime += timeStep; // t = 5
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(4f));
// lost time=6, extrapolating
serverTime += timeStep; // t = 5.5
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(4.5f));
serverTime += timeStep; // t = 6.0
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(5f));
// misprediction
serverTime += timeStep; // t = 6.5
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.EqualTo(5.5f));
// lerp to right value
serverTime += timeStep; // t = 7.0
interpolator.Update((float)timeStep, serverTime);
Assert.That(interpolator.GetInterpolatedValue(), Is.GreaterThan(6.0f));
Assert.That(interpolator.GetInterpolatedValue(), Is.LessThanOrEqualTo(100f));
}
[Test]
public void AddFirstMeasurement()
{
var interpolator = new BufferedLinearInterpolatorFloat();
var serverTime = new NetworkTime(k_MockTickRate, 0d);
interpolator.AddMeasurement(2f, 1d);
interpolator.AddMeasurement(3f, 2d);
serverTime += 1d; // t = 1
var interpolatedValue = interpolator.Update(1f, serverTime);
// when consuming only one measurement and it's the first one consumed, teleport to it
Assert.That(interpolatedValue, Is.EqualTo(2f));
// then interpolation should work as usual
serverTime += 1d; // t = 2
interpolatedValue = interpolator.Update(1f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(2f));
serverTime += 0.5d; // t = 2.5
interpolatedValue = interpolator.Update(0.5f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(2.5f));
serverTime += 0.5d; // t = 3
interpolatedValue = interpolator.Update(.5f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(3f));
}
[Test]
public void JumpToEachValueIfDeltaTimeTooBig()
{
var interpolator = new BufferedLinearInterpolatorFloat();
var serverTime = new NetworkTime(k_MockTickRate, 0d);
interpolator.AddMeasurement(2f, 1d);
interpolator.AddMeasurement(3f, 2d);
serverTime += 1d; // t = 1
var interpolatedValue = interpolator.Update(1f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(2f));
// big deltaTime, jumping to latest value
serverTime += 9f; // t = 10
interpolatedValue = interpolator.Update(8f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(3));
}
[Test]
public void JumpToLastValueFromStart()
{
var interpolator = new BufferedLinearInterpolatorFloat();
var serverTime = new NetworkTime(k_MockTickRate, 0d);
serverTime += 1d; // t = 1
interpolator.AddMeasurement(1f, serverTime.Time);
serverTime += 1d; // t = 2
interpolator.AddMeasurement(2f, serverTime.Time);
serverTime += 1d; // t = 3
interpolator.AddMeasurement(3f, serverTime.Time);
// big time jump
serverTime += 7d; // t = 10
var interpolatedValue = interpolator.Update(10f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(3f));
// interpolation continues as normal
serverTime = new NetworkTime(k_MockTickRate, 11d); // t = 11
interpolator.AddMeasurement(11f, serverTime.Time); // out of order
serverTime = new NetworkTime(k_MockTickRate, 10.5d); // t = 10.5
interpolatedValue = interpolator.Update(0.5f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(3f));
serverTime += 0.5d; // t = 11
interpolatedValue = interpolator.Update(0.5f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(10f));
serverTime += 0.5d; // t = 11.5
interpolatedValue = interpolator.Update(0.5f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(10.5f));
serverTime += 0.5d; // t = 12
interpolatedValue = interpolator.Update(0.5f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(11f));
}
[Test]
public void TestBufferSizeLimit()
{
var interpolator = new BufferedLinearInterpolatorFloat();
// set first value
var serverTime = new NetworkTime(k_MockTickRate, 0d);
serverTime += 1.0d; // t = 1
interpolator.AddMeasurement(-1f, serverTime.Time);
interpolator.Update(1f, serverTime);
// max + 1
serverTime += 1.0d; // t = 2
interpolator.AddMeasurement(2, serverTime.Time); // +1, this should trigger a burst and teleport to last value
for (int i = 0; i < 100; i++)
{
interpolator.AddMeasurement(i + 3, i + 3d);
}
// client was paused for a while, some time has past, we just got a burst of values from the server that teleported us to the last value received
serverTime = new NetworkTime(k_MockTickRate, 102d);
var interpolatedValue = interpolator.Update(101f, serverTime);
Assert.That(interpolatedValue, Is.EqualTo(102));
}
[Test]
public void TestUpdatingInterpolatorWithNoData()
{
var interpolator = new BufferedLinearInterpolatorFloat();
var serverTime = new NetworkTime(k_MockTickRate, 0.0d);
// invalid case, this is undefined behaviour
Assert.Throws<InvalidOperationException>(() => interpolator.Update(1f, serverTime));
}
[Test]
public void TestDuplicatedValues()
{
var interpolator = new BufferedLinearInterpolatorFloat();
var serverTime = new NetworkTime(k_MockTickRate, 0.0d);
serverTime += 1d; // t = 1
interpolator.AddMeasurement(1f, serverTime.Time);
serverTime += 1d; // t = 2
interpolator.AddMeasurement(2f, serverTime.Time);
interpolator.AddMeasurement(2f, serverTime.Time);
// empty interpolator teleports to initial value
serverTime = new NetworkTime(k_MockTickRate, 0.0d);
serverTime += 1d; // t = 1
var interp = interpolator.Update(1f, serverTime);
Assert.That(interp, Is.EqualTo(1f));
// consume value, start interp, currently at start value
serverTime += 1d; // t = 2
interp = interpolator.Update(1f, serverTime);
Assert.That(interp, Is.EqualTo(1f));
// interp
serverTime += 0.5d; // t = 2.5
interp = interpolator.Update(0.5f, serverTime);
Assert.That(interp, Is.EqualTo(1.5f));
// reach end
serverTime += 0.5d; // t = 3
interp = interpolator.Update(0.5f, serverTime);
Assert.That(interp, Is.EqualTo(2f));
// with unclamped interpolation, we continue mispredicting since the two last values are actually treated as the same. Therefore we're not stopping at "2"
serverTime += 0.5d; // t = 3.5
interp = interpolator.Update(0.5f, serverTime);
Assert.That(interp, Is.EqualTo(2.5f));
serverTime += 0.5d; // t = 4
interp = interpolator.Update(0.5f, serverTime);
Assert.That(interp, Is.EqualTo(3f));
// we add a measurement with an updated time
var pastServerTime = new NetworkTime(k_MockTickRate, 3.0d);
interpolator.AddMeasurement(2f, pastServerTime.Time);
interp = interpolator.Update(0.5f, serverTime);
Assert.That(interp, Is.EqualTo(2f));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5cfbc170161c4e95ac6124ee43068b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 55531000b0344935b665541f089df60e
timeCreated: 1630354914

View File

@@ -0,0 +1,216 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using NUnit.Framework.Internal;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace Unity.Netcode.EditorTests
{
public class MessageReceivingTests
{
private struct TestMessage : INetworkMessage
{
public int A;
public int B;
public int C;
public static bool Deserialized;
public static List<TestMessage> DeserializedValues = new List<TestMessage>();
public void Serialize(FastBufferWriter writer)
{
writer.WriteValueSafe(this);
}
public static void Receive(FastBufferReader reader, in NetworkContext context)
{
Deserialized = true;
reader.ReadValueSafe(out TestMessage value);
DeserializedValues.Add(value);
}
}
private class TestMessageProvider : IMessageProvider
{
public List<MessagingSystem.MessageWithHandler> GetMessages()
{
return new List<MessagingSystem.MessageWithHandler>
{
new MessagingSystem.MessageWithHandler
{
MessageType = typeof(TestMessage),
Handler = TestMessage.Receive
}
};
}
}
private MessagingSystem m_MessagingSystem;
[SetUp]
public void SetUp()
{
TestMessage.Deserialized = false;
TestMessage.DeserializedValues.Clear();
m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this, new TestMessageProvider());
}
[TearDown]
public void TearDown()
{
m_MessagingSystem.Dispose();
}
private TestMessage GetMessage()
{
var random = new Random();
return new TestMessage
{
A = random.Next(),
B = random.Next(),
C = random.Next(),
};
}
[Test]
public void WhenHandlingAMessage_ReceiveMethodIsCalled()
{
var messageHeader = new MessageHeader
{
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
};
var message = GetMessage();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.TryBeginWrite(FastBufferWriter.GetWriteSize(message));
writer.WriteValue(message);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
m_MessagingSystem.HandleMessage(messageHeader, reader, 0, 0);
Assert.IsTrue(TestMessage.Deserialized);
Assert.AreEqual(1, TestMessage.DeserializedValues.Count);
Assert.AreEqual(message, TestMessage.DeserializedValues[0]);
}
}
}
[Test]
public void WhenHandlingIncomingData_ReceiveIsNotCalledBeforeProcessingIncomingMessageQueue()
{
var batchHeader = new BatchHeader
{
BatchSize = 1
};
var messageHeader = new MessageHeader
{
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
};
var message = GetMessage();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) +
FastBufferWriter.GetWriteSize(messageHeader) +
FastBufferWriter.GetWriteSize(message));
writer.WriteValue(batchHeader);
writer.WriteValue(messageHeader);
writer.WriteValue(message);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
m_MessagingSystem.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
Assert.IsFalse(TestMessage.Deserialized);
Assert.IsEmpty(TestMessage.DeserializedValues); ;
}
}
}
[Test]
public void WhenReceivingAMessageAndProcessingMessageQueue_ReceiveMethodIsCalled()
{
var batchHeader = new BatchHeader
{
BatchSize = 1
};
var messageHeader = new MessageHeader
{
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
};
var message = GetMessage();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) +
FastBufferWriter.GetWriteSize(messageHeader) +
FastBufferWriter.GetWriteSize(message));
writer.WriteValue(batchHeader);
writer.WriteValue(messageHeader);
writer.WriteValue(message);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
m_MessagingSystem.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
m_MessagingSystem.ProcessIncomingMessageQueue();
Assert.IsTrue(TestMessage.Deserialized);
Assert.AreEqual(1, TestMessage.DeserializedValues.Count);
Assert.AreEqual(message, TestMessage.DeserializedValues[0]);
}
}
}
[Test]
public void WhenReceivingMultipleMessagesAndProcessingMessageQueue_ReceiveMethodIsCalledMultipleTimes()
{
var batchHeader = new BatchHeader
{
BatchSize = 2
};
var messageHeader = new MessageHeader
{
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
};
var message = GetMessage();
var message2 = GetMessage();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.TryBeginWrite(FastBufferWriter.GetWriteSize(batchHeader) +
FastBufferWriter.GetWriteSize(messageHeader) * 2 +
FastBufferWriter.GetWriteSize(message) * 2);
writer.WriteValue(batchHeader);
writer.WriteValue(messageHeader);
writer.WriteValue(message);
writer.WriteValue(messageHeader);
writer.WriteValue(message2);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
m_MessagingSystem.HandleIncomingData(0, new ArraySegment<byte>(writer.ToArray()), 0);
Assert.IsFalse(TestMessage.Deserialized);
Assert.IsEmpty(TestMessage.DeserializedValues);
m_MessagingSystem.ProcessIncomingMessageQueue();
Assert.IsTrue(TestMessage.Deserialized);
Assert.AreEqual(2, TestMessage.DeserializedValues.Count);
Assert.AreEqual(message, TestMessage.DeserializedValues[0]);
Assert.AreEqual(message2, TestMessage.DeserializedValues[1]);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbc8fb6cf75f52d46a5d74971ce4b240
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,179 @@
using System.Collections.Generic;
using NUnit.Framework;
namespace Unity.Netcode.EditorTests
{
public class MessageRegistrationTests
{
private struct TestMessageOne : INetworkMessage
{
public int A;
public int B;
public int C;
public void Serialize(FastBufferWriter writer)
{
writer.WriteValue(this);
}
public static void Receive(FastBufferReader reader, in NetworkContext context)
{
}
}
private struct TestMessageTwo : INetworkMessage
{
public int A;
public int B;
public int C;
public void Serialize(FastBufferWriter writer)
{
writer.WriteValue(this);
}
public static void Receive(FastBufferReader reader, in NetworkContext context)
{
}
}
private class TestMessageProviderOne : IMessageProvider
{
public List<MessagingSystem.MessageWithHandler> GetMessages()
{
return new List<MessagingSystem.MessageWithHandler>
{
new MessagingSystem.MessageWithHandler
{
MessageType = typeof(TestMessageOne),
Handler = TestMessageOne.Receive
},
new MessagingSystem.MessageWithHandler
{
MessageType = typeof(TestMessageTwo),
Handler = TestMessageTwo.Receive
}
};
}
}
private struct TestMessageThree : INetworkMessage
{
public int A;
public int B;
public int C;
public void Serialize(FastBufferWriter writer)
{
writer.WriteValue(this);
}
public static void Receive(FastBufferReader reader, in NetworkContext context)
{
}
}
private class TestMessageProviderTwo : IMessageProvider
{
public List<MessagingSystem.MessageWithHandler> GetMessages()
{
return new List<MessagingSystem.MessageWithHandler>
{
new MessagingSystem.MessageWithHandler
{
MessageType = typeof(TestMessageThree),
Handler = TestMessageThree.Receive
}
};
}
}
private struct TestMessageFour : INetworkMessage
{
public int A;
public int B;
public int C;
public void Serialize(FastBufferWriter writer)
{
writer.WriteValue(this);
}
public static void Receive(FastBufferReader reader, in NetworkContext context)
{
}
}
private class TestMessageProviderThree : IMessageProvider
{
public List<MessagingSystem.MessageWithHandler> GetMessages()
{
return new List<MessagingSystem.MessageWithHandler>
{
new MessagingSystem.MessageWithHandler
{
MessageType = typeof(TestMessageFour),
Handler = TestMessageFour.Receive
}
};
}
}
[Test]
public void WhenCreatingMessageSystem_OnlyProvidedTypesAreRegistered()
{
var sender = new NopMessageSender();
var systemOne = new MessagingSystem(sender, null, new TestMessageProviderOne());
var systemTwo = new MessagingSystem(sender, null, new TestMessageProviderTwo());
var systemThree = new MessagingSystem(sender, null, new TestMessageProviderThree());
using (systemOne)
using (systemTwo)
using (systemThree)
{
Assert.AreEqual(2, systemOne.MessageHandlerCount);
Assert.AreEqual(1, systemTwo.MessageHandlerCount);
Assert.AreEqual(1, systemThree.MessageHandlerCount);
Assert.Contains(typeof(TestMessageOne), systemOne.MessageTypes);
Assert.Contains(typeof(TestMessageTwo), systemOne.MessageTypes);
Assert.Contains(typeof(TestMessageThree), systemTwo.MessageTypes);
Assert.Contains(typeof(TestMessageFour), systemThree.MessageTypes);
}
}
[Test]
public void WhenCreatingMessageSystem_BoundTypeMessageHandlersAreRegistered()
{
var sender = new NopMessageSender();
var systemOne = new MessagingSystem(sender, null, new TestMessageProviderOne());
var systemTwo = new MessagingSystem(sender, null, new TestMessageProviderTwo());
var systemThree = new MessagingSystem(sender, null, new TestMessageProviderThree());
using (systemOne)
using (systemTwo)
using (systemThree)
{
MessagingSystem.MessageHandler handlerOne = TestMessageOne.Receive;
MessagingSystem.MessageHandler handlerTwo = TestMessageTwo.Receive;
MessagingSystem.MessageHandler handlerThree = TestMessageThree.Receive;
MessagingSystem.MessageHandler handlerFour = TestMessageFour.Receive;
var foundHandlerOne = systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageOne))];
Assert.AreEqual(handlerOne,
systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageOne))]);
Assert.AreEqual(handlerTwo,
systemOne.MessageHandlers[systemOne.GetMessageType(typeof(TestMessageTwo))]);
Assert.AreEqual(handlerThree,
systemTwo.MessageHandlers[systemTwo.GetMessageType(typeof(TestMessageThree))]);
Assert.AreEqual(handlerFour,
systemThree.MessageHandlers[systemThree.GetMessageType(typeof(TestMessageFour))]);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 03e7cc2b6f0c5c540a529429f48529f5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace Unity.Netcode.EditorTests
{
public class MessageSendingTests
{
private struct TestMessage : INetworkMessage
{
public int A;
public int B;
public int C;
public static bool Serialized;
public void Serialize(FastBufferWriter writer)
{
Serialized = true;
writer.WriteValueSafe(this);
}
public static void Receive(FastBufferReader reader, in NetworkContext context)
{
}
}
private class TestMessageSender : IMessageSender
{
public List<byte[]> MessageQueue = new List<byte[]>();
public void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData)
{
MessageQueue.Add(batchData.ToArray());
}
}
private class TestMessageProvider : IMessageProvider
{
public List<MessagingSystem.MessageWithHandler> GetMessages()
{
return new List<MessagingSystem.MessageWithHandler>
{
new MessagingSystem.MessageWithHandler
{
MessageType = typeof(TestMessage),
Handler = TestMessage.Receive
}
};
}
}
private TestMessageSender m_MessageSender;
private MessagingSystem m_MessagingSystem;
private ulong[] m_Clients = { 0 };
[SetUp]
public void SetUp()
{
TestMessage.Serialized = false;
m_MessageSender = new TestMessageSender();
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, new TestMessageProvider());
m_MessagingSystem.ClientConnected(0);
}
[TearDown]
public void TearDown()
{
m_MessagingSystem.Dispose();
}
private TestMessage GetMessage()
{
var random = new Random();
return new TestMessage
{
A = random.Next(),
B = random.Next(),
C = random.Next(),
};
}
[Test]
public void WhenSendingMessage_SerializeIsCalled()
{
var message = GetMessage();
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
Assert.IsTrue(TestMessage.Serialized);
}
[Test]
public void WhenSendingMessage_NothingIsSentBeforeProcessingSendQueue()
{
var message = GetMessage();
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
Assert.IsEmpty(m_MessageSender.MessageQueue);
}
[Test]
public void WhenProcessingSendQueue_MessageIsSent()
{
var message = GetMessage();
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.ProcessSendQueues();
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
}
[Test]
public void WhenSendingMultipleMessages_MessagesAreBatched()
{
var message = GetMessage();
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.ProcessSendQueues();
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
}
[Test]
public void WhenNotExceedingBatchSize_NewBatchesAreNotCreated()
{
var message = GetMessage();
var size = UnsafeUtility.SizeOf<TestMessage>() + UnsafeUtility.SizeOf<MessageHeader>();
for (var i = 0; i < 1300 / size; ++i)
{
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
}
m_MessagingSystem.ProcessSendQueues();
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
}
[Test]
public void WhenExceedingBatchSize_NewBatchesAreCreated()
{
var message = GetMessage();
var size = UnsafeUtility.SizeOf<TestMessage>() + UnsafeUtility.SizeOf<MessageHeader>();
for (var i = 0; i < (1300 / size) + 1; ++i)
{
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
}
m_MessagingSystem.ProcessSendQueues();
Assert.AreEqual(2, m_MessageSender.MessageQueue.Count);
}
[Test]
public void WhenExceedingMTUSizeWithFragmentedDelivery_NewBatchesAreNotCreated()
{
var message = GetMessage();
var size = UnsafeUtility.SizeOf<TestMessage>() + UnsafeUtility.SizeOf<MessageHeader>();
for (var i = 0; i < (1300 / size) + 1; ++i)
{
m_MessagingSystem.SendMessage(message, NetworkDelivery.ReliableFragmentedSequenced, m_Clients);
}
m_MessagingSystem.ProcessSendQueues();
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
}
[Test]
public void WhenSwitchingDelivery_NewBatchesAreCreated()
{
var message = GetMessage();
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.SendMessage(message, NetworkDelivery.Unreliable, m_Clients);
m_MessagingSystem.ProcessSendQueues();
Assert.AreEqual(2, m_MessageSender.MessageQueue.Count);
}
[Test]
public void WhenSwitchingChannel_NewBatchesAreNotCreated()
{
var message = GetMessage();
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.ProcessSendQueues();
Assert.AreEqual(1, m_MessageSender.MessageQueue.Count);
}
[Test]
public void WhenSendingMessaged_SentDataIsCorrect()
{
var message = GetMessage();
var message2 = GetMessage();
m_MessagingSystem.SendMessage(message, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.SendMessage(message2, NetworkDelivery.Reliable, m_Clients);
m_MessagingSystem.ProcessSendQueues();
var reader = new FastBufferReader(m_MessageSender.MessageQueue[0], Allocator.Temp);
using (reader)
{
reader.TryBeginRead(
FastBufferWriter.GetWriteSize<BatchHeader>() +
FastBufferWriter.GetWriteSize<MessageHeader>() * 2 +
FastBufferWriter.GetWriteSize<TestMessage>() * 2
);
reader.ReadValue(out BatchHeader header);
Assert.AreEqual(2, header.BatchSize);
reader.ReadValue(out MessageHeader messageHeader);
Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader.MessageType);
Assert.AreEqual(UnsafeUtility.SizeOf<TestMessage>(), messageHeader.MessageSize);
reader.ReadValue(out TestMessage receivedMessage);
Assert.AreEqual(message, receivedMessage);
reader.ReadValue(out MessageHeader messageHeader2);
Assert.AreEqual(m_MessagingSystem.GetMessageType(typeof(TestMessage)), messageHeader2.MessageType);
Assert.AreEqual(UnsafeUtility.SizeOf<TestMessage>(), messageHeader2.MessageSize);
reader.ReadValue(out TestMessage receivedMessage2);
Assert.AreEqual(message2, receivedMessage2);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ea2fc218c5e07c54795fc9bed4a6a62c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
namespace Unity.Netcode.EditorTests
{
internal class NopMessageSender : IMessageSender
{
public void Send(ulong clientId, NetworkDelivery delivery, FastBufferWriter batchData)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 817c58672ba39a74da57082ed176956e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87ddfad8823c4fe192fff56b7acc241b
timeCreated: 1629386688

View File

@@ -0,0 +1,41 @@
#if MULTIPLAYER_TOOLS
using System;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using Unity.Multiplayer.Tools.MetricTypes;
using Unity.Multiplayer.Tools.NetStats;
namespace Unity.Netcode.EditorTests.Metrics
{
public class NetworkMetricsRegistrationTests
{
static Type[] s_MetricTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => x.GetInterfaces().Contains(typeof(INetworkMetricEvent)))
.ToArray();
[TestCaseSource(nameof(s_MetricTypes))][Ignore("Disable test while we reevaluate the assumption that INetworkMetricEvent interfaces must be reported from MLAPI.")]
public void ValidateThatAllMetricTypesAreRegistered(Type metricType)
{
var dispatcher = new NetworkMetrics().Dispatcher as MetricDispatcher;
Assert.NotNull(dispatcher);
var collection = typeof(MetricDispatcher)
.GetField("m_Collection", BindingFlags.NonPublic | BindingFlags.Instance)?
.GetValue(dispatcher) as MetricCollection;
Assert.NotNull(collection);
Assert.That(
collection.Metrics.OfType<IEventMetric>(),
Has.Exactly(2).Matches<IEventMetric>(
eventMetric =>
{
var eventType = eventMetric.GetType().GetGenericArguments()?.FirstOrDefault();
return eventType == metricType;
}));
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af741f5e3d4f5544eaa68bb9bcaf54c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
using NUnit.Framework;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Unity.Netcode.EditorTests
{
public class NetworkBehaviourTests
{
[Test]
public void HasNetworkObjectTest()
{
var gameObject = new GameObject(nameof(HasNetworkObjectTest));
var networkBehaviour = gameObject.AddComponent<EmptyNetworkBehaviour>();
Assert.That(networkBehaviour.HasNetworkObject, Is.False);
var networkObject = gameObject.AddComponent<NetworkObject>();
Assert.That(networkBehaviour.HasNetworkObject, Is.True);
Object.DestroyImmediate(networkObject);
Assert.That(networkBehaviour.HasNetworkObject, Is.False);
// Cleanup
Object.DestroyImmediate(gameObject);
}
[Test]
public void AccessNetworkObjectTest()
{
var gameObject = new GameObject(nameof(AccessNetworkObjectTest));
var networkBehaviour = gameObject.AddComponent<EmptyNetworkBehaviour>();
Assert.That(networkBehaviour.NetworkObject, Is.Null);
var networkObject = gameObject.AddComponent<NetworkObject>();
Assert.That(networkBehaviour.NetworkObject, Is.EqualTo(networkObject));
Object.DestroyImmediate(networkObject);
Assert.That(networkBehaviour.NetworkObject, Is.Null);
// Cleanup
Object.DestroyImmediate(gameObject);
}
[Test]
public void GivenClassDerivesFromNetworkBehaviour_GetTypeNameReturnsCorrectValue()
{
var gameObject = new GameObject(nameof(GivenClassDerivesFromNetworkBehaviour_GetTypeNameReturnsCorrectValue));
var networkBehaviour = gameObject.AddComponent<EmptyNetworkBehaviour>();
Assert.AreEqual(nameof(EmptyNetworkBehaviour), networkBehaviour.__getTypeName());
}
[Test]
public void GivenClassDerivesFromNetworkBehaviourDerivedClass_GetTypeNameReturnsCorrectValue()
{
var gameObject = new GameObject(nameof(GivenClassDerivesFromNetworkBehaviourDerivedClass_GetTypeNameReturnsCorrectValue));
var networkBehaviour = gameObject.AddComponent<DerivedNetworkBehaviour>();
Assert.AreEqual(nameof(DerivedNetworkBehaviour), networkBehaviour.__getTypeName());
}
// Note: in order to repro https://github.com/Unity-Technologies/com.unity.netcode.gameobjects/issues/1078
// this child class must be defined before its parent to assure it is processed first by ILPP
public class DerivedNetworkBehaviour : EmptyNetworkBehaviour
{
}
public class EmptyNetworkBehaviour : NetworkBehaviour
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 73e18571452c102e4b209671741f3b51
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
using NUnit.Framework;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
public class NetworkManagerCustomMessageManagerTests
{
[Test]
public void CustomMessageManagerAssigned()
{
var gameObject = new GameObject(nameof(CustomMessageManagerAssigned));
var networkManager = gameObject.AddComponent<NetworkManager>();
var transport = gameObject.AddComponent<DummyTransport>();
networkManager.NetworkConfig = new NetworkConfig();
// Set dummy transport that does nothing
networkManager.NetworkConfig.NetworkTransport = transport;
CustomMessagingManager preManager = networkManager.CustomMessagingManager;
// Start server to cause initialization
networkManager.StartServer();
Debug.Assert(preManager == null);
Debug.Assert(networkManager.CustomMessagingManager != null);
Object.DestroyImmediate(gameObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 315ffe39806441839400d21871d566a0
timeCreated: 1618478909

View File

@@ -0,0 +1,54 @@
using System;
using Unity.Netcode.Editor;
namespace Unity.Netcode.EditorTests
{
// Should probably have one of these for more files? In the future we could use the SIPTransport?
[DontShowInTransportDropdown]
internal class DummyTransport : NetworkTransport
{
public override ulong ServerClientId { get; } = 0;
public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery networkDelivery)
{
}
public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
{
clientId = 0;
payload = new ArraySegment<byte>();
receiveTime = 0;
return NetworkEvent.Nothing;
}
public override bool StartClient()
{
return true;
}
public override bool StartServer()
{
return true;
}
public override void DisconnectRemoteClient(ulong clientId)
{
}
public override void DisconnectLocalClient()
{
}
public override ulong GetCurrentRtt(ulong clientId)
{
return 0;
}
public override void Shutdown()
{
}
public override void Initialize()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 976ca592c7fa4bcb854203dfbadc0ad9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using NUnit.Framework;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
public class NetworkManagerSceneManagerTests
{
[Test]
public void SceneManagerAssigned()
{
var gameObject = new GameObject(nameof(SceneManagerAssigned));
var networkManager = gameObject.AddComponent<NetworkManager>();
var transport = gameObject.AddComponent<DummyTransport>();
networkManager.NetworkConfig = new NetworkConfig();
// Set dummy transport that does nothing
networkManager.NetworkConfig.NetworkTransport = transport;
NetworkSceneManager preManager = networkManager.SceneManager;
// Start server to cause initialization process
networkManager.StartServer();
Debug.Assert(preManager == null);
Debug.Assert(networkManager.SceneManager != null);
Object.DestroyImmediate(gameObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9753bf4088484cbebee95d917699dec6
timeCreated: 1618482634

View File

@@ -0,0 +1,76 @@
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.EditorTests
{
public class NetworkObjectTests
{
[Test]
public void NetworkManagerOverrideTest()
{
// Create "bait"
var singletonNetworkManager = new GameObject(nameof(NetworkManager)).AddComponent<NetworkManager>();
singletonNetworkManager.SetSingleton();
// Create override
var networkManager = new GameObject(nameof(NetworkManager)).AddComponent<NetworkManager>();
// NetworkObject
var gameObject = new GameObject(nameof(NetworkManagerOverrideTest));
var networkObject = gameObject.AddComponent<NetworkObject>();
// Set override
networkObject.NetworkManagerOwner = networkManager;
Debug.Assert(networkObject.NetworkManager == networkManager);
Object.DestroyImmediate(singletonNetworkManager.gameObject);
Object.DestroyImmediate(networkManager.gameObject);
Object.DestroyImmediate(gameObject);
}
[Test]
public void GetBehaviourIndexNone()
{
var gameObject = new GameObject(nameof(GetBehaviourIndexNone));
var networkObject = gameObject.AddComponent<NetworkObject>();
// TODO: Maybe not hardcode message?
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(0), Is.Null);
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(1), Is.Null);
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(2), Is.Null);
// Cleanup
Object.DestroyImmediate(gameObject);
}
[Test]
public void GetBehaviourIndexOne()
{
var gameObject = new GameObject(nameof(GetBehaviourIndexOne));
var networkObject = gameObject.AddComponent<NetworkObject>();
var networkBehaviour = gameObject.AddComponent<EmptyNetworkBehaviour>();
// TODO: Maybe not hardcode message?
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
LogAssert.Expect(LogType.Error, $"[Netcode] Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(0), Is.EqualTo(networkBehaviour));
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(1), Is.Null);
Assert.That(networkObject.GetNetworkBehaviourAtOrderIndex(2), Is.Null);
// Cleanup
Object.DestroyImmediate(gameObject);
}
public class EmptyNetworkBehaviour : NetworkBehaviour
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 542af4c12ffee14f59967bc8e41f5e9d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e12c4be6e89f459aa2826abba8c8d301
timeCreated: 1628799671

View File

@@ -0,0 +1,587 @@
using System;
using NUnit.Framework;
using UnityEngine;
using Random = System.Random;
namespace Unity.Netcode
{
public abstract class BaseFastBufferReaderWriterTest
{
#region Test Types
protected enum ByteEnum : byte
{
A,
B,
C
};
protected enum SByteEnum : sbyte
{
A,
B,
C
};
protected enum ShortEnum : short
{
A,
B,
C
};
protected enum UShortEnum : ushort
{
A,
B,
C
};
protected enum IntEnum : int
{
A,
B,
C
};
protected enum UIntEnum : uint
{
A,
B,
C
};
protected enum LongEnum : long
{
A,
B,
C
};
protected enum ULongEnum : ulong
{
A,
B,
C
};
protected struct TestStruct
{
public byte A;
public short B;
public ushort C;
public int D;
public uint E;
public long F;
public ulong G;
public bool H;
public char I;
public float J;
public double K;
}
public enum WriteType
{
WriteDirect,
WriteSafe
}
#endregion
protected abstract void RunTypeTest<T>(T valueToTest) where T : unmanaged;
protected abstract void RunTypeTestSafe<T>(T valueToTest) where T : unmanaged;
protected abstract void RunTypeArrayTest<T>(T[] valueToTest) where T : unmanaged;
protected abstract void RunTypeArrayTestSafe<T>(T[] valueToTest) where T : unmanaged;
#region Helpers
protected TestStruct GetTestStruct()
{
var random = new Random();
var testStruct = new TestStruct
{
A = (byte)random.Next(),
B = (short)random.Next(),
C = (ushort)random.Next(),
D = (int)random.Next(),
E = (uint)random.Next(),
F = ((long)random.Next() << 32) + random.Next(),
G = ((ulong)random.Next() << 32) + (ulong)random.Next(),
H = true,
I = '\u263a',
J = (float)random.NextDouble(),
K = random.NextDouble(),
};
return testStruct;
}
#endregion
public void BaseTypeTest(Type testType, WriteType writeType)
{
var random = new Random();
void RunTypeTestLocal<T>(T val, WriteType wt) where T : unmanaged
{
switch (wt)
{
case WriteType.WriteDirect:
RunTypeTest(val);
break;
case WriteType.WriteSafe:
RunTypeTestSafe(val);
break;
}
}
if (testType == typeof(byte))
{
RunTypeTestLocal((byte)random.Next(), writeType);
}
else if (testType == typeof(sbyte))
{
RunTypeTestLocal((sbyte)random.Next(), writeType);
}
else if (testType == typeof(short))
{
RunTypeTestLocal((short)random.Next(), writeType);
}
else if (testType == typeof(ushort))
{
RunTypeTestLocal((ushort)random.Next(), writeType);
}
else if (testType == typeof(int))
{
RunTypeTestLocal((int)random.Next(), writeType);
}
else if (testType == typeof(uint))
{
RunTypeTestLocal((uint)random.Next(), writeType);
}
else if (testType == typeof(long))
{
RunTypeTestLocal(((long)random.Next() << 32) + random.Next(), writeType);
}
else if (testType == typeof(ulong))
{
RunTypeTestLocal(((ulong)random.Next() << 32) + (ulong)random.Next(), writeType);
}
else if (testType == typeof(bool))
{
RunTypeTestLocal(true, writeType);
}
else if (testType == typeof(char))
{
RunTypeTestLocal('a', writeType);
RunTypeTestLocal('\u263a', writeType);
}
else if (testType == typeof(float))
{
RunTypeTestLocal((float)random.NextDouble(), writeType);
}
else if (testType == typeof(double))
{
RunTypeTestLocal(random.NextDouble(), writeType);
}
else if (testType == typeof(ByteEnum))
{
RunTypeTestLocal(ByteEnum.C, writeType);
}
else if (testType == typeof(SByteEnum))
{
RunTypeTestLocal(SByteEnum.C, writeType);
}
else if (testType == typeof(ShortEnum))
{
RunTypeTestLocal(ShortEnum.C, writeType);
}
else if (testType == typeof(UShortEnum))
{
RunTypeTestLocal(UShortEnum.C, writeType);
}
else if (testType == typeof(IntEnum))
{
RunTypeTestLocal(IntEnum.C, writeType);
}
else if (testType == typeof(UIntEnum))
{
RunTypeTestLocal(UIntEnum.C, writeType);
}
else if (testType == typeof(LongEnum))
{
RunTypeTestLocal(LongEnum.C, writeType);
}
else if (testType == typeof(ULongEnum))
{
RunTypeTestLocal(ULongEnum.C, writeType);
}
else if (testType == typeof(Vector2))
{
RunTypeTestLocal(new Vector2((float)random.NextDouble(), (float)random.NextDouble()), writeType);
}
else if (testType == typeof(Vector3))
{
RunTypeTestLocal(new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType);
}
else if (testType == typeof(Vector4))
{
RunTypeTestLocal(new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType);
}
else if (testType == typeof(Quaternion))
{
RunTypeTestLocal(new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType);
}
else if (testType == typeof(Color))
{
RunTypeTestLocal(new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType);
}
else if (testType == typeof(Color32))
{
RunTypeTestLocal(new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()), writeType);
}
else if (testType == typeof(Ray))
{
RunTypeTestLocal(new Ray(
new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()),
new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())), writeType);
}
else if (testType == typeof(Ray2D))
{
RunTypeTestLocal(new Ray2D(
new Vector2((float)random.NextDouble(), (float)random.NextDouble()),
new Vector2((float)random.NextDouble(), (float)random.NextDouble())), writeType);
}
else if (testType == typeof(TestStruct))
{
RunTypeTestLocal(GetTestStruct(), writeType);
}
else
{
Assert.Fail("No type handler was provided for this type in the test!");
}
}
public void BaseArrayTypeTest(Type testType, WriteType writeType)
{
var random = new Random();
void RunTypeTestLocal<T>(T[] val, WriteType wt) where T : unmanaged
{
switch (wt)
{
case WriteType.WriteDirect:
RunTypeArrayTest(val);
break;
case WriteType.WriteSafe:
RunTypeArrayTestSafe(val);
break;
}
}
if (testType == typeof(byte))
{
RunTypeTestLocal(new[]{
(byte) random.Next(),
(byte) random.Next(),
(byte) random.Next(),
(byte) random.Next(),
(byte) random.Next(),
(byte) random.Next(),
(byte) random.Next()
}, writeType);
}
else if (testType == typeof(sbyte))
{
RunTypeTestLocal(new[]{
(sbyte) random.Next(),
(sbyte) random.Next(),
(sbyte) random.Next(),
(sbyte) random.Next(),
(sbyte) random.Next(),
(sbyte) random.Next(),
(sbyte) random.Next()
}, writeType);
}
else if (testType == typeof(short))
{
RunTypeTestLocal(new[]{
(short) random.Next(),
(short) random.Next(),
(short) random.Next(),
(short) random.Next(),
(short) random.Next()
}, writeType);
}
else if (testType == typeof(ushort))
{
RunTypeTestLocal(new[]{
(ushort) random.Next(),
(ushort) random.Next(),
(ushort) random.Next(),
(ushort) random.Next(),
(ushort) random.Next(),
(ushort) random.Next()
}, writeType);
}
else if (testType == typeof(int))
{
RunTypeTestLocal(new[]{
random.Next(),
random.Next(),
random.Next(),
random.Next(),
random.Next(),
random.Next(),
random.Next(),
random.Next()
}, writeType);
}
else if (testType == typeof(uint))
{
RunTypeTestLocal(new[]{
(uint) random.Next(),
(uint) random.Next(),
(uint) random.Next(),
(uint) random.Next(),
(uint) random.Next(),
(uint) random.Next()
}, writeType);
}
else if (testType == typeof(long))
{
RunTypeTestLocal(new[]{
((long)random.Next() << 32) + (long)random.Next(),
((long)random.Next() << 32) + (long)random.Next(),
((long)random.Next() << 32) + (long)random.Next(),
((long)random.Next() << 32) + (long)random.Next()
}, writeType);
}
else if (testType == typeof(ulong))
{
RunTypeTestLocal(new[]{
((ulong)random.Next() << 32) + (ulong)random.Next(),
((ulong)random.Next() << 32) + (ulong)random.Next(),
((ulong)random.Next() << 32) + (ulong)random.Next(),
((ulong)random.Next() << 32) + (ulong)random.Next(),
((ulong)random.Next() << 32) + (ulong)random.Next(),
((ulong)random.Next() << 32) + (ulong)random.Next(),
((ulong)random.Next() << 32) + (ulong)random.Next(),
((ulong)random.Next() << 32) + (ulong)random.Next(),
((ulong)random.Next() << 32) + (ulong)random.Next()
}, writeType);
}
else if (testType == typeof(bool))
{
RunTypeTestLocal(new[]{
true,
false,
true,
true,
false,
false,
true,
false,
true
}, writeType);
}
else if (testType == typeof(char))
{
RunTypeTestLocal(new[]{
'a',
'\u263a'
}, writeType);
}
else if (testType == typeof(float))
{
RunTypeTestLocal(new[]{
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble(),
(float)random.NextDouble()
}, writeType);
}
else if (testType == typeof(double))
{
RunTypeTestLocal(new[]{
random.NextDouble(),
random.NextDouble(),
random.NextDouble(),
random.NextDouble(),
random.NextDouble(),
random.NextDouble(),
random.NextDouble(),
random.NextDouble(),
random.NextDouble()
}, writeType);
}
else if (testType == typeof(ByteEnum))
{
RunTypeTestLocal(new[]{
ByteEnum.C,
ByteEnum.A,
ByteEnum.B
}, writeType);
}
else if (testType == typeof(SByteEnum))
{
RunTypeTestLocal(new[]{
SByteEnum.C,
SByteEnum.A,
SByteEnum.B
}, writeType);
}
else if (testType == typeof(ShortEnum))
{
RunTypeTestLocal(new[]{
ShortEnum.C,
ShortEnum.A,
ShortEnum.B
}, writeType);
}
else if (testType == typeof(UShortEnum))
{
RunTypeTestLocal(new[]{
UShortEnum.C,
UShortEnum.A,
UShortEnum.B
}, writeType);
}
else if (testType == typeof(IntEnum))
{
RunTypeTestLocal(new[]{
IntEnum.C,
IntEnum.A,
IntEnum.B
}, writeType);
}
else if (testType == typeof(UIntEnum))
{
RunTypeTestLocal(new[]{
UIntEnum.C,
UIntEnum.A,
UIntEnum.B
}, writeType);
}
else if (testType == typeof(LongEnum))
{
RunTypeTestLocal(new[]{
LongEnum.C,
LongEnum.A,
LongEnum.B
}, writeType);
}
else if (testType == typeof(ULongEnum))
{
RunTypeTestLocal(new[]{
ULongEnum.C,
ULongEnum.A,
ULongEnum.B
}, writeType);
}
else if (testType == typeof(Vector2))
{
RunTypeTestLocal(new[]{
new Vector2((float) random.NextDouble(), (float) random.NextDouble()),
new Vector2((float) random.NextDouble(), (float) random.NextDouble()),
new Vector2((float) random.NextDouble(), (float) random.NextDouble()),
}, writeType);
}
else if (testType == typeof(Vector3))
{
RunTypeTestLocal(new[]{
new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()),
new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()),
new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()),
}, writeType);
}
else if (testType == typeof(Vector4))
{
RunTypeTestLocal(new[]{
new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
}, writeType);
}
else if (testType == typeof(Quaternion))
{
RunTypeTestLocal(new[]{
new Quaternion((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble(), (float) random.NextDouble()),
new Quaternion((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble(), (float) random.NextDouble()),
new Quaternion((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble(), (float) random.NextDouble()),
}, writeType);
}
else if (testType == typeof(Color))
{
RunTypeTestLocal(new[]{
new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
}, writeType);
}
else if (testType == typeof(Color32))
{
RunTypeTestLocal(new[]{
new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()),
new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()),
new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()),
}, writeType);
}
else if (testType == typeof(Ray))
{
RunTypeTestLocal(new[]{
new Ray(
new Vector3((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
new Vector3((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble())),
new Ray(
new Vector3((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
new Vector3((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble())),
new Ray(
new Vector3((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble()),
new Vector3((float) random.NextDouble(), (float) random.NextDouble(),
(float) random.NextDouble())),
}, writeType);
}
else if (testType == typeof(Ray2D))
{
RunTypeTestLocal(new[]{
new Ray2D(
new Vector2((float) random.NextDouble(), (float) random.NextDouble()),
new Vector2((float) random.NextDouble(), (float) random.NextDouble())),
new Ray2D(
new Vector2((float) random.NextDouble(), (float) random.NextDouble()),
new Vector2((float) random.NextDouble(), (float) random.NextDouble())),
new Ray2D(
new Vector2((float) random.NextDouble(), (float) random.NextDouble()),
new Vector2((float) random.NextDouble(), (float) random.NextDouble())),
}, writeType);
}
else if (testType == typeof(TestStruct))
{
RunTypeTestLocal(new[] {
GetTestStruct(),
GetTestStruct(),
GetTestStruct(),
}, writeType);
}
else
{
Assert.Fail("No type handler was provided for this type in the test!");
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 573b1f36caed496a9c6e0eaa788d0c29
timeCreated: 1629917174

View File

@@ -0,0 +1,71 @@
using NUnit.Framework;
namespace Unity.Netcode.EditorTests
{
public class BitCounterTests
{
[Test]
public void WhenCountingUsedBitsIn64BitValue_ResultMatchesHighBitSetPlusOne([Range(0, 63)] int highBit)
{
if (highBit == 0)
{
ulong value = 0;
// 0 is a special case. All values are considered at least 1 bit.
Assert.AreEqual(1, BitCounter.GetUsedBitCount(value));
}
else
{
ulong value = 1UL << highBit;
Assert.AreEqual(highBit + 1, BitCounter.GetUsedBitCount(value));
}
}
[Test]
public void WhenCountingUsedBitsIn32BitValue_ResultMatchesHighBitSetPlusOne([Range(0, 31)] int highBit)
{
if (highBit == 0)
{
uint value = 0;
// 0 is a special case. All values are considered at least 1 bit.
Assert.AreEqual(1, BitCounter.GetUsedBitCount(value));
}
else
{
uint value = 1U << highBit;
Assert.AreEqual(highBit + 1, BitCounter.GetUsedBitCount(value));
}
}
[Test]
public void WhenCountingUsedBytesIn64BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 63)] int highBit)
{
if (highBit == 0)
{
ulong value = 0;
// 0 is a special case. All values are considered at least 1 byte.
Assert.AreEqual(1, BitCounter.GetUsedByteCount(value));
}
else
{
ulong value = 1UL << highBit;
Assert.AreEqual(highBit / 8 + 1, BitCounter.GetUsedByteCount(value));
}
}
[Test]
public void WhenCountingUsedBytesIn32BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 31)] int highBit)
{
if (highBit == 0)
{
uint value = 0;
// 0 is a special case. All values are considered at least 1 byte.
Assert.AreEqual(1, BitCounter.GetUsedByteCount(value));
}
else
{
uint value = 1U << highBit;
Assert.AreEqual(highBit / 8 + 1, BitCounter.GetUsedByteCount(value));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 76e459b9c2aeea94ebf448c237061485
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,371 @@
using System;
using NUnit.Framework;
using Unity.Collections;
namespace Unity.Netcode.EditorTests
{
public class BitReaderTests
{
[Test]
public void TestReadingOneBit()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
Assert.IsTrue(writer.TryBeginWrite(3));
using (var bitWriter = writer.EnterBitwiseContext())
{
bitWriter.WriteBit(true);
bitWriter.WriteBit(true);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
bitWriter.WriteBit(false);
bitWriter.WriteBit(false);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
}
writer.WriteByte(0b11111111);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
Assert.IsTrue(reader.TryBeginRead(3));
using (var bitReader = reader.EnterBitwiseContext())
{
bool b;
bitReader.ReadBit(out b);
Assert.IsTrue(b);
bitReader.ReadBit(out b);
Assert.IsTrue(b);
bitReader.ReadBit(out b);
Assert.IsFalse(b);
bitReader.ReadBit(out b);
Assert.IsTrue(b);
bitReader.ReadBit(out b);
Assert.IsFalse(b);
bitReader.ReadBit(out b);
Assert.IsFalse(b);
bitReader.ReadBit(out b);
Assert.IsFalse(b);
bitReader.ReadBit(out b);
Assert.IsTrue(b);
bitReader.ReadBit(out b);
Assert.IsFalse(b);
bitReader.ReadBit(out b);
Assert.IsTrue(b);
bitReader.ReadBit(out b);
Assert.IsFalse(b);
bitReader.ReadBit(out b);
Assert.IsTrue(b);
}
reader.ReadByte(out byte lastByte);
Assert.AreEqual(0b11111111, lastByte);
}
}
}
[Test]
public unsafe void TestTryBeginReadBits()
{
var nativeArray = new NativeArray<byte>(4, Allocator.Temp);
var reader = new FastBufferReader(nativeArray, Allocator.Temp);
nativeArray.Dispose();
using (reader)
{
int* asInt = (int*)reader.GetUnsafePtr();
*asInt = 0b11111111_00001010_10101011;
using (var bitReader = reader.EnterBitwiseContext())
{
Assert.Throws<InvalidOperationException>(() => reader.TryBeginRead(1));
Assert.Throws<InvalidOperationException>(() => reader.TryBeginReadValue(1));
Assert.IsTrue(bitReader.TryBeginReadBits(1));
bitReader.ReadBit(out bool b);
Assert.IsTrue(b);
// Can't use Assert.Throws() because ref struct BitWriter can't be captured in a lambda
try
{
bitReader.ReadBit(out b);
}
catch (OverflowException)
{
// Should get called here.
}
Assert.IsTrue(bitReader.TryBeginReadBits(3));
bitReader.ReadBit(out b);
Assert.IsTrue(b);
bitReader.ReadBit(out b);
Assert.IsFalse(b);
bitReader.ReadBit(out b);
Assert.IsTrue(b);
byte byteVal;
try
{
bitReader.ReadBits(out byteVal, 4);
}
catch (OverflowException)
{
// Should get called here.
}
try
{
bitReader.ReadBits(out byteVal, 1);
}
catch (OverflowException)
{
// Should get called here.
}
Assert.IsTrue(bitReader.TryBeginReadBits(3));
try
{
bitReader.ReadBits(out byteVal, 4);
}
catch (OverflowException)
{
// Should get called here.
}
Assert.IsTrue(bitReader.TryBeginReadBits(4));
bitReader.ReadBits(out byteVal, 3);
Assert.AreEqual(0b010, byteVal);
Assert.IsTrue(bitReader.TryBeginReadBits(5));
bitReader.ReadBits(out byteVal, 5);
Assert.AreEqual(0b10101, byteVal);
}
Assert.AreEqual(2, reader.Position);
Assert.IsTrue(reader.TryBeginRead(1));
reader.ReadByte(out byte nextByte);
Assert.AreEqual(0b11111111, nextByte);
Assert.IsTrue(reader.TryBeginRead(1));
reader.ReadByte(out nextByte);
Assert.AreEqual(0b00000000, nextByte);
Assert.IsFalse(reader.TryBeginRead(1));
using (var bitReader = reader.EnterBitwiseContext())
{
Assert.IsFalse(bitReader.TryBeginReadBits(1));
}
}
}
[Test]
public void TestReadingMultipleBits()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
Assert.IsTrue(writer.TryBeginWrite(3));
using (var bitWriter = writer.EnterBitwiseContext())
{
bitWriter.WriteBits(0b11111111, 1);
bitWriter.WriteBits(0b11111111, 1);
bitWriter.WriteBits(0b11111110, 2);
bitWriter.WriteBits(0b11111000, 4);
bitWriter.WriteBits(0b11111010, 4);
}
writer.WriteByte(0b11111111);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
Assert.IsTrue(reader.TryBeginRead(3));
using (var bitReader = reader.EnterBitwiseContext())
{
byte b;
bitReader.ReadBits(out b, 1);
Assert.AreEqual(0b1, b);
bitReader.ReadBits(out b, 1);
Assert.AreEqual(0b1, b);
bitReader.ReadBits(out b, 2);
Assert.AreEqual(0b10, b);
bitReader.ReadBits(out b, 4);
Assert.AreEqual(0b1000, b);
bitReader.ReadBits(out b, 4);
Assert.AreEqual(0b1010, b);
}
reader.ReadByte(out byte lastByte);
Assert.AreEqual(0b11111111, lastByte);
}
}
}
[Test]
public void TestReadingMultipleBitsToLongs()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
Assert.IsTrue(writer.TryBeginWrite(3));
using (var bitWriter = writer.EnterBitwiseContext())
{
bitWriter.WriteBits(0b11111111UL, 1);
bitWriter.WriteBits(0b11111111UL, 1);
bitWriter.WriteBits(0b11111110UL, 2);
bitWriter.WriteBits(0b11111000UL, 4);
bitWriter.WriteBits(0b11111010UL, 4);
}
writer.WriteByte(0b11111111);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
Assert.IsTrue(reader.TryBeginRead(3));
using (var bitReader = reader.EnterBitwiseContext())
{
ulong ul;
bitReader.ReadBits(out ul, 1);
Assert.AreEqual(0b1, ul);
bitReader.ReadBits(out ul, 1);
Assert.AreEqual(0b1, ul);
bitReader.ReadBits(out ul, 2);
Assert.AreEqual(0b10, ul);
bitReader.ReadBits(out ul, 4);
Assert.AreEqual(0b1000, ul);
bitReader.ReadBits(out ul, 4);
Assert.AreEqual(0b1010, ul);
}
reader.ReadByte(out byte lastByte);
Assert.AreEqual(0b11111111, lastByte);
}
}
}
[Test]
public unsafe void TestReadingMultipleBytesToLongs([Range(1U, 64U)] uint numBits)
{
ulong value = 0xFFFFFFFFFFFFFFFF;
var reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong));
using (reader)
{
ulong* asUlong = (ulong*)reader.GetUnsafePtr();
Assert.AreEqual(value, *asUlong);
var mask = 0UL;
for (var i = 0; i < numBits; ++i)
{
mask |= (1UL << i);
}
ulong readValue;
Assert.IsTrue(reader.TryBeginRead(sizeof(ulong)));
using (var bitReader = reader.EnterBitwiseContext())
{
bitReader.ReadBits(out readValue, numBits);
}
Assert.AreEqual(value & mask, readValue);
}
}
[Test]
public unsafe void TestReadingMultipleBytesToLongsMisaligned([Range(1U, 63U)] uint numBits)
{
ulong value = 0b01010101_10101010_01010101_10101010_01010101_10101010_01010101_10101010;
var reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong));
using (reader)
{
ulong* asUlong = (ulong*)reader.GetUnsafePtr();
Assert.AreEqual(value, *asUlong);
var mask = 0UL;
for (var i = 0; i < numBits; ++i)
{
mask |= (1UL << i);
}
ulong readValue;
Assert.IsTrue(reader.TryBeginRead(sizeof(ulong)));
using (var bitReader = reader.EnterBitwiseContext())
{
bitReader.ReadBit(out bool unused);
bitReader.ReadBits(out readValue, numBits);
}
Assert.AreEqual((value >> 1) & mask, readValue);
}
}
[Test]
public unsafe void TestReadingBitsThrowsIfTryBeginReadNotCalled()
{
var nativeArray = new NativeArray<byte>(4, Allocator.Temp);
var reader = new FastBufferReader(nativeArray, Allocator.Temp);
nativeArray.Dispose();
using (reader)
{
int* asInt = (int*)reader.GetUnsafePtr();
*asInt = 0b11111111_00001010_10101011;
Assert.Throws<OverflowException>(() =>
{
using var bitReader = reader.EnterBitwiseContext();
bitReader.ReadBit(out bool b);
});
Assert.Throws<OverflowException>(() =>
{
using var bitReader = reader.EnterBitwiseContext();
bitReader.ReadBits(out byte b, 1);
});
Assert.Throws<OverflowException>(() =>
{
using var bitReader = reader.EnterBitwiseContext();
bitReader.ReadBits(out ulong ul, 1);
});
Assert.AreEqual(0, reader.Position);
Assert.Throws<OverflowException>(() =>
{
Assert.IsTrue(reader.TryBeginRead(1));
using var bitReader = reader.EnterBitwiseContext();
ulong ul;
try
{
bitReader.ReadBits(out ul, 4);
bitReader.ReadBits(out ul, 4);
}
catch (OverflowException)
{
Assert.Fail("Overflow exception was thrown too early.");
throw;
}
bitReader.ReadBits(out ul, 4);
});
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67df11865abcd5843a4e142cf6bbd901
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,333 @@
using System;
using NUnit.Framework;
using Unity.Collections;
namespace Unity.Netcode.EditorTests
{
public class BitWriterTests
{
[Test]
public unsafe void TestWritingOneBit()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
int* asInt = (int*)writer.GetUnsafePtr();
Assert.AreEqual(0, *asInt);
Assert.IsTrue(writer.TryBeginWrite(3));
using (var bitWriter = writer.EnterBitwiseContext())
{
bitWriter.WriteBit(true);
Assert.AreEqual(0b1, *asInt);
bitWriter.WriteBit(true);
Assert.AreEqual(0b11, *asInt);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
Assert.AreEqual(0b1011, *asInt);
bitWriter.WriteBit(false);
bitWriter.WriteBit(false);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
Assert.AreEqual(0b10001011, *asInt);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
Assert.AreEqual(0b1010_10001011, *asInt);
}
Assert.AreEqual(2, writer.Position);
Assert.AreEqual(0b1010_10001011, *asInt);
writer.WriteByte(0b11111111);
Assert.AreEqual(0b11111111_00001010_10001011, *asInt);
}
}
[Test]
public unsafe void TestTryBeginWriteBits()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
int* asInt = (int*)writer.GetUnsafePtr();
Assert.AreEqual(0, *asInt);
using (var bitWriter = writer.EnterBitwiseContext())
{
Assert.Throws<InvalidOperationException>(() => writer.TryBeginWrite(1));
Assert.Throws<InvalidOperationException>(() => writer.TryBeginWriteValue(1));
Assert.IsTrue(bitWriter.TryBeginWriteBits(1));
bitWriter.WriteBit(true);
Assert.AreEqual(0b1, *asInt);
// Can't use Assert.Throws() because ref struct BitWriter can't be captured in a lambda
try
{
bitWriter.WriteBit(true);
}
catch (OverflowException)
{
// Should get called here.
}
Assert.IsTrue(bitWriter.TryBeginWriteBits(3));
bitWriter.WriteBit(true);
Assert.AreEqual(0b11, *asInt);
bitWriter.WriteBit(false);
bitWriter.WriteBit(true);
Assert.AreEqual(0b1011, *asInt);
try
{
bitWriter.WriteBits(0b11111111, 4);
}
catch (OverflowException)
{
// Should get called here.
}
try
{
bitWriter.WriteBits(0b11111111, 1);
}
catch (OverflowException)
{
// Should get called here.
}
Assert.IsTrue(bitWriter.TryBeginWriteBits(3));
try
{
bitWriter.WriteBits(0b11111111, 4);
}
catch (OverflowException)
{
// Should get called here.
}
Assert.IsTrue(bitWriter.TryBeginWriteBits(4));
bitWriter.WriteBits(0b11111010, 3);
Assert.AreEqual(0b00101011, *asInt);
Assert.IsTrue(bitWriter.TryBeginWriteBits(5));
bitWriter.WriteBits(0b11110101, 5);
Assert.AreEqual(0b1010_10101011, *asInt);
}
Assert.AreEqual(2, writer.Position);
Assert.AreEqual(0b1010_10101011, *asInt);
Assert.IsTrue(writer.TryBeginWrite(1));
writer.WriteByte(0b11111111);
Assert.AreEqual(0b11111111_00001010_10101011, *asInt);
Assert.IsTrue(writer.TryBeginWrite(1));
writer.WriteByte(0b00000000);
Assert.AreEqual(0b11111111_00001010_10101011, *asInt);
Assert.IsFalse(writer.TryBeginWrite(1));
using (var bitWriter = writer.EnterBitwiseContext())
{
Assert.IsFalse(bitWriter.TryBeginWriteBits(1));
}
}
}
[Test]
public unsafe void TestWritingMultipleBits()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
int* asInt = (int*)writer.GetUnsafePtr();
Assert.AreEqual(0, *asInt);
Assert.IsTrue(writer.TryBeginWrite(3));
using (var bitWriter = writer.EnterBitwiseContext())
{
bitWriter.WriteBits(0b11111111, 1);
Assert.AreEqual(0b1, *asInt);
bitWriter.WriteBits(0b11111111, 1);
Assert.AreEqual(0b11, *asInt);
bitWriter.WriteBits(0b11111110, 2);
Assert.AreEqual(0b1011, *asInt);
bitWriter.WriteBits(0b11111000, 4);
Assert.AreEqual(0b10001011, *asInt);
bitWriter.WriteBits(0b11111010, 4);
Assert.AreEqual(0b1010_10001011, *asInt);
}
Assert.AreEqual(2, writer.Position);
Assert.AreEqual(0b1010_10001011, *asInt);
writer.WriteByte(0b11111111);
Assert.AreEqual(0b11111111_00001010_10001011, *asInt);
}
}
[Test]
public unsafe void TestWritingMultipleBitsFromLongs()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
int* asInt = (int*)writer.GetUnsafePtr();
Assert.AreEqual(0, *asInt);
Assert.IsTrue(writer.TryBeginWrite(3));
using (var bitWriter = writer.EnterBitwiseContext())
{
bitWriter.WriteBits(0b11111111UL, 1);
Assert.AreEqual(0b1, *asInt);
bitWriter.WriteBits(0b11111111UL, 1);
Assert.AreEqual(0b11, *asInt);
bitWriter.WriteBits(0b11111110UL, 2);
Assert.AreEqual(0b1011, *asInt);
bitWriter.WriteBits(0b11111000UL, 4);
Assert.AreEqual(0b10001011, *asInt);
bitWriter.WriteBits(0b11111010UL, 4);
Assert.AreEqual(0b1010_10001011, *asInt);
}
Assert.AreEqual(2, writer.Position);
Assert.AreEqual(0b1010_10001011, *asInt);
writer.WriteByte(0b11111111);
Assert.AreEqual(0b11111111_00001010_10001011, *asInt);
}
}
[Test]
public unsafe void TestWritingMultipleBytesFromLongs([Range(1U, 64U)] uint numBits)
{
var writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp);
using (writer)
{
ulong* asUlong = (ulong*)writer.GetUnsafePtr();
Assert.AreEqual(0, *asUlong);
var mask = 0UL;
for (var i = 0; i < numBits; ++i)
{
mask |= (1UL << i);
}
ulong value = 0xFFFFFFFFFFFFFFFF;
Assert.IsTrue(writer.TryBeginWrite(sizeof(ulong)));
using (var bitWriter = writer.EnterBitwiseContext())
{
bitWriter.WriteBits(value, numBits);
}
Assert.AreEqual(value & mask, *asUlong);
}
}
[Test]
public unsafe void TestWritingMultipleBytesFromLongsMisaligned([Range(1U, 63U)] uint numBits)
{
var writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp);
using (writer)
{
ulong* asUlong = (ulong*)writer.GetUnsafePtr();
Assert.AreEqual(0, *asUlong);
var mask = 0UL;
for (var i = 0; i < numBits; ++i)
{
mask |= (1UL << i);
}
ulong value = 0xFFFFFFFFFFFFFFFF;
Assert.IsTrue(writer.TryBeginWrite(sizeof(ulong)));
using (var bitWriter = writer.EnterBitwiseContext())
{
bitWriter.WriteBit(false);
bitWriter.WriteBits(value, numBits);
}
Assert.AreEqual(value & mask, *asUlong >> 1);
}
}
[Test]
public unsafe void TestWritingBitsThrowsIfTryBeginWriteNotCalled()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
int* asInt = (int*)writer.GetUnsafePtr();
Assert.AreEqual(0, *asInt);
Assert.Throws<OverflowException>(() =>
{
using var bitWriter = writer.EnterBitwiseContext();
bitWriter.WriteBit(true);
});
Assert.Throws<OverflowException>(() =>
{
using var bitWriter = writer.EnterBitwiseContext();
bitWriter.WriteBit(false);
});
Assert.Throws<OverflowException>(() =>
{
using var bitWriter = writer.EnterBitwiseContext();
bitWriter.WriteBits(0b11111111, 1);
});
Assert.Throws<OverflowException>(() =>
{
using var bitWriter = writer.EnterBitwiseContext();
bitWriter.WriteBits(0b11111111UL, 1);
});
Assert.AreEqual(0, writer.Position);
Assert.AreEqual(0, *asInt);
writer.WriteByteSafe(0b11111111);
Assert.AreEqual(0b11111111, *asInt);
Assert.Throws<OverflowException>(() =>
{
Assert.IsTrue(writer.TryBeginWrite(1));
using var bitWriter = writer.EnterBitwiseContext();
try
{
bitWriter.WriteBits(0b11111111UL, 4);
bitWriter.WriteBits(0b11111111UL, 4);
}
catch (OverflowException)
{
Assert.Fail("Overflow exception was thrown too early.");
}
bitWriter.WriteBits(0b11111111UL, 1);
});
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fed657e0516a72f469fbf886e3e5149a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,353 @@
using System;
using NUnit.Framework;
using Unity.Collections;
using Random = System.Random;
namespace Unity.Netcode.EditorTests
{
public class BufferSerializerTests
{
[Test]
public void TestIsReaderIsWriter()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
Assert.IsFalse(serializer.IsReader);
Assert.IsTrue(serializer.IsWriter);
}
byte[] readBuffer = new byte[4];
var reader = new FastBufferReader(readBuffer, Allocator.Temp);
using (reader)
{
var serializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
Assert.IsTrue(serializer.IsReader);
Assert.IsFalse(serializer.IsWriter);
}
}
[Test]
public unsafe void TestGetUnderlyingStructs()
{
var writer = new FastBufferWriter(4, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
FastBufferWriter underlyingWriter = serializer.GetFastBufferWriter();
Assert.IsTrue(underlyingWriter.Handle == writer.Handle);
// Can't use Assert.Throws() because ref structs can't be passed into lambdas.
try
{
serializer.GetFastBufferReader();
}
catch (InvalidOperationException)
{
// pass
}
}
byte[] readBuffer = new byte[4];
var reader = new FastBufferReader(readBuffer, Allocator.Temp);
using (reader)
{
var serializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
FastBufferReader underlyingReader = serializer.GetFastBufferReader();
Assert.IsTrue(underlyingReader.Handle == reader.Handle);
// Can't use Assert.Throws() because ref structs can't be passed into lambdas.
try
{
serializer.GetFastBufferWriter();
}
catch (InvalidOperationException)
{
// pass
}
}
}
[Test]
public void TestSerializingValues()
{
var random = new Random();
int value = random.Next();
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
serializer.SerializeValue(ref value);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var deserializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
int readValue = 0;
deserializer.SerializeValue(ref readValue);
Assert.AreEqual(value, readValue);
}
}
}
[Test]
public void TestSerializingBytes()
{
var random = new Random();
byte value = (byte)random.Next();
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
serializer.SerializeValue(ref value);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var deserializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
byte readValue = 0;
deserializer.SerializeValue(ref readValue);
Assert.AreEqual(value, readValue);
}
}
}
[Test]
public void TestSerializingArrays()
{
var random = new Random();
int[] value = { random.Next(), random.Next(), random.Next() };
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
serializer.SerializeValue(ref value);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var deserializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
int[] readValue = null;
deserializer.SerializeValue(ref readValue);
Assert.AreEqual(value, readValue);
}
}
}
[Test]
public void TestSerializingStrings([Values] bool oneBytChars)
{
string value = "I am a test string";
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
serializer.SerializeValue(ref value, oneBytChars);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var deserializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
string readValue = null;
deserializer.SerializeValue(ref readValue, oneBytChars);
Assert.AreEqual(value, readValue);
}
}
}
[Test]
public void TestSerializingValuesPreChecked()
{
var random = new Random();
int value = random.Next();
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
try
{
serializer.SerializeValuePreChecked(ref value);
}
catch (OverflowException)
{
// Pass
}
Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value)));
serializer.SerializeValuePreChecked(ref value);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var deserializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
int readValue = 0;
try
{
deserializer.SerializeValuePreChecked(ref readValue);
}
catch (OverflowException)
{
// Pass
}
Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value)));
deserializer.SerializeValuePreChecked(ref readValue);
Assert.AreEqual(value, readValue);
}
}
}
[Test]
public void TestSerializingBytesPreChecked()
{
var random = new Random();
byte value = (byte)random.Next();
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
try
{
serializer.SerializeValuePreChecked(ref value);
}
catch (OverflowException)
{
// Pass
}
Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value)));
serializer.SerializeValuePreChecked(ref value);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var deserializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
byte readValue = 0;
try
{
deserializer.SerializeValuePreChecked(ref readValue);
}
catch (OverflowException)
{
// Pass
}
Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value)));
deserializer.SerializeValuePreChecked(ref readValue);
Assert.AreEqual(value, readValue);
}
}
}
[Test]
public void TestSerializingArraysPreChecked()
{
var random = new Random();
int[] value = { random.Next(), random.Next(), random.Next() };
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
try
{
serializer.SerializeValuePreChecked(ref value);
}
catch (OverflowException)
{
// Pass
}
Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value)));
serializer.SerializeValuePreChecked(ref value);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var deserializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
int[] readValue = null;
try
{
deserializer.SerializeValuePreChecked(ref readValue);
}
catch (OverflowException)
{
// Pass
}
Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value)));
deserializer.SerializeValuePreChecked(ref readValue);
Assert.AreEqual(value, readValue);
}
}
}
[Test]
public void TestSerializingStringsPreChecked([Values] bool oneBytChars)
{
string value = "I am a test string";
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
var serializer =
new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
try
{
serializer.SerializeValuePreChecked(ref value, oneBytChars);
}
catch (OverflowException)
{
// Pass
}
Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars)));
serializer.SerializeValuePreChecked(ref value, oneBytChars);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var deserializer =
new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
string readValue = null;
try
{
deserializer.SerializeValuePreChecked(ref readValue, oneBytChars);
}
catch (OverflowException)
{
// Pass
}
Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars)));
deserializer.SerializeValuePreChecked(ref readValue, oneBytChars);
Assert.AreEqual(value, readValue);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0cf899c97866c76498b71585a61a8142
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b50db056cd7443b4eb2e00b603d4c15c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,830 @@
using System;
using NUnit.Framework;
using Unity.Collections;
using UnityEngine;
using Random = System.Random;
namespace Unity.Netcode.EditorTests
{
public class FastBufferReaderTests : BaseFastBufferReaderWriterTest
{
#region Common Checks
private void WriteCheckBytes(FastBufferWriter writer, int writeSize, string failMessage = "")
{
Assert.IsTrue(writer.TryBeginWrite(2), "Writer denied write permission");
writer.WriteValue((byte)0x80);
Assert.AreEqual(writeSize + 1, writer.Position, failMessage);
Assert.AreEqual(writeSize + 1, writer.Length, failMessage);
writer.WriteValue((byte)0xFF);
Assert.AreEqual(writeSize + 2, writer.Position, failMessage);
Assert.AreEqual(writeSize + 2, writer.Length, failMessage);
}
private void VerifyCheckBytes(FastBufferReader reader, int checkPosition, string failMessage = "")
{
reader.Seek(checkPosition);
reader.TryBeginRead(2);
reader.ReadByte(out byte value);
Assert.AreEqual(0x80, value, failMessage);
reader.ReadByte(out value);
Assert.AreEqual(0xFF, value, failMessage);
}
private void VerifyPositionAndLength(FastBufferReader reader, int length, string failMessage = "")
{
Assert.AreEqual(0, reader.Position, failMessage);
Assert.AreEqual(length, reader.Length, failMessage);
}
private FastBufferReader CommonChecks<T>(FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T : unmanaged
{
WriteCheckBytes(writer, writeSize, failMessage);
var reader = new FastBufferReader(writer, Allocator.Temp);
VerifyPositionAndLength(reader, writer.Length, failMessage);
VerifyCheckBytes(reader, writeSize, failMessage);
reader.Seek(0);
return reader;
}
#endregion
#region Generic Checks
protected override unsafe void RunTypeTest<T>(T valueToTest)
{
var writeSize = FastBufferWriter.GetWriteSize(valueToTest);
Assert.AreEqual(sizeof(T), writeSize);
var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp);
using (writer)
{
Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission");
var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}";
writer.WriteValue(valueToTest);
var reader = CommonChecks(writer, valueToTest, writeSize, failMessage);
using (reader)
{
Assert.IsTrue(reader.TryBeginRead(FastBufferWriter.GetWriteSize<T>()));
reader.ReadValue(out T result);
Assert.AreEqual(valueToTest, result);
}
}
}
protected override unsafe void RunTypeTestSafe<T>(T valueToTest)
{
var writeSize = FastBufferWriter.GetWriteSize(valueToTest);
var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp);
using (writer)
{
Assert.AreEqual(sizeof(T), writeSize);
var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}";
writer.WriteValueSafe(valueToTest);
var reader = CommonChecks(writer, valueToTest, writeSize, failMessage);
using (reader)
{
reader.ReadValueSafe(out T result);
Assert.AreEqual(valueToTest, result);
}
}
}
private void VerifyArrayEquality<T>(T[] value, T[] compareValue, int offset) where T : unmanaged
{
Assert.AreEqual(value.Length, compareValue.Length);
for (var i = 0; i < value.Length; ++i)
{
Assert.AreEqual(value[i], compareValue[i]);
}
}
protected override unsafe void RunTypeArrayTest<T>(T[] valueToTest)
{
var writeSize = FastBufferWriter.GetWriteSize(valueToTest);
var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp);
using (writer)
{
Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize);
Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission");
writer.WriteValue(valueToTest);
WriteCheckBytes(writer, writeSize);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
VerifyPositionAndLength(reader, writer.Length);
Assert.IsTrue(reader.TryBeginRead(writeSize));
reader.ReadValue(out T[] result);
VerifyArrayEquality(valueToTest, result, 0);
VerifyCheckBytes(reader, writeSize);
}
}
}
protected override unsafe void RunTypeArrayTestSafe<T>(T[] valueToTest)
{
var writeSize = FastBufferWriter.GetWriteSize(valueToTest);
var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp);
using (writer)
{
Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize);
writer.WriteValueSafe(valueToTest);
WriteCheckBytes(writer, writeSize);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
VerifyPositionAndLength(reader, writer.Length);
reader.ReadValueSafe(out T[] result);
VerifyArrayEquality(valueToTest, result, 0);
VerifyCheckBytes(reader, writeSize);
}
}
}
#endregion
#region Tests
[Test]
public void GivenFastBufferWriterContainingValue_WhenReadingUnmanagedType_ValueMatchesWhatWasWritten(
[Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint),
typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double),
typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum),
typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4),
typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))]
Type testType,
[Values] WriteType writeType)
{
BaseTypeTest(testType, writeType);
}
[Test]
public void GivenFastBufferWriterContainingValue_WhenReadingArrayOfUnmanagedElementType_ValueMatchesWhatWasWritten(
[Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint),
typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double),
typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum),
typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4),
typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))]
Type testType,
[Values] WriteType writeType)
{
BaseArrayTypeTest(testType, writeType);
}
[TestCase(false, WriteType.WriteDirect)]
[TestCase(false, WriteType.WriteSafe)]
[TestCase(true, WriteType.WriteDirect)]
[TestCase(true, WriteType.WriteSafe)]
public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesWhatWasWritten(bool oneByteChars, WriteType writeType)
{
string valueToTest = "Hello, I am a test string!";
var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, oneByteChars);
var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp);
using (writer)
{
switch (writeType)
{
case WriteType.WriteDirect:
Assert.IsTrue(writer.TryBeginWrite(serializedValueSize + 2), "Writer denied write permission");
writer.WriteValue(valueToTest, oneByteChars);
break;
case WriteType.WriteSafe:
writer.WriteValueSafe(valueToTest, oneByteChars);
break;
}
WriteCheckBytes(writer, serializedValueSize);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
VerifyPositionAndLength(reader, writer.Length);
string result = null;
switch (writeType)
{
case WriteType.WriteDirect:
Assert.IsTrue(reader.TryBeginRead(serializedValueSize + 2), "Reader denied read permission");
reader.ReadValue(out result, oneByteChars);
break;
case WriteType.WriteSafe:
reader.ReadValueSafe(out result, oneByteChars);
break;
}
Assert.AreEqual(valueToTest, result);
VerifyCheckBytes(reader, serializedValueSize);
}
}
}
[TestCase(1, 0)]
[TestCase(2, 0)]
[TestCase(3, 0)]
[TestCase(4, 0)]
[TestCase(5, 0)]
[TestCase(6, 0)]
[TestCase(7, 0)]
[TestCase(8, 0)]
[TestCase(1, 1)]
[TestCase(2, 1)]
[TestCase(3, 1)]
[TestCase(4, 1)]
[TestCase(5, 1)]
[TestCase(6, 1)]
[TestCase(7, 1)]
[TestCase(1, 2)]
[TestCase(2, 2)]
[TestCase(3, 2)]
[TestCase(4, 2)]
[TestCase(5, 2)]
[TestCase(6, 2)]
[TestCase(1, 3)]
[TestCase(2, 3)]
[TestCase(3, 3)]
[TestCase(4, 3)]
[TestCase(5, 3)]
[TestCase(1, 4)]
[TestCase(2, 4)]
[TestCase(3, 4)]
[TestCase(4, 4)]
[TestCase(1, 5)]
[TestCase(2, 5)]
[TestCase(3, 5)]
[TestCase(1, 6)]
[TestCase(2, 6)]
[TestCase(1, 7)]
public void GivenFastBufferWriterContainingValue_WhenReadingPartialValue_ValueMatchesWhatWasWritten(int count, int offset)
{
var random = new Random();
var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next();
var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp);
using (writer)
{
Assert.IsTrue(writer.TryBeginWrite(count + 2), "Writer denied write permission");
writer.WritePartialValue(valueToTest, count, offset);
var failMessage = $"TestReadingPartialValues failed with value {valueToTest}";
WriteCheckBytes(writer, count, failMessage);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
VerifyPositionAndLength(reader, writer.Length, failMessage);
Assert.IsTrue(reader.TryBeginRead(count + 2), "Reader denied read permission");
ulong mask = 0;
for (var i = 0; i < count; ++i)
{
mask = (mask << 8) | 0b11111111;
}
mask <<= (offset * 8);
reader.ReadPartialValue(out ulong result, count, offset);
Assert.AreEqual(valueToTest & mask, result & mask, failMessage);
VerifyCheckBytes(reader, count, failMessage);
}
}
}
[Test]
public unsafe void GivenFastBufferReaderInitializedFromFastBufferWriterContainingValue_WhenCallingToArray_ReturnedArrayMatchesContentOfWriter()
{
var testStruct = GetTestStruct();
var requiredSize = FastBufferWriter.GetWriteSize(testStruct);
var writer = new FastBufferWriter(requiredSize, Allocator.Temp);
using (writer)
{
writer.TryBeginWrite(requiredSize);
writer.WriteValue(testStruct);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
var array = reader.ToArray();
var underlyingArray = writer.GetUnsafePtr();
for (var i = 0; i < array.Length; ++i)
{
Assert.AreEqual(array[i], underlyingArray[i]);
}
}
}
}
[Test]
public void WhenCallingReadByteWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
Assert.Throws<OverflowException>(() => { emptyReader.ReadByte(out byte b); });
}
}
[Test]
public void WhenCallingReadBytesWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
byte[] b = { 0, 1, 2 };
using (emptyReader)
{
Assert.Throws<OverflowException>(() => { emptyReader.ReadBytes(ref b, 3); });
}
}
[Test]
public void WhenCallingReadValueWithUnmanagedTypeWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
Assert.Throws<OverflowException>(() => { emptyReader.ReadValue(out int i); });
}
}
[Test]
public void WhenCallingReadValueWithByteArrayWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
Assert.Throws<OverflowException>(() => { emptyReader.ReadValue(out byte[] b); });
}
}
[Test]
public void WhenCallingReadValueWithStringWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
Assert.Throws<OverflowException>(() => { emptyReader.ReadValue(out string s); });
}
}
[Test]
public void WhenCallingReadValueAfterCallingTryBeginWriteWithTooFewBytes_OverflowExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(sizeof(int) - 1);
Assert.Throws<OverflowException>(() => { emptyReader.ReadValue(out int i); });
}
}
[Test]
public void WhenCallingReadBytePastBoundaryMarkedByTryBeginWrite_OverflowExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(sizeof(int) - 1);
emptyReader.ReadByte(out byte b);
emptyReader.ReadByte(out b);
emptyReader.ReadByte(out b);
Assert.Throws<OverflowException>(() => { emptyReader.ReadByte(out b); });
}
}
[Test]
public void WhenCallingReadByteDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadByte(out byte b); });
}
}
[Test]
public void WhenCallingReadBytesDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
byte[] b = { 0, 1, 2 };
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadBytes(ref b, 3); });
}
}
[Test]
public void WhenCallingReadValueWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadValue(out int i); });
}
}
[Test]
public void WhenCallingReadValueWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadValue(out byte[] b); });
}
}
[Test]
public void WhenCallingReadValueWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadValue(out string s); });
}
}
[Test]
public void WhenCallingReadByteSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadByteSafe(out byte b); });
}
}
[Test]
public void WhenCallingReadBytesSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
byte[] b = { 0, 1, 2 };
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadBytesSafe(ref b, 3); });
}
}
[Test]
public void WhenCallingReadValueSafeWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadValueSafe(out int i); });
}
}
[Test]
public void WhenCallingReadValueSafeWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadValueSafe(out byte[] b); });
}
}
[Test]
public void WhenCallingReadValueSafeWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
using var context = emptyReader.EnterBitwiseContext();
Assert.Throws<InvalidOperationException>(() => { emptyReader.ReadValueSafe(out string s); });
}
}
[Test]
public void WhenCallingReadByteAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
emptyReader.ReadByte(out byte theByte);
}
}
[Test]
public void WhenCallingReadBytesAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
byte[] theBytes = { 0, 1, 2 };
emptyReader.ReadBytes(ref theBytes, 3);
}
}
[Test]
public void WhenCallingReadValueWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
emptyReader.ReadValue(out int i);
}
}
[Test]
public void WhenCallingReadValueWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
emptyReader.ReadValue(out byte[] theBytes);
}
}
[Test]
public void WhenCallingReadValueWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
emptyReader.ReadValue(out string s);
}
}
[Test]
public void WhenCallingReadByteSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
emptyReader.ReadByteSafe(out byte theByte);
}
}
[Test]
public void WhenCallingReadBytesSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
byte[] theBytes = { 0, 1, 2 };
emptyReader.ReadBytesSafe(ref theBytes, 3);
}
}
[Test]
public void WhenCallingReadValueSafeWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
emptyReader.ReadValueSafe(out int i);
}
}
[Test]
public void WhenCallingReadValueSafeWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
emptyReader.ReadValueSafe(out byte[] theBytes);
}
}
[Test]
public void WhenCallingReadValueSafeWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
using (var context = emptyReader.EnterBitwiseContext())
{
context.ReadBit(out bool theBit);
}
emptyReader.ReadValueSafe(out string s);
}
}
[Test]
public void WhenCallingTryBeginRead_TheAllowedReadPositionIsMarkedRelativeToCurrentPosition()
{
var nativeArray = new NativeArray<byte>(100, Allocator.Temp);
var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp);
using (emptyReader)
{
emptyReader.TryBeginRead(100);
emptyReader.ReadByte(out byte b);
emptyReader.TryBeginRead(1);
emptyReader.ReadByte(out b);
Assert.Throws<OverflowException>(() => { emptyReader.ReadByte(out byte b); });
}
}
[Test]
public void WhenReadingAfterSeeking_TheNewReadComesFromTheCorrectPosition()
{
var writer = new FastBufferWriter(100, Allocator.Temp);
using (writer)
{
writer.WriteByteSafe(1);
writer.WriteByteSafe(3);
writer.WriteByteSafe(2);
writer.WriteByteSafe(5);
writer.WriteByteSafe(4);
writer.WriteByteSafe(0);
var reader = new FastBufferReader(writer, Allocator.Temp);
using (reader)
{
reader.Seek(5);
reader.ReadByteSafe(out byte b);
Assert.AreEqual(reader.Position, 6);
Assert.AreEqual(reader.Length, writer.Length);
Assert.AreEqual(0, b);
reader.Seek(0);
reader.ReadByteSafe(out b);
Assert.AreEqual(reader.Position, 1);
Assert.AreEqual(reader.Length, writer.Length);
Assert.AreEqual(1, b);
reader.Seek(10);
Assert.AreEqual(reader.Position, writer.Length);
Assert.AreEqual(reader.Length, writer.Length);
reader.Seek(2);
reader.ReadByteSafe(out b);
Assert.AreEqual(2, b);
reader.Seek(1);
reader.ReadByteSafe(out b);
Assert.AreEqual(3, b);
reader.Seek(4);
reader.ReadByteSafe(out b);
Assert.AreEqual(4, b);
reader.Seek(3);
reader.ReadByteSafe(out b);
Assert.AreEqual(5, b);
Assert.AreEqual(reader.Position, 4);
Assert.AreEqual(reader.Length, writer.Length);
}
}
}
[Test]
public unsafe void WhenCallingTryBeginReadInternal_AllowedReadPositionDoesNotMoveBackward()
{
var reader = new FastBufferReader(new NativeArray<byte>(100, Allocator.Temp), Allocator.Temp);
using (reader)
{
reader.TryBeginRead(25);
reader.TryBeginReadInternal(5);
Assert.AreEqual(reader.Handle->AllowedReadMark, 25);
}
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2881f8138b479c34389b76687e5307ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1cef42b60935e29469ed1404fb30ba2d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,73 @@
using NUnit.Framework;
namespace Unity.Netcode.EditorTests
{
public class SnapshotRttTests
{
private const double k_Epsilon = 0.0001;
[Test]
public void TestBasicRtt()
{
var snapshot = new SnapshotSystem(default);
var client1 = snapshot.GetConnectionRtt(0);
client1.NotifySend(0, 0.0);
client1.NotifySend(1, 10.0);
client1.NotifyAck(1, 15.0);
client1.NotifySend(2, 20.0);
client1.NotifySend(3, 30.0);
client1.NotifySend(4, 32.0);
client1.NotifyAck(4, 38.0);
client1.NotifyAck(3, 40.0);
ConnectionRtt.Rtt ret = client1.GetRtt();
Assert.True(ret.AverageSec < 7.0 + k_Epsilon);
Assert.True(ret.AverageSec > 7.0 - k_Epsilon);
Assert.True(ret.WorstSec < 10.0 + k_Epsilon);
Assert.True(ret.WorstSec > 10.0 - k_Epsilon);
Assert.True(ret.BestSec < 5.0 + k_Epsilon);
Assert.True(ret.BestSec > 5.0 - k_Epsilon);
// note: `last` latency is latest received Ack, not latest sent sequence.
Assert.True(ret.LastSec < 10.0 + k_Epsilon);
Assert.True(ret.LastSec > 10.0 - k_Epsilon);
}
[Test]
public void TestEdgeCasesRtt()
{
var snapshot = new SnapshotSystem(NetworkManager.Singleton);
var client1 = snapshot.GetConnectionRtt(0);
var iterationCount = NetworkConfig.RttWindowSize * 3;
var extraCount = NetworkConfig.RttWindowSize * 2;
// feed in some messages
for (var iteration = 0; iteration < iterationCount; iteration++)
{
client1.NotifySend(iteration, 25.0 * iteration);
}
// ack some random ones in there (1 out of each 9), always 7.0 later
for (var iteration = 0; iteration < iterationCount; iteration += 9)
{
client1.NotifyAck(iteration, 25.0 * iteration + 7.0);
}
// ack some unused key, to check it doesn't throw off the values
for (var iteration = iterationCount; iteration < iterationCount + extraCount; iteration++)
{
client1.NotifyAck(iteration, 42.0);
}
ConnectionRtt.Rtt ret = client1.GetRtt();
Assert.True(ret.AverageSec < 7.0 + k_Epsilon);
Assert.True(ret.AverageSec > 7.0 - k_Epsilon);
Assert.True(ret.WorstSec < 7.0 + k_Epsilon);
Assert.True(ret.WorstSec > 7.0 - k_Epsilon);
Assert.True(ret.BestSec < 7.0 + k_Epsilon);
Assert.True(ret.BestSec > 7.0 - k_Epsilon);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a05afab7f08d44c07b2c5e144ba0b45a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
using NUnit.Framework;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
public class StartStopTests
{
private NetworkManager m_NetworkManager;
[SetUp]
public void Setup()
{
// Create the reusable NetworkManager
m_NetworkManager = new GameObject(nameof(NetworkManager)).AddComponent<NetworkManager>();
var transport = m_NetworkManager.gameObject.AddComponent<DummyTransport>();
m_NetworkManager.NetworkConfig = new NetworkConfig()
{
NetworkTransport = transport
};
}
[Test]
public void TestStopAndRestartForExceptions()
{
m_NetworkManager.StartServer();
m_NetworkManager.Shutdown();
m_NetworkManager.StartServer();
m_NetworkManager.Shutdown();
}
[Test]
public void TestStartupServerState()
{
m_NetworkManager.StartServer();
Assert.True(m_NetworkManager.IsServer);
Assert.False(m_NetworkManager.IsClient);
Assert.False(m_NetworkManager.IsHost);
m_NetworkManager.Shutdown();
}
[Test]
public void TestFlagShutdown()
{
m_NetworkManager.StartServer();
m_NetworkManager.Shutdown();
Assert.False(m_NetworkManager.IsServer);
Assert.False(m_NetworkManager.IsClient);
Assert.False(m_NetworkManager.IsHost);
}
[TearDown]
public void Teardown()
{
// Cleanup
Object.DestroyImmediate(m_NetworkManager.gameObject);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e8e36047cb5542bcaade2a9a8746d713
timeCreated: 1630336158

8
Tests/Editor/Timing.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ff3d73d1a9b7596419b0f389d0219f31
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,182 @@
using System;
using NUnit.Framework;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
/// <summary>
/// Tests for running a <see cref="NetworkTimeSystem"/> as a client.
/// </summary>
public class ClientNetworkTimeSystemTests
{
private const double k_AcceptableRttOffset = 0.03d; // 30ms offset is fine
/// <summary>
/// Tests whether time is stable if RTT is stable.
/// </summary>
[Test]
public void StableRttTest()
{
double receivedServerTime = 2;
var timeSystem = new NetworkTimeSystem(0.05d, 0.05d, 0.1d);
timeSystem.Reset(receivedServerTime, 0.15);
var tickSystem = new NetworkTickSystem(60, timeSystem.LocalTime, timeSystem.ServerTime);
Assert.True(timeSystem.LocalTime > 2);
var steps = TimingTestHelper.GetRandomTimeSteps(100f, 0.01f, 0.1f, 42);
var rttSteps = TimingTestHelper.GetRandomTimeSteps(1000f, 0.095f, 0.105f, 42); // 10ms jitter
// run for a while so that we reach regular RTT offset
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
{
// sync network stats
receivedServerTime += steps[step];
timeSystem.Sync(receivedServerTime, rttSteps[step]);
});
// check how we close we are to target time.
var expectedRtt = 0.1d;
var offsetToTarget = (timeSystem.LocalTime - timeSystem.ServerTime) - expectedRtt - timeSystem.ServerBufferSec - timeSystem.LocalBufferSec;
Debug.Log($"offset to target time after running for a while: {offsetToTarget}");
Assert.IsTrue(Math.Abs(offsetToTarget) < k_AcceptableRttOffset);
// run again, test that we never need to speed up or slow down under stable RTT
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
{
// sync network stats
receivedServerTime += steps[step];
timeSystem.Sync(receivedServerTime, rttSteps[step]);
});
// check again to ensure we are still close to the target
var newOffsetToTarget = (timeSystem.LocalTime - timeSystem.ServerTime) - expectedRtt - timeSystem.ServerBufferSec - timeSystem.LocalBufferSec;
Debug.Log($"offset to target time after running longer: {newOffsetToTarget}");
Assert.IsTrue(Math.Abs(newOffsetToTarget) < k_AcceptableRttOffset);
// difference between first and second offset should be minimal
var dif = offsetToTarget - newOffsetToTarget;
Assert.IsTrue(Math.Abs(dif) < 0.01d); // less than 10ms
}
/// <summary>
/// Tests whether local time can speed up and slow down to catch up when RTT changes.
/// </summary>
[Test]
public void RttCatchupSlowdownTest()
{
double receivedServerTime = 2;
var timeSystem = new NetworkTimeSystem(0.05d, 0.05d, 0.1d);
timeSystem.Reset(receivedServerTime, 0.15);
var tickSystem = new NetworkTickSystem(60, timeSystem.LocalTime, timeSystem.ServerTime);
var steps = TimingTestHelper.GetRandomTimeSteps(100f, 0.01f, 0.1f, 42);
var rttSteps = TimingTestHelper.GetRandomTimeSteps(1000f, 0.095f, 0.105f, 42); // 10ms jitter
// run for a while so that we reach regular RTT offset
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
{
// sync network stats
receivedServerTime += steps[step];
timeSystem.Sync(receivedServerTime, rttSteps[step]);
});
// increase RTT to ~200ms from ~100ms
var rttSteps2 = TimingTestHelper.GetRandomTimeSteps(1000f, 0.195f, 0.205f, 42);
double unscaledLocalTime = timeSystem.LocalTime;
double unscaledServerTime = timeSystem.ServerTime;
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
{
// sync network stats
unscaledLocalTime += steps[step];
unscaledServerTime += steps[step];
receivedServerTime += steps[step];
timeSystem.Sync(receivedServerTime, rttSteps2[step]);
});
var totalLocalSpeedUpTime = timeSystem.LocalTime - unscaledLocalTime;
var totalServerSpeedUpTime = timeSystem.ServerTime - unscaledServerTime;
// speed up of 0.1f expected
Debug.Log($"Total local speed up time catch up: {totalLocalSpeedUpTime}");
Assert.True(Math.Abs(totalLocalSpeedUpTime - 0.1) < k_AcceptableRttOffset);
Assert.True(Math.Abs(totalServerSpeedUpTime) < k_AcceptableRttOffset); // server speedup/slowdowns should not be affected by RTT
// run again with RTT ~100ms and see whether we slow down by -0.1f
unscaledLocalTime = timeSystem.LocalTime;
unscaledServerTime = timeSystem.ServerTime;
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
{
// sync network stats
unscaledLocalTime += steps[step];
unscaledServerTime += steps[step];
receivedServerTime += steps[step];
timeSystem.Sync(receivedServerTime, rttSteps[step]);
});
totalLocalSpeedUpTime = timeSystem.LocalTime - unscaledLocalTime;
totalServerSpeedUpTime = timeSystem.ServerTime - unscaledServerTime;
// slow down of 0.1f expected
Debug.Log($"Total local speed up time slow down: {totalLocalSpeedUpTime}");
Assert.True(Math.Abs(totalLocalSpeedUpTime + 0.1) < k_AcceptableRttOffset);
Assert.True(Math.Abs(totalServerSpeedUpTime) < k_AcceptableRttOffset); // server speedup/slowdowns should not be affected by RTT
}
/// <summary>
/// Tests whether time resets when there is a huge spike in RTT and is able to stabilize again.
/// </summary>
[Test]
public void ResetTest()
{
double receivedServerTime = 2;
var timeSystem = new NetworkTimeSystem(0.05d, 0.05d, 0.1d);
timeSystem.Reset(receivedServerTime, 0.15);
var tickSystem = new NetworkTickSystem(60, timeSystem.LocalTime, timeSystem.ServerTime);
var steps = TimingTestHelper.GetRandomTimeSteps(100f, 0.01f, 0.1f, 42);
var rttSteps = TimingTestHelper.GetRandomTimeSteps(1000f, 0.095f, 0.105f, 42); // 10ms jitter
// run for a while so that we reach regular RTT offset
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step)
{
// sync network stats
receivedServerTime += steps[step];
timeSystem.Sync(receivedServerTime, rttSteps[step]);
});
// increase RTT to ~500ms from ~100ms
var rttSteps2 = TimingTestHelper.GetRandomTimeSteps(1000f, 0.495f, 0.505f, 42);
// run a single advance expect a hard rest
receivedServerTime += 1 / 60d;
timeSystem.Sync(receivedServerTime, 0.5);
bool reset = timeSystem.Advance(1 / 60d);
Assert.IsTrue(reset);
TimingTestHelper.ApplySteps(timeSystem, tickSystem, steps, delegate (int step, bool reset)
{
Assert.IsFalse(reset);
// sync network stats
receivedServerTime += steps[step];
timeSystem.Sync(receivedServerTime, rttSteps2[step]);
// after hard reset time should stay close to rtt
var expectedRtt = 0.5d;
Assert.IsTrue(Math.Abs((timeSystem.LocalTime - timeSystem.ServerTime) - expectedRtt - timeSystem.ServerBufferSec - timeSystem.LocalBufferSec) < k_AcceptableRttOffset);
});
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1cd95d37c9e3f904ba19b1fcf33123c5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using UnityEngine;
using Random = System.Random;
namespace Unity.Netcode.EditorTests
{
public class NetworkTimeTests
{
[Test]
[TestCase(0d, 0u)]
[TestCase(5d, 0u)]
[TestCase(-5d, 0u)]
public void TestFailCreateInvalidTime(double time, uint tickrate)
{
Assert.Throws<UnityEngine.Assertions.AssertionException>(() => new NetworkTime(tickrate, time));
}
[Test]
[TestCase(0d, 0f, 20u)]
[TestCase(0d, 0f, 30u)]
[TestCase(0d, 0f, 60u)]
[TestCase(201d, 201f, 20u)]
[TestCase(201d, 201f, 30u)]
[TestCase(201d, 201f, 60u)]
[TestCase(-4301d, -4301f, 20u)]
[TestCase(-4301d, -4301f, 30u)]
[TestCase(-4301d, -4301f, 60u)]
[TestCase(float.MaxValue, float.MaxValue, 20u)]
[TestCase(float.MaxValue, float.MaxValue, 30u)]
[TestCase(float.MaxValue, float.MaxValue, 60u)]
public void TestTimeAsFloat(double d, float f, uint tickRate)
{
var networkTime = new NetworkTime(tickRate, d);
Assert.True(Mathf.Approximately(networkTime.TimeAsFloat, f));
}
[Test]
[TestCase(53.55d, 53.5d, 10u)]
[TestCase(1013553.55d, 1013553.5d, 10u)]
[TestCase(0d, 0d, 10u)]
[TestCase(-27.41d, -27.5d, 10u)]
[TestCase(53.55d, 53.54d, 50u)]
[TestCase(1013553.55d, 1013553.54d, 50u)]
[TestCase(0d, 0d, 50u)]
[TestCase(-27.4133d, -27.42d, 50u)]
public void TestToFixedTime(double time, double expectedFixedTime, uint tickRate)
{
Assert.AreEqual(expectedFixedTime, new NetworkTime(tickRate, time).ToFixedTime().Time);
}
[Test]
[TestCase(34d, 0)]
[TestCase(17.32d, 0.2d / 60d)]
[TestCase(-42.44d, 1d / 60d - 0.4d / 60d)]
[TestCase(-6d, 0)]
[TestCase(int.MaxValue / 61d, 0.00082, 10d)] // Int.Max / 61 / (1/60) to get divisor then: Int.Max - divisor * 1 / 60
public void NetworkTimeCreate(double time, double tickOffset, double epsilon = 0.0001d)
{
var networkTime = new NetworkTime(60, time);
Assert.IsTrue(Approximately(time, networkTime.Time));
Assert.IsTrue(Approximately(networkTime.Tick * networkTime.FixedDeltaTime + networkTime.TickOffset, networkTime.Time, epsilon));
Assert.IsTrue(Approximately(networkTime.TickOffset, tickOffset));
}
[Test]
public void NetworkTimeDefault()
{
NetworkTime defaultTime = default;
Assert.IsTrue(defaultTime.Time == 0f);
}
[Test]
[TestCase(17.32d)]
[TestCase(34d)]
[TestCase(-42.4d)]
[TestCase(-6d)]
[TestCase(int.MaxValue / 61d)]
public void NetworkTimeAddFloatTest(double time)
{
double a = 34d;
double floatResultB = a + time;
var timeA = new NetworkTime(60, a);
NetworkTime timeB = timeA + time;
Assert.IsTrue(Approximately(floatResultB, timeB.Time));
}
[Test]
[TestCase(17.32d)]
[TestCase(34d)]
[TestCase(-42.4d)]
[TestCase(-6d)]
[TestCase(int.MaxValue / 61d)]
public void NetworkTimeSubFloatTest(double time)
{
double a = 34d;
double floatResultB = a - time;
var timeA = new NetworkTime(60, a);
NetworkTime timeB = timeA - time;
Assert.IsTrue(Approximately(floatResultB, timeB.Time));
}
[Test]
[TestCase(17.32d)]
[TestCase(34d)]
[TestCase(-42.4d)]
[TestCase(-6d)]
[TestCase(int.MaxValue / 61d)]
public void NetworkTimeAddNetworkTimeTest(double time)
{
double a = 34d;
double floatResultB = a + time;
var timeA = new NetworkTime(60, a);
NetworkTime timeB = timeA + new NetworkTime(60, time);
Assert.IsTrue(Approximately(floatResultB, timeB.Time));
}
[Test]
[TestCase(17.32d)]
[TestCase(34d)]
[TestCase(-42.4d)]
[TestCase(-6d)]
[TestCase(int.MaxValue / 61d)]
public void NetworkTimeSubNetworkTimeTest(double time)
{
double a = 34d;
double floatResultB = a - time;
var timeA = new NetworkTime(60, a);
NetworkTime timeB = timeA - new NetworkTime(60, time);
Assert.IsTrue(Approximately(floatResultB, timeB.Time));
}
[Test]
public void NetworkTimeAdvanceTest()
{
var random = new Random(42);
var randomSteps = Enumerable.Repeat(0f, 1000).Select(t => Mathf.Lerp(1 / 25f, 1.80f, (float)random.NextDouble())).ToList();
NetworkTimeAdvanceTestInternal(randomSteps, 60, 0f);
NetworkTimeAdvanceTestInternal(randomSteps, 1, 0f);
NetworkTimeAdvanceTestInternal(randomSteps, 10, 0f);
NetworkTimeAdvanceTestInternal(randomSteps, 20, 0f);
NetworkTimeAdvanceTestInternal(randomSteps, 30, 0f);
NetworkTimeAdvanceTestInternal(randomSteps, 144, 0f);
NetworkTimeAdvanceTestInternal(randomSteps, 60, 23132.231f);
NetworkTimeAdvanceTestInternal(randomSteps, 1, 23132.231f);
NetworkTimeAdvanceTestInternal(randomSteps, 10, 23132.231f);
NetworkTimeAdvanceTestInternal(randomSteps, 20, 23132.231f);
NetworkTimeAdvanceTestInternal(randomSteps, 30, 23132.231f);
NetworkTimeAdvanceTestInternal(randomSteps, 30, 23132.231f);
NetworkTimeAdvanceTestInternal(randomSteps, 144, 23132.231f);
var shortSteps = Enumerable.Repeat(1 / 30f, 1000);
NetworkTimeAdvanceTestInternal(shortSteps, 60, 0f);
NetworkTimeAdvanceTestInternal(shortSteps, 1, 0f);
NetworkTimeAdvanceTestInternal(shortSteps, 10, 0f);
NetworkTimeAdvanceTestInternal(shortSteps, 20, 0f);
NetworkTimeAdvanceTestInternal(shortSteps, 30, 0f);
NetworkTimeAdvanceTestInternal(shortSteps, 144, 0f);
NetworkTimeAdvanceTestInternal(shortSteps, 60, 1000000f);
NetworkTimeAdvanceTestInternal(shortSteps, 60, 1000000f);
NetworkTimeAdvanceTestInternal(shortSteps, 1, 1000000f);
NetworkTimeAdvanceTestInternal(shortSteps, 10, 1000000f);
NetworkTimeAdvanceTestInternal(shortSteps, 20, 1000000f);
NetworkTimeAdvanceTestInternal(shortSteps, 30, 1000000f);
NetworkTimeAdvanceTestInternal(shortSteps, 144, 1000000f);
}
private void NetworkTimeAdvanceTestInternal(IEnumerable<float> steps, uint tickRate, float start, float start2 = 0f)
{
float maxAcceptableTotalOffset = 0.005f;
var startTime = new NetworkTime(tickRate, start);
var startTime2 = new NetworkTime(tickRate, start2);
NetworkTime dif = startTime2 - startTime;
foreach (var step in steps)
{
startTime += step;
startTime2 += step;
Assert.IsTrue(Approximately(startTime.Time, (startTime2 - dif).Time));
}
Assert.IsTrue(Approximately(startTime.Time, (startTime2 - dif).Time, maxAcceptableTotalOffset));
}
private static bool Approximately(double a, double b, double epsilon = 0.000001d)
{
var dif = Math.Abs(a - b);
return dif <= epsilon;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c9814eeb95ebba4d8cbb67deab9369b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,35 @@
using NUnit.Framework;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
public class ServerNetworkTimeSystemTests
{
/// <summary>
/// On the server local time should always be equal to server time. This test ensures that this is the case.
/// </summary>
[Test]
public void LocalTimeEqualServerTimeTest()
{
var steps = TimingTestHelper.GetRandomTimeSteps(100f, 0.01f, 0.1f, 42);
var serverTimeSystem = NetworkTimeSystem.ServerTimeSystem();
var serverTickSystem = new NetworkTickSystem(60, 0, 0);
serverTimeSystem.Reset(0.5d, 0);
TimingTestHelper.ApplySteps(serverTimeSystem, serverTickSystem, steps, step =>
{
Assert.IsTrue(Mathf.Approximately((float)serverTimeSystem.LocalTime, (float)serverTimeSystem.ServerTime));
Assert.IsTrue(Mathf.Approximately((float)serverTickSystem.LocalTime.Time, (float)serverTimeSystem.ServerTime));
});
Assert.IsTrue(serverTimeSystem.LocalTime > 1d);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 970d8f921595c7249a5ffc92fd74c7fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using System.Collections.Generic;
using UnityEngine;
using Random = System.Random;
namespace Unity.Netcode.EditorTests
{
/// <summary>
/// Helper functions for timing related tests. Allows to get a set of time steps and simulate time advancing without the need of a full playmode test.
/// </summary>
public static class TimingTestHelper
{
public static List<float> GetRandomTimeSteps(float totalDuration, float min, float max, int seed)
{
var random = new Random(seed);
var steps = new List<float>();
while (totalDuration > 0f)
{
var next = Mathf.Lerp(min, max, (float)random.NextDouble());
steps.Add(next);
totalDuration -= next;
}
// correct overshoot at the end
steps[steps.Count - 1] -= totalDuration;
return steps;
}
public delegate void StepCheckDelegate(int step);
public delegate void StepCheckResetDelegate(int step, bool reset);
public static void ApplySteps(NetworkTimeSystem timeSystem, NetworkTickSystem tickSystem, List<float> steps, StepCheckDelegate stepCheck = null)
{
for (var i = 0; i < steps.Count; i++)
{
var step = steps[i];
timeSystem.Advance(step);
tickSystem.UpdateTick(timeSystem.LocalTime, timeSystem.ServerTime);
if (stepCheck != null)
{
stepCheck(i);
}
}
}
public static void ApplySteps(NetworkTimeSystem timeSystem, NetworkTickSystem tickSystem, List<float> steps, StepCheckResetDelegate stepCheck = null)
{
for (var i = 0; i < steps.Count; i++)
{
var step = steps[i];
var reset = timeSystem.Advance(step);
tickSystem.UpdateTick(timeSystem.LocalTime, timeSystem.ServerTime);
if (stepCheck != null)
{
stepCheck(i, reset);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9010f986d5ecb994a8dd34076ff41c8e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

3
Tests/Editor/UI.meta Normal file
View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f672293e0efc41a6a7e930fd7ff14436
timeCreated: 1631650280

View File

@@ -0,0 +1,15 @@
using Unity.Netcode.Editor;
using UnityEditor;
using UnityEngine;
namespace Unity.Netcode.EditorTests
{
internal static class UITestHelpers
{
[MenuItem("Netcode/UI/Reset Multiplayer Tools Tip Status")]
private static void ResetMultiplayerToolsTipStatus()
{
PlayerPrefs.DeleteKey(NetworkManagerEditor.InstallMultiplayerToolsTipDismissedPlayerPrefKey);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bde5fc3349494f77bebd0be12a6957e1
timeCreated: 1631650292

View File

@@ -0,0 +1,36 @@
{
"name": "Unity.Netcode.EditorTests",
"rootNamespace": "Unity.Netcode.EditorTests",
"references": [
"Unity.Netcode.Runtime",
"Unity.Netcode.Editor",
"Unity.Netcode.Components",
"Unity.Multiplayer.MetricTypes",
"Unity.Multiplayer.NetStats"
],
"optionalUnityReferences": [
"TestAssemblies"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false,
"versionDefines": [
{
"name": "com.unity.multiplayer.tools",
"expression": "",
"define": "MULTIPLAYER_TOOLS"
}
]
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 072b82e2b7c1dcf439827d3fbc4f52a1
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Tests/Runtime.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9d3a545f9ce7b074c95a3b1c3101dee9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("TestProject.EditorTests")]
[assembly: InternalsVisibleTo("TestProject.RuntimeTests")]
[assembly: InternalsVisibleTo("TestProject.ToolsIntegration.RuntimeTests")]

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5751b3c3bb5621e4686249b8083be068
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,178 @@
using System;
using System.Collections;
using System.Linq;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace Unity.Netcode.RuntimeTests
{
public abstract class BaseMultiInstanceTest
{
private const string k_FirstPartOfTestRunnerSceneName = "InitTestScene";
protected GameObject m_PlayerPrefab;
protected NetworkManager m_ServerNetworkManager;
protected NetworkManager[] m_ClientNetworkManagers;
protected abstract int NbClients { get; }
protected bool m_BypassStartAndWaitForClients = false;
[UnitySetUp]
public virtual IEnumerator Setup()
{
yield return StartSomeClientsAndServerWithPlayers(true, NbClients, _ => { });
}
[UnityTearDown]
public virtual IEnumerator Teardown()
{
// Shutdown and clean up both of our NetworkManager instances
try
{
MultiInstanceHelpers.Destroy();
}
catch (Exception e) { throw e; }
finally
{
if (m_PlayerPrefab != null)
{
Object.Destroy(m_PlayerPrefab);
m_PlayerPrefab = null;
}
}
// Make sure any NetworkObject with a GlobalObjectIdHash value of 0 is destroyed
// If we are tearing down, we don't want to leave NetworkObjects hanging around
var networkObjects = Object.FindObjectsOfType<NetworkObject>().ToList();
foreach (var networkObject in networkObjects)
{
Object.DestroyImmediate(networkObject);
}
// wait for next frame so everything is destroyed, so following tests can execute from clean environment
int nextFrameNumber = Time.frameCount + 1;
yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber);
}
/// <summary>
/// We want to exclude the TestRunner scene on the host-server side so it won't try to tell clients to
/// synchronize to this scene when they connect
/// </summary>
private static bool VerifySceneIsValidForClientsToLoad(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
{
// exclude test runner scene
if (sceneName.StartsWith(k_FirstPartOfTestRunnerSceneName))
{
return false;
}
return true;
}
/// <summary>
/// This registers scene validation callback for the server to prevent it from telling connecting
/// clients to synchronize (i.e. load) the test runner scene. This will also register the test runner
/// scene and its handle for both client(s) and server-host.
/// </summary>
public static void SceneManagerValidationAndTestRunnerInitialization(NetworkManager networkManager)
{
// If VerifySceneBeforeLoading is not already set, then go ahead and set it so the host/server
// will not try to synchronize clients to the TestRunner scene. We only need to do this for the server.
if (networkManager.IsServer && networkManager.SceneManager.VerifySceneBeforeLoading == null)
{
networkManager.SceneManager.VerifySceneBeforeLoading = VerifySceneIsValidForClientsToLoad;
// If a unit/integration test does not handle this on their own, then Ignore the validation warning
networkManager.SceneManager.DisableValidationWarnings(true);
}
// Register the test runner scene so it will be able to synchronize NetworkObjects without logging a
// warning about using the currently active scene
var scene = SceneManager.GetActiveScene();
// As long as this is a test runner scene (or most likely a test runner scene)
if (scene.name.StartsWith(k_FirstPartOfTestRunnerSceneName))
{
// Register the test runner scene just so we avoid another warning about not being able to find the
// scene to synchronize NetworkObjects. Next, add the currently active test runner scene to the scenes
// loaded and register the server to client scene handle since host-server shares the test runner scene
// with the clients.
networkManager.SceneManager.GetAndAddNewlyLoadedSceneByName(scene.name);
networkManager.SceneManager.ServerSceneHandleToClientSceneHandle.Add(scene.handle, scene.handle);
}
}
/// <summary>
/// Utility to spawn some clients and a server and set them up
/// </summary>
/// <param name="nbClients"></param>
/// <param name="updatePlayerPrefab">Update the prefab with whatever is needed before players spawn</param>
/// <param name="targetFrameRate">The targetFrameRate of the Unity engine to use while this multi instance test is running. Will be reset on teardown.</param>
/// <returns></returns>
public IEnumerator StartSomeClientsAndServerWithPlayers(bool useHost, int nbClients, Action<GameObject> updatePlayerPrefab = null, int targetFrameRate = 60)
{
// Make sure any NetworkObject with a GlobalObjectIdHash value of 0 is destroyed
// If we are tearing down, we don't want to leave NetworkObjects hanging around
var networkObjects = Object.FindObjectsOfType<NetworkObject>().ToList();
var networkObjectsList = networkObjects.Where(c => c.GlobalObjectIdHash == 0);
foreach (var netObject in networkObjects)
{
Object.DestroyImmediate(netObject);
}
// Create multiple NetworkManager instances
if (!MultiInstanceHelpers.Create(nbClients, out NetworkManager server, out NetworkManager[] clients, targetFrameRate))
{
Debug.LogError("Failed to create instances");
Assert.Fail("Failed to create instances");
}
m_ClientNetworkManagers = clients;
m_ServerNetworkManager = server;
// Create playerPrefab
m_PlayerPrefab = new GameObject("Player");
NetworkObject networkObject = m_PlayerPrefab.AddComponent<NetworkObject>();
/*
* Normally we would only allow player prefabs to be set to a prefab. Not runtime created objects.
* In order to prevent having a Resource folder full of a TON of prefabs that we have to maintain,
* MultiInstanceHelper has a helper function that lets you mark a runtime created object to be
* treated as a prefab by the Netcode. That's how we can get away with creating the player prefab
* at runtime without it being treated as a SceneObject or causing other conflicts with the Netcode.
*/
// Make it a prefab
MultiInstanceHelpers.MakeNetworkObjectTestPrefab(networkObject);
if (updatePlayerPrefab != null)
{
updatePlayerPrefab(m_PlayerPrefab); // update player prefab with whatever is needed before players are spawned
}
// Set the player prefab
server.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
for (int i = 0; i < clients.Length; i++)
{
clients[i].NetworkConfig.PlayerPrefab = m_PlayerPrefab;
}
if (!m_BypassStartAndWaitForClients)
{
// Start the instances and pass in our SceneManagerInitialization action that is invoked immediately after host-server
// is started and after each client is started.
if (!MultiInstanceHelpers.Start(useHost, server, clients, SceneManagerValidationAndTestRunnerInitialization))
{
Debug.LogError("Failed to start instances");
Assert.Fail("Failed to start instances");
}
// Wait for connection on client side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients));
// Wait for connection on server side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnectedToServer(server, useHost ? nbClients + 1 : nbClients));
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 789a3189410645aca48f11a51c823418
timeCreated: 1621620979

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 09f6601e441556642ab8217941b24e5c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,109 @@
using System.Collections.Generic;
using UnityEngine;
namespace Unity.Netcode.RuntimeTests
{
/// <summary>
/// Used in conjunction with the RpcQueueTest to validate from 1 byte to (n) MaximumBufferSize
/// - Sending and Receiving a continually growing buffer up to (MaximumBufferSize)
/// - Default maximum buffer size is 1MB
/// </summary>
public class BufferDataValidationComponent : NetworkBehaviour
{
/// <summary>
/// Allows the external RPCQueueTest to begin testing or stop it
/// </summary>
public bool EnableTesting;
/// <summary>
/// The maximum size of the buffer to send
/// </summary>
public int MaximumBufferSize = 1 << 15;
/// <summary>
/// The rate at which the buffer size increases until it reaches MaximumBufferSize
/// (the default starting buffer size is 1 bytes)
/// </summary>
public int BufferSizeStart = 1;
/// <summary>
/// Is checked to determine if the test exited because it failed
/// </summary>
public bool TestFailed { get; internal set; }
private bool m_WaitForValidation;
private int m_CurrentBufferSize;
private List<byte> m_SendBuffer;
private List<byte> m_PreCalculatedBufferValues;
// Start is called before the first frame update
private void Start()
{
m_WaitForValidation = false;
m_CurrentBufferSize = BufferSizeStart;
m_SendBuffer = new List<byte>(MaximumBufferSize + 1);
m_PreCalculatedBufferValues = new List<byte>(MaximumBufferSize + 1);
while (m_PreCalculatedBufferValues.Count <= MaximumBufferSize)
{
m_PreCalculatedBufferValues.Add((byte)Random.Range(0, 255));
}
}
/// <summary>
/// Returns back whether the test has completed the total number of iterations
/// </summary>
/// <returns></returns>
public bool IsTestComplete()
{
if (m_CurrentBufferSize > MaximumBufferSize || TestFailed)
{
return true;
}
return false;
}
// Update is called once per frame
private void Update()
{
if (NetworkManager.Singleton.IsListening && EnableTesting && !IsTestComplete() && !m_WaitForValidation)
{
m_SendBuffer.Clear();
//Keep the current contents of the bufffer and fill the buffer with the delta difference of the buffer's current size and new size from the m_PreCalculatedBufferValues
m_SendBuffer.AddRange(m_PreCalculatedBufferValues.GetRange(0, m_CurrentBufferSize));
//Make sure we don't do anything until we finish validating buffer
m_WaitForValidation = true;
//Send the buffer
SendBufferServerRpc(m_SendBuffer.ToArray());
}
}
/// <summary>
/// Server side RPC for testing
/// </summary>
/// <param name="parameters">server rpc parameters</param>
[ServerRpc]
private void SendBufferServerRpc(byte[] buffer)
{
TestFailed = !NetworkManagerHelper.BuffersMatch(0, buffer.Length, buffer, m_SendBuffer.ToArray());
if (!TestFailed)
{
Debug.Log($"Tested buffer size of {m_SendBuffer.Count} -- OK");
}
if (m_CurrentBufferSize == MaximumBufferSize)
{
m_CurrentBufferSize++;
}
else
{
//Increasse buffer size
m_CurrentBufferSize = m_CurrentBufferSize << 1;
}
m_WaitForValidation = false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1bb4bf89a220a8b409a4afb1f0f4eced
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,242 @@
using UnityEngine;
namespace Unity.Netcode.RuntimeTests
{
/// <summary>
/// This provides coverage for all of the predefined NetworkVariable types
/// The initial goal is for generalized full coverage of NetworkVariables:
/// Covers all of the various constructor calls (i.e. various parameters or no parameters)
/// Covers the local NetworkVariable's OnValueChanged functionality (i.e. when a specific type changes do we get a notification?)
/// This was built as a NetworkBehaviour for further client-server unit testing patterns when this capability is available.
/// </summary>
internal class NetworkVariableTestComponent : NetworkBehaviour
{
private NetworkVariable<bool> m_NetworkVariableBool;
private NetworkVariable<byte> m_NetworkVariableByte;
private NetworkVariable<Color> m_NetworkVariableColor;
private NetworkVariable<Color32> m_NetworkVariableColor32;
private NetworkVariable<double> m_NetworkVariableDouble;
private NetworkVariable<float> m_NetworkVariableFloat;
private NetworkVariable<int> m_NetworkVariableInt;
private NetworkVariable<long> m_NetworkVariableLong;
private NetworkVariable<sbyte> m_NetworkVariableSByte;
private NetworkVariable<Quaternion> m_NetworkVariableQuaternion;
private NetworkVariable<short> m_NetworkVariableShort;
private NetworkVariable<Vector4> m_NetworkVariableVector4;
private NetworkVariable<Vector3> m_NetworkVariableVector3;
private NetworkVariable<Vector2> m_NetworkVariableVector2;
private NetworkVariable<Ray> m_NetworkVariableRay;
private NetworkVariable<ulong> m_NetworkVariableULong;
private NetworkVariable<uint> m_NetworkVariableUInt;
private NetworkVariable<ushort> m_NetworkVariableUShort;
public NetworkVariableHelper<bool> Bool_Var;
public NetworkVariableHelper<byte> Byte_Var;
public NetworkVariableHelper<Color> Color_Var;
public NetworkVariableHelper<Color32> Color32_Var;
public NetworkVariableHelper<double> Double_Var;
public NetworkVariableHelper<float> Float_Var;
public NetworkVariableHelper<int> Int_Var;
public NetworkVariableHelper<long> Long_Var;
public NetworkVariableHelper<sbyte> Sbyte_Var;
public NetworkVariableHelper<Quaternion> Quaternion_Var;
public NetworkVariableHelper<short> Short_Var;
public NetworkVariableHelper<Vector4> Vector4_Var;
public NetworkVariableHelper<Vector3> Vector3_Var;
public NetworkVariableHelper<Vector2> Vector2_Var;
public NetworkVariableHelper<Ray> Ray_Var;
public NetworkVariableHelper<ulong> Ulong_Var;
public NetworkVariableHelper<uint> Uint_Var;
public NetworkVariableHelper<ushort> Ushort_Var;
public bool EnableTesting;
private bool m_Initialized;
private bool m_FinishedTests;
private bool m_ChangesAppliedToNetworkVariables;
private float m_WaitForChangesTimeout;
// Start is called before the first frame update
private void InitializeTest()
{
// Generic Constructor Test Coverage
m_NetworkVariableBool = new NetworkVariable<bool>();
m_NetworkVariableByte = new NetworkVariable<byte>();
m_NetworkVariableColor = new NetworkVariable<Color>();
m_NetworkVariableColor32 = new NetworkVariable<Color32>();
m_NetworkVariableDouble = new NetworkVariable<double>();
m_NetworkVariableFloat = new NetworkVariable<float>();
m_NetworkVariableInt = new NetworkVariable<int>();
m_NetworkVariableLong = new NetworkVariable<long>();
m_NetworkVariableSByte = new NetworkVariable<sbyte>();
m_NetworkVariableQuaternion = new NetworkVariable<Quaternion>();
m_NetworkVariableShort = new NetworkVariable<short>();
m_NetworkVariableVector4 = new NetworkVariable<Vector4>();
m_NetworkVariableVector3 = new NetworkVariable<Vector3>();
m_NetworkVariableVector2 = new NetworkVariable<Vector2>();
m_NetworkVariableRay = new NetworkVariable<Ray>();
m_NetworkVariableULong = new NetworkVariable<ulong>();
m_NetworkVariableUInt = new NetworkVariable<uint>();
m_NetworkVariableUShort = new NetworkVariable<ushort>();
// NetworkVariable Value Type Constructor Test Coverage
m_NetworkVariableBool = new NetworkVariable<bool>(true);
m_NetworkVariableByte = new NetworkVariable<byte>((byte)0);
m_NetworkVariableColor = new NetworkVariable<Color>(new Color(1, 1, 1, 1));
m_NetworkVariableColor32 = new NetworkVariable<Color32>(new Color32(1, 1, 1, 1));
m_NetworkVariableDouble = new NetworkVariable<double>(1.0);
m_NetworkVariableFloat = new NetworkVariable<float>(1.0f);
m_NetworkVariableInt = new NetworkVariable<int>(1);
m_NetworkVariableLong = new NetworkVariable<long>(1);
m_NetworkVariableSByte = new NetworkVariable<sbyte>((sbyte)0);
m_NetworkVariableQuaternion = new NetworkVariable<Quaternion>(Quaternion.identity);
m_NetworkVariableShort = new NetworkVariable<short>(256);
m_NetworkVariableVector4 = new NetworkVariable<Vector4>(new Vector4(1, 1, 1, 1));
m_NetworkVariableVector3 = new NetworkVariable<Vector3>(new Vector3(1, 1, 1));
m_NetworkVariableVector2 = new NetworkVariable<Vector2>(new Vector2(1, 1));
m_NetworkVariableRay = new NetworkVariable<Ray>(new Ray());
m_NetworkVariableULong = new NetworkVariable<ulong>(1);
m_NetworkVariableUInt = new NetworkVariable<uint>(1);
m_NetworkVariableUShort = new NetworkVariable<ushort>(1);
m_NetworkVariableBool = new NetworkVariable<bool>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableByte = new NetworkVariable<byte>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableColor = new NetworkVariable<Color>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableColor32 = new NetworkVariable<Color32>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableDouble = new NetworkVariable<double>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableFloat = new NetworkVariable<float>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableInt = new NetworkVariable<int>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableLong = new NetworkVariable<long>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableSByte = new NetworkVariable<sbyte>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableQuaternion = new NetworkVariable<Quaternion>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableShort = new NetworkVariable<short>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableVector4 = new NetworkVariable<Vector4>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableVector3 = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableVector2 = new NetworkVariable<Vector2>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableRay = new NetworkVariable<Ray>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableULong = new NetworkVariable<ulong>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableUInt = new NetworkVariable<uint>(NetworkVariableReadPermission.Everyone);
m_NetworkVariableUShort = new NetworkVariable<ushort>(NetworkVariableReadPermission.Everyone);
// NetworkVariable Value Type and NetworkVariableSettings Constructor Test Coverage
m_NetworkVariableBool = new NetworkVariable<bool>(NetworkVariableReadPermission.Everyone, true);
m_NetworkVariableByte = new NetworkVariable<byte>(NetworkVariableReadPermission.Everyone, 0);
m_NetworkVariableColor = new NetworkVariable<Color>(NetworkVariableReadPermission.Everyone, new Color(1, 1, 1, 1));
m_NetworkVariableColor32 = new NetworkVariable<Color32>(NetworkVariableReadPermission.Everyone, new Color32(1, 1, 1, 1));
m_NetworkVariableDouble = new NetworkVariable<double>(NetworkVariableReadPermission.Everyone, 1.0);
m_NetworkVariableFloat = new NetworkVariable<float>(NetworkVariableReadPermission.Everyone, 1.0f);
m_NetworkVariableInt = new NetworkVariable<int>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableLong = new NetworkVariable<long>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableSByte = new NetworkVariable<sbyte>(NetworkVariableReadPermission.Everyone, 0);
m_NetworkVariableQuaternion = new NetworkVariable<Quaternion>(NetworkVariableReadPermission.Everyone, Quaternion.identity);
m_NetworkVariableShort = new NetworkVariable<short>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableVector4 = new NetworkVariable<Vector4>(NetworkVariableReadPermission.Everyone, new Vector4(1, 1, 1, 1));
m_NetworkVariableVector3 = new NetworkVariable<Vector3>(NetworkVariableReadPermission.Everyone, new Vector3(1, 1, 1));
m_NetworkVariableVector2 = new NetworkVariable<Vector2>(NetworkVariableReadPermission.Everyone, new Vector2(1, 1));
m_NetworkVariableRay = new NetworkVariable<Ray>(NetworkVariableReadPermission.Everyone, new Ray());
m_NetworkVariableULong = new NetworkVariable<ulong>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableUInt = new NetworkVariable<uint>(NetworkVariableReadPermission.Everyone, 1);
m_NetworkVariableUShort = new NetworkVariable<ushort>(NetworkVariableReadPermission.Everyone, 1);
// Use this nifty class: NetworkVariableHelper
// Tracks if NetworkVariable changed invokes the OnValueChanged callback for the given instance type
Bool_Var = new NetworkVariableHelper<bool>(m_NetworkVariableBool);
Byte_Var = new NetworkVariableHelper<byte>(m_NetworkVariableByte);
Color_Var = new NetworkVariableHelper<Color>(m_NetworkVariableColor);
Color32_Var = new NetworkVariableHelper<Color32>(m_NetworkVariableColor32);
Double_Var = new NetworkVariableHelper<double>(m_NetworkVariableDouble);
Float_Var = new NetworkVariableHelper<float>(m_NetworkVariableFloat);
Int_Var = new NetworkVariableHelper<int>(m_NetworkVariableInt);
Long_Var = new NetworkVariableHelper<long>(m_NetworkVariableLong);
Sbyte_Var = new NetworkVariableHelper<sbyte>(m_NetworkVariableSByte);
Quaternion_Var = new NetworkVariableHelper<Quaternion>(m_NetworkVariableQuaternion);
Short_Var = new NetworkVariableHelper<short>(m_NetworkVariableShort);
Vector4_Var = new NetworkVariableHelper<Vector4>(m_NetworkVariableVector4);
Vector3_Var = new NetworkVariableHelper<Vector3>(m_NetworkVariableVector3);
Vector2_Var = new NetworkVariableHelper<Vector2>(m_NetworkVariableVector2);
Ray_Var = new NetworkVariableHelper<Ray>(m_NetworkVariableRay);
Ulong_Var = new NetworkVariableHelper<ulong>(m_NetworkVariableULong);
Uint_Var = new NetworkVariableHelper<uint>(m_NetworkVariableUInt);
Ushort_Var = new NetworkVariableHelper<ushort>(m_NetworkVariableUShort);
}
/// <summary>
/// Test result for all values changed the expected number of times (once per unique NetworkVariable type)
/// </summary>
public bool DidAllValuesChange()
{
if (NetworkVariableBaseHelper.VarChangedCount == NetworkVariableBaseHelper.InstanceCount)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// Returns back whether the test has completed the total number of iterations
/// </summary>
public bool IsTestComplete()
{
return m_FinishedTests;
}
// Update is called once per frame
private void Update()
{
if (EnableTesting)
{
//Added timeout functionality for near future changes to NetworkVariables and the Snapshot system
if (!m_FinishedTests && m_ChangesAppliedToNetworkVariables)
{
//We finish testing if all NetworkVariables changed their value or we timed out waiting for
//all NetworkVariables to change their value
m_FinishedTests = DidAllValuesChange() || (m_WaitForChangesTimeout < Time.realtimeSinceStartup);
}
else
{
if (NetworkManager != null && NetworkManager.IsListening)
{
if (!m_Initialized)
{
InitializeTest();
m_Initialized = true;
}
else
{
//Now change all of the values to make sure we are at least testing the local callback
m_NetworkVariableBool.Value = false;
m_NetworkVariableByte.Value = 255;
m_NetworkVariableColor.Value = new Color(100, 100, 100);
m_NetworkVariableColor32.Value = new Color32(100, 100, 100, 100);
m_NetworkVariableDouble.Value = 1000;
m_NetworkVariableFloat.Value = 1000.0f;
m_NetworkVariableInt.Value = 1000;
m_NetworkVariableLong.Value = 100000;
m_NetworkVariableSByte.Value = -127;
m_NetworkVariableQuaternion.Value = new Quaternion(100, 100, 100, 100);
m_NetworkVariableShort.Value = short.MaxValue;
m_NetworkVariableVector4.Value = new Vector4(1000, 1000, 1000, 1000);
m_NetworkVariableVector3.Value = new Vector3(1000, 1000, 1000);
m_NetworkVariableVector2.Value = new Vector2(1000, 1000);
m_NetworkVariableRay.Value = new Ray(Vector3.one, Vector3.right);
m_NetworkVariableULong.Value = ulong.MaxValue;
m_NetworkVariableUInt.Value = uint.MaxValue;
m_NetworkVariableUShort.Value = ushort.MaxValue;
//Set the timeout (i.e. how long we will wait for all NetworkVariables to have registered their changes)
m_WaitForChangesTimeout = Time.realtimeSinceStartup + 0.50f;
m_ChangesAppliedToNetworkVariables = true;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef882d7ba8231eb45839424f54a12486
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
using System;
using System.Text;
using System.Collections;
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
namespace Unity.Netcode.RuntimeTests
{
public class ConnectionApprovalTests
{
private Guid m_ValidationToken;
private bool m_IsValidated;
[SetUp]
public void Setup()
{
// Create, instantiate, and host
Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _, NetworkManagerHelper.NetworkManagerOperatingMode.None));
m_ValidationToken = Guid.NewGuid();
}
[UnityTest]
public IEnumerator ConnectionApproval()
{
NetworkManagerHelper.NetworkManagerObject.ConnectionApprovalCallback += NetworkManagerObject_ConnectionApprovalCallback;
NetworkManagerHelper.NetworkManagerObject.NetworkConfig.ConnectionApproval = true;
NetworkManagerHelper.NetworkManagerObject.NetworkConfig.PlayerPrefab = null;
NetworkManagerHelper.NetworkManagerObject.NetworkConfig.ConnectionData = Encoding.UTF8.GetBytes(m_ValidationToken.ToString());
m_IsValidated = false;
NetworkManagerHelper.NetworkManagerObject.StartHost();
var timeOut = Time.realtimeSinceStartup + 3.0f;
var timedOut = false;
while (!m_IsValidated)
{
yield return new WaitForSeconds(0.01f);
if (timeOut < Time.realtimeSinceStartup)
{
timedOut = true;
}
}
//Make sure we didn't time out
Assert.False(timedOut);
Assert.True(m_IsValidated);
}
private void NetworkManagerObject_ConnectionApprovalCallback(byte[] connectionData, ulong clientId, NetworkManager.ConnectionApprovedDelegate callback)
{
var stringGuid = Encoding.UTF8.GetString(connectionData);
if (m_ValidationToken.ToString() == stringGuid)
{
m_IsValidated = true;
}
callback(false, null, m_IsValidated, null, null);
}
[TearDown]
public void TearDown()
{
// Stop, shutdown, and destroy
NetworkManagerHelper.ShutdownNetworkManager();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52ef2017d72b57f418907e98e1d8b90a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
using System.Collections;
using System.Linq;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
public class DisconnectTests
{
[UnityTest]
public IEnumerator RemoteDisconnectPlayerObjectCleanup()
{
// create server and client instances
MultiInstanceHelpers.Create(1, out NetworkManager server, out NetworkManager[] clients);
// create prefab
var gameObject = new GameObject("PlayerObject");
var networkObject = gameObject.AddComponent<NetworkObject>();
networkObject.DontDestroyWithOwner = true;
MultiInstanceHelpers.MakeNetworkObjectTestPrefab(networkObject);
server.NetworkConfig.PlayerPrefab = gameObject;
for (int i = 0; i < clients.Length; i++)
{
clients[i].NetworkConfig.PlayerPrefab = gameObject;
}
// start server and connect clients
MultiInstanceHelpers.Start(false, server, clients);
// wait for connection on client side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients));
// wait for connection on server side
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientConnectedToServer(server));
// disconnect the remote client
server.DisconnectClient(clients[0].LocalClientId);
// wait 1 frame because destroys are delayed
var nextFrameNumber = Time.frameCount + 1;
yield return new WaitUntil(() => Time.frameCount >= nextFrameNumber);
// ensure the object was destroyed
Assert.False(server.SpawnManager.SpawnedObjects.Any(x => x.Value.IsPlayerObject && x.Value.OwnerClientId == clients[0].LocalClientId));
// cleanup
MultiInstanceHelpers.Destroy();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b05b4daca3854ff6b01a1f002d433dd6
timeCreated: 1631652586

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fb1b6e801936c7f4a9af28dbed5ea2ff
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,273 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using NUnit.Framework;
using Unity.Netcode.Transports.UNET;
namespace Unity.Netcode.RuntimeTests
{
/// <summary>
/// Helper class to instantiate a NetworkManager
/// This also provides the ability to:
/// --- instantiate GameObjects with NetworkObject components that returns a Guid for accessing it later.
/// --- add NetworkBehaviour components to the instantiated GameObjects
/// --- spawn a NetworkObject using its parent GameObject's Guid
/// Call StartNetworkManager in the constructor of your runtime unit test class.
/// Call ShutdownNetworkManager in the destructor of your runtime unit test class.
///
/// Includes a useful "BuffersMatch" method that allows you to compare two buffers (returns true if they match false if not)
/// </summary>
public static class NetworkManagerHelper
{
public static NetworkManager NetworkManagerObject { get; internal set; }
public static GameObject NetworkManagerGameObject { get; internal set; }
internal static Dictionary<Guid, GameObject> InstantiatedGameObjects = new Dictionary<Guid, GameObject>();
internal static Dictionary<Guid, NetworkObject> InstantiatedNetworkObjects = new Dictionary<Guid, NetworkObject>();
internal static NetworkManagerOperatingMode CurrentNetworkManagerMode;
/// <summary>
/// This provides the ability to start NetworkManager in various modes
/// </summary>
public enum NetworkManagerOperatingMode
{
None,
Host,
Server,
Client,
}
/// <summary>
/// Called upon the RpcQueueTests being instantiated.
/// This creates an instance of the NetworkManager to be used during unit tests.
/// Currently, the best method to run unit tests is by starting in host mode as you can
/// send messages to yourself (i.e. Host-Client to Host-Server and vice versa).
/// As such, the default setting is to start in Host mode.
/// </summary>
/// <param name="managerMode">parameter to specify which mode you want to start the NetworkManager</param>
/// <param name="networkConfig">parameter to specify custom NetworkConfig settings</param>
/// <returns>true if it was instantiated or is already instantiate otherwise false means it failed to instantiate</returns>
public static bool StartNetworkManager(out NetworkManager networkManager, NetworkManagerOperatingMode managerMode = NetworkManagerOperatingMode.Host, NetworkConfig networkConfig = null)
{
// If we are changing the current manager mode and the current manager mode is not "None", then stop the NetworkManager mode
if (CurrentNetworkManagerMode != managerMode && CurrentNetworkManagerMode != NetworkManagerOperatingMode.None)
{
StopNetworkManagerMode();
}
if (NetworkManagerGameObject == null)
{
NetworkManagerGameObject = new GameObject(nameof(NetworkManager));
NetworkManagerObject = NetworkManagerGameObject.AddComponent<NetworkManager>();
if (NetworkManagerObject == null)
{
networkManager = null;
return false;
}
Debug.Log($"{nameof(NetworkManager)} Instantiated.");
var unetTransport = NetworkManagerGameObject.AddComponent<UNetTransport>();
if (networkConfig == null)
{
networkConfig = new NetworkConfig
{
EnableSceneManagement = false,
};
}
NetworkManagerObject.NetworkConfig = networkConfig;
unetTransport.ConnectAddress = "127.0.0.1";
unetTransport.ConnectPort = 7777;
unetTransport.ServerListenPort = 7777;
unetTransport.MessageBufferSize = 65535;
unetTransport.MaxConnections = 100;
unetTransport.MessageSendMode = UNetTransport.SendMode.Immediately;
NetworkManagerObject.NetworkConfig.NetworkTransport = unetTransport;
// Starts the network manager in the mode specified
StartNetworkManagerMode(managerMode);
}
networkManager = NetworkManagerObject;
return true;
}
/// <summary>
/// Add a GameObject with a NetworkObject component
/// </summary>
/// <param name="nameOfGameObject">the name of the object</param>
/// <returns></returns>
public static Guid AddGameNetworkObject(string nameOfGameObject)
{
var gameObjectId = Guid.NewGuid();
// Create the player object that we will spawn as a host
var gameObject = new GameObject(nameOfGameObject);
Assert.IsNotNull(gameObject);
var networkObject = gameObject.AddComponent<NetworkObject>();
Assert.IsNotNull(networkObject);
Assert.IsFalse(InstantiatedGameObjects.ContainsKey(gameObjectId));
Assert.IsFalse(InstantiatedNetworkObjects.ContainsKey(gameObjectId));
InstantiatedGameObjects.Add(gameObjectId, gameObject);
InstantiatedNetworkObjects.Add(gameObjectId, networkObject);
return gameObjectId;
}
/// <summary>
/// Helper class to add a component to the GameObject with a NetoworkObject component
/// </summary>
/// <typeparam name="T">NetworkBehaviour component being added to the GameObject</typeparam>
/// <param name="gameObjectIdentifier">ID returned to reference the game object</param>
/// <returns></returns>
public static T AddComponentToObject<T>(Guid gameObjectIdentifier) where T : NetworkBehaviour
{
Assert.IsTrue(InstantiatedGameObjects.ContainsKey(gameObjectIdentifier));
return InstantiatedGameObjects[gameObjectIdentifier].AddComponent<T>();
}
/// <summary>
/// Spawn the NetworkObject, so Rpcs can flow
/// </summary>
/// <param name="gameObjectIdentifier">ID returned to reference the game object</param>
public static void SpawnNetworkObject(Guid gameObjectIdentifier)
{
Assert.IsTrue(InstantiatedNetworkObjects.ContainsKey(gameObjectIdentifier));
if (!InstantiatedNetworkObjects[gameObjectIdentifier].IsSpawned)
{
InstantiatedNetworkObjects[gameObjectIdentifier].Spawn();
}
}
/// <summary>
/// Starts the NetworkManager in the current mode specified by managerMode
/// </summary>
/// <param name="managerMode">the mode to start the NetworkManager as</param>
private static void StartNetworkManagerMode(NetworkManagerOperatingMode managerMode)
{
CurrentNetworkManagerMode = managerMode;
switch (CurrentNetworkManagerMode)
{
case NetworkManagerOperatingMode.Host:
{
// Starts the host
NetworkManagerObject.StartHost();
break;
}
case NetworkManagerOperatingMode.Server:
{
// Starts the server
NetworkManagerObject.StartServer();
break;
}
case NetworkManagerOperatingMode.Client:
{
// Starts the client
NetworkManagerObject.StartClient();
break;
}
}
// If we started an netcode session
if (CurrentNetworkManagerMode != NetworkManagerOperatingMode.None)
{
// With some unit tests the Singleton can still be from a previous unit test
// depending upon the order of operations that occurred.
if (NetworkManager.Singleton != NetworkManagerObject)
{
NetworkManagerObject.SetSingleton();
}
// Only log this if we started an netcode session
Debug.Log($"{CurrentNetworkManagerMode} started.");
}
}
/// <summary>
/// Stops the current mode of the NetworkManager
/// </summary>
private static void StopNetworkManagerMode()
{
NetworkManagerObject.Shutdown();
Debug.Log($"{CurrentNetworkManagerMode} stopped.");
CurrentNetworkManagerMode = NetworkManagerOperatingMode.None;
}
// This is called, even if we assert and exit early from a test
public static void ShutdownNetworkManager()
{
// clean up any game objects created with custom unit testing components
foreach (var entry in InstantiatedGameObjects)
{
UnityEngine.Object.DestroyImmediate(entry.Value);
}
InstantiatedGameObjects.Clear();
if (NetworkManagerGameObject != null)
{
Debug.Log($"{nameof(NetworkManager)} shutdown.");
StopNetworkManagerMode();
UnityEngine.Object.DestroyImmediate(NetworkManagerGameObject);
Debug.Log($"{nameof(NetworkManager)} destroyed.");
}
NetworkManagerGameObject = null;
NetworkManagerObject = null;
}
public static bool BuffersMatch(int indexOffset, long targetSize, byte[] sourceArray, byte[] originalArray)
{
long largeInt64Blocks = targetSize >> 3; // Divide by 8
int originalArrayOffset = 0;
// process by 8 byte blocks if we can
for (long i = 0; i < largeInt64Blocks; i++)
{
if (BitConverter.ToInt64(sourceArray, indexOffset) != BitConverter.ToInt64(originalArray, originalArrayOffset))
{
return false;
}
indexOffset += 8;
originalArrayOffset += 8;
}
long offset = largeInt64Blocks * 8;
long remainder = targetSize - offset;
// 4 byte block
if (remainder >= 4)
{
if (BitConverter.ToInt32(sourceArray, indexOffset) != BitConverter.ToInt32(originalArray, originalArrayOffset))
{
return false;
}
indexOffset += 4;
originalArrayOffset += 4;
offset += 4;
}
// Remainder of bytes < 4
if (targetSize - offset > 0)
{
for (long i = 0; i < (targetSize - offset); i++)
{
if (sourceArray[indexOffset + i] != originalArray[originalArrayOffset + i])
{
return false;
}
}
}
return true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c920be150fd14ad4ca1936e1a259417c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
namespace Unity.Netcode.RuntimeTests
{
/// <summary>
/// Will automatically register for the NetworkVariable OnValueChanged
/// delegate handler. It then will expose that single delegate invocation
/// to anything that registers for this NetworkVariableHelper's instance's OnValueChanged event.
/// This allows us to register any NetworkVariable type as well as there are basically two "types of types":
/// IEquatable<T>
/// ValueType
/// From both we can then at least determine if the value indeed changed
/// </summary>
/// <typeparam name="T"></typeparam>
internal class NetworkVariableHelper<T> : NetworkVariableBaseHelper where T : unmanaged
{
private readonly NetworkVariable<T> m_NetworkVariable;
public delegate void OnMyValueChangedDelegateHandler(T previous, T next);
public event OnMyValueChangedDelegateHandler OnValueChanged;
/// <summary>
/// IEquatable<T> Equals Check
/// </summary>
private void CheckVariableChanged(IEquatable<T> previous, IEquatable<T> next)
{
if (!previous.Equals(next))
{
ValueChanged();
}
}
/// <summary>
/// ValueType Equals Check
/// </summary>
private void CheckVariableChanged(ValueType previous, ValueType next)
{
if (!previous.Equals(next))
{
ValueChanged();
}
}
/// <summary>
/// INetworkVariable's OnVariableChanged delegate callback
/// </summary>
/// <param name="previous"></param>
/// <param name="next"></param>
private void OnVariableChanged(T previous, T next)
{
if (previous is ValueType testValueType)
{
CheckVariableChanged(previous, next);
}
else
{
CheckVariableChanged(previous as IEquatable<T>, next as IEquatable<T>);
}
OnValueChanged?.Invoke(previous, next);
}
public NetworkVariableHelper(NetworkVariableBase networkVariable) : base(networkVariable)
{
m_NetworkVariable = networkVariable as NetworkVariable<T>;
m_NetworkVariable.OnValueChanged = OnVariableChanged;
}
}
/// <summary>
/// The BaseNetworkVariableHelper keeps track of:
/// The number of instances and associates the instance with the NetworkVariable
/// The number of times a specific NetworkVariable instance had its value changed (i.e. !Equal)
/// Note: This could be expanded for future tests focuses around NetworkVariables
/// </summary>
internal class NetworkVariableBaseHelper
{
private static Dictionary<NetworkVariableBaseHelper, NetworkVariableBase> s_Instances;
private static Dictionary<NetworkVariableBase, int> s_InstanceChangedCount;
/// <summary>
/// Returns the total number of registered INetworkVariables
/// </summary>
public static int InstanceCount
{
get
{
if (s_Instances != null)
{
return s_Instances.Count;
}
return 0;
}
}
/// <summary>
/// Returns total number of changes that occurred for all registered INetworkVariables
/// </summary>
public static int VarChangedCount
{
get
{
if (s_InstanceChangedCount != null)
{
var changeCount = 0;
foreach (var keyPair in s_InstanceChangedCount)
{
changeCount += keyPair.Value;
}
return changeCount;
}
return 0;
}
}
/// <summary>
/// Called by the child class NetworkVariableHelper when a value changed
/// </summary>
protected void ValueChanged()
{
if (s_Instances.ContainsKey(this))
{
if (s_InstanceChangedCount.ContainsKey(s_Instances[this]))
{
s_InstanceChangedCount[s_Instances[this]]++;
}
}
}
public NetworkVariableBaseHelper(NetworkVariableBase networkVariable)
{
if (s_Instances == null)
{
s_Instances = new Dictionary<NetworkVariableBaseHelper, NetworkVariableBase>();
}
if (s_InstanceChangedCount == null)
{
s_InstanceChangedCount = new Dictionary<NetworkVariableBase, int>();
}
// Register new instance and associated INetworkVariable
if (!s_Instances.ContainsKey(this))
{
s_Instances.Add(this, networkVariable);
if (!s_InstanceChangedCount.ContainsKey(networkVariable))
{
s_InstanceChangedCount.Add(networkVariable, 0);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 218bb185a48c6f9449e1c74f4855a774
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,245 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
public class HiddenVariableTest : NetworkBehaviour
{
}
public class HiddenVariableObject : NetworkBehaviour
{
public NetworkVariable<int> MyNetworkVariable = new NetworkVariable<int>();
public NetworkList<int> MyNetworkList = new NetworkList<int>();
public static Dictionary<ulong, int> ValueOnClient = new Dictionary<ulong, int>();
public static int ExpectedSize = 0;
public static int SpawnCount = 0;
public override void OnNetworkSpawn()
{
Debug.Log($"{nameof(HiddenVariableObject)}.{nameof(OnNetworkSpawn)}() with value {MyNetworkVariable.Value}");
MyNetworkVariable.OnValueChanged += Changed;
MyNetworkList.OnListChanged += ListChanged;
SpawnCount++;
base.OnNetworkSpawn();
}
public void Changed(int before, int after)
{
Debug.Log($"Value changed from {before} to {after} on {NetworkManager.LocalClientId}");
ValueOnClient[NetworkManager.LocalClientId] = after;
}
public void ListChanged(NetworkListEvent<int> listEvent)
{
Debug.Log($"ListEvent received: type {listEvent.Type}, index {listEvent.Index}, value {listEvent.Value}");
Debug.Assert(ExpectedSize == MyNetworkList.Count);
}
}
public class HiddenVariableTests : BaseMultiInstanceTest
{
protected override int NbClients => 4;
private NetworkObject m_NetSpawnedObject;
private List<NetworkObject> m_NetSpawnedObjectOnClient = new List<NetworkObject>();
private GameObject m_TestNetworkPrefab;
[UnitySetUp]
public override IEnumerator Setup()
{
yield return StartSomeClientsAndServerWithPlayers(useHost: true, nbClients: NbClients,
updatePlayerPrefab: playerPrefab =>
{
var networkTransform = playerPrefab.AddComponent<HiddenVariableTest>();
m_TestNetworkPrefab = PreparePrefab();
});
}
public GameObject PreparePrefab()
{
var prefabToSpawn = new GameObject("MyTestObject");
var networkObjectPrefab = prefabToSpawn.AddComponent<NetworkObject>();
MultiInstanceHelpers.MakeNetworkObjectTestPrefab(networkObjectPrefab);
prefabToSpawn.AddComponent<HiddenVariableObject>();
m_ServerNetworkManager.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab() { Prefab = prefabToSpawn });
foreach (var clientNetworkManager in m_ClientNetworkManagers)
{
clientNetworkManager.NetworkConfig.NetworkPrefabs.Add(new NetworkPrefab() { Prefab = prefabToSpawn });
}
return prefabToSpawn;
}
public IEnumerator WaitForConnectedCount(int targetCount)
{
var endTime = Time.realtimeSinceStartup + 1.0;
while (m_ServerNetworkManager.ConnectedClientsList.Count < targetCount && Time.realtimeSinceStartup < endTime)
{
yield return new WaitForSeconds(0.01f);
}
}
public IEnumerator WaitForSpawnCount(int targetCount)
{
var endTime = Time.realtimeSinceStartup + 1.0;
while (HiddenVariableObject.SpawnCount != targetCount &&
Time.realtimeSinceStartup < endTime)
{
yield return new WaitForSeconds(0.01f);
}
}
public void VerifyLists()
{
NetworkList<int> prev = null;
int numComparison = 0;
// for all the instances of NetworkList
foreach (var gameObject in m_NetSpawnedObjectOnClient)
{
// this skips despawned/hidden objects
if (gameObject != null)
{
// if we've seen another one before
if (prev != null)
{
var curr = gameObject.GetComponent<HiddenVariableObject>().MyNetworkList;
// check that the two lists are identical
Debug.Assert(curr.Count == prev.Count);
for (int index = 0; index < curr.Count; index++)
{
Debug.Assert(curr[index] == prev[index]);
}
numComparison++;
}
// store the list
prev = gameObject.GetComponent<HiddenVariableObject>().MyNetworkList;
}
}
Debug.Log($"{numComparison} comparisons done.");
}
public IEnumerator RefreshGameObects()
{
m_NetSpawnedObjectOnClient.Clear();
foreach (var netMan in m_ClientNetworkManagers)
{
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
yield return MultiInstanceHelpers.Run(
MultiInstanceHelpers.GetNetworkObjectByRepresentation(
x => x.NetworkObjectId == m_NetSpawnedObject.NetworkObjectId,
netMan,
serverClientPlayerResult));
m_NetSpawnedObjectOnClient.Add(serverClientPlayerResult.Result);
}
}
[UnityTest]
public IEnumerator HiddenVariableTest()
{
HiddenVariableObject.SpawnCount = 0;
HiddenVariableObject.ValueOnClient.Clear();
HiddenVariableObject.ExpectedSize = 0;
HiddenVariableObject.SpawnCount = 0;
Debug.Log("Running test");
var spawnedObject = Object.Instantiate(m_TestNetworkPrefab);
m_NetSpawnedObject = spawnedObject.GetComponent<NetworkObject>();
m_NetSpawnedObject.NetworkManagerOwner = m_ServerNetworkManager;
yield return WaitForConnectedCount(NbClients);
Debug.Log("Clients connected");
// ==== Spawn object with ownership on one client
var client = m_ServerNetworkManager.ConnectedClientsList[1];
var otherClient = m_ServerNetworkManager.ConnectedClientsList[2];
m_NetSpawnedObject.SpawnWithOwnership(client.ClientId);
yield return RefreshGameObects();
// === Check spawn occured
yield return WaitForSpawnCount(NbClients + 1);
Debug.Assert(HiddenVariableObject.SpawnCount == NbClients + 1);
Debug.Log("Objects spawned");
// ==== Set the NetworkVariable value to 2
HiddenVariableObject.ExpectedSize = 1;
HiddenVariableObject.SpawnCount = 0;
m_NetSpawnedObject.GetComponent<HiddenVariableObject>().MyNetworkVariable.Value = 2;
m_NetSpawnedObject.GetComponent<HiddenVariableObject>().MyNetworkList.Add(2);
yield return new WaitForSeconds(1.0f);
foreach (var id in m_ServerNetworkManager.ConnectedClientsIds)
{
Debug.Assert(HiddenVariableObject.ValueOnClient[id] == 2);
}
VerifyLists();
Debug.Log("Value changed");
// ==== Hide our object to a different client
HiddenVariableObject.ExpectedSize = 2;
m_NetSpawnedObject.NetworkHide(otherClient.ClientId);
// ==== Change the NetworkVariable value
// we should get one less notification of value changing and no errors or exception
m_NetSpawnedObject.GetComponent<HiddenVariableObject>().MyNetworkVariable.Value = 3;
m_NetSpawnedObject.GetComponent<HiddenVariableObject>().MyNetworkList.Add(3);
yield return new WaitForSeconds(1.0f);
foreach (var id in m_ServerNetworkManager.ConnectedClientsIds)
{
if (id != otherClient.ClientId)
{
Debug.Assert(HiddenVariableObject.ValueOnClient[id] == 3);
}
}
VerifyLists();
Debug.Log("Values changed");
// ==== Show our object again to this client
HiddenVariableObject.ExpectedSize = 3;
m_NetSpawnedObject.NetworkShow(otherClient.ClientId);
// ==== Wait for object to be spawned
yield return WaitForSpawnCount(1);
Debug.Assert(HiddenVariableObject.SpawnCount == 1);
Debug.Log("Object spawned");
// ==== We need a refresh for the newly re-spawned object
yield return RefreshGameObects();
// ==== Change the NetworkVariable value
// we should get all notifications of value changing and no errors or exception
m_NetSpawnedObject.GetComponent<HiddenVariableObject>().MyNetworkVariable.Value = 4;
m_NetSpawnedObject.GetComponent<HiddenVariableObject>().MyNetworkList.Add(4);
yield return new WaitForSeconds(1.0f);
foreach (var id in m_ServerNetworkManager.ConnectedClientsIds)
{
Debug.Assert(HiddenVariableObject.ValueOnClient[id] == 4);
}
VerifyLists();
Debug.Log("Values changed");
// ==== Hide our object to that different client again, and then destroy it
m_NetSpawnedObject.NetworkHide(otherClient.ClientId);
yield return new WaitForSeconds(0.2f);
m_NetSpawnedObject.Despawn();
yield return new WaitForSeconds(0.2f);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c64eb7c36fc44eadac730241b23e006
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 60efb82fa0154d6caf94fea440f167d4
timeCreated: 1627407732

View File

@@ -0,0 +1,156 @@
using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using Unity.Collections;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
public class NamedMessageTests : BaseMultiInstanceTest
{
protected override int NbClients => 2;
private NetworkManager FirstClient => m_ClientNetworkManagers[0];
private NetworkManager SecondClient => m_ClientNetworkManagers[1];
[UnityTest]
public IEnumerator NamedMessageIsReceivedOnClientWithContent()
{
var messageName = Guid.NewGuid().ToString();
var messageContent = Guid.NewGuid();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.WriteValueSafe(messageContent);
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(
messageName,
FirstClient.LocalClientId,
writer);
}
ulong receivedMessageSender = 0;
var receivedMessageContent = new Guid();
FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(
messageName,
(ulong sender, FastBufferReader reader) =>
{
receivedMessageSender = sender;
reader.ReadValueSafe(out receivedMessageContent);
});
yield return new WaitForSeconds(0.2f);
Assert.AreEqual(messageContent, receivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
}
[UnityTest]
public IEnumerator NamedMessageIsReceivedOnMultipleClientsWithContent()
{
var messageName = Guid.NewGuid().ToString();
var messageContent = Guid.NewGuid();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.WriteValueSafe(messageContent);
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(
messageName,
new List<ulong> { FirstClient.LocalClientId, SecondClient.LocalClientId },
writer);
}
ulong firstReceivedMessageSender = 0;
var firstReceivedMessageContent = new Guid();
FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(
messageName,
(ulong sender, FastBufferReader reader) =>
{
firstReceivedMessageSender = sender;
reader.ReadValueSafe(out firstReceivedMessageContent);
});
ulong secondReceivedMessageSender = 0;
var secondReceivedMessageContent = new Guid();
SecondClient.CustomMessagingManager.RegisterNamedMessageHandler(
messageName,
(ulong sender, FastBufferReader reader) =>
{
secondReceivedMessageSender = sender;
reader.ReadValueSafe(out secondReceivedMessageContent);
});
yield return new WaitForSeconds(0.2f);
Assert.AreEqual(messageContent, firstReceivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
Assert.AreEqual(messageContent, secondReceivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
}
[UnityTest]
public IEnumerator WhenSendingNamedMessageToAll_AllClientsReceiveIt()
{
var messageName = Guid.NewGuid().ToString();
var messageContent = Guid.NewGuid();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.WriteValueSafe(messageContent);
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessageToAll(messageName, writer);
}
ulong firstReceivedMessageSender = 0;
var firstReceivedMessageContent = new Guid();
FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(
messageName,
(ulong sender, FastBufferReader reader) =>
{
firstReceivedMessageSender = sender;
reader.ReadValueSafe(out firstReceivedMessageContent);
});
ulong secondReceivedMessageSender = 0;
var secondReceivedMessageContent = new Guid();
SecondClient.CustomMessagingManager.RegisterNamedMessageHandler(
messageName,
(ulong sender, FastBufferReader reader) =>
{
secondReceivedMessageSender = sender;
reader.ReadValueSafe(out secondReceivedMessageContent);
});
yield return new WaitForSeconds(0.2f);
Assert.AreEqual(messageContent, firstReceivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
Assert.AreEqual(messageContent, secondReceivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
}
[Test]
public void WhenSendingNamedMessageToNullClientList_ArgumentNullExceptionIsThrown()
{
var messageName = Guid.NewGuid().ToString();
var messageContent = Guid.NewGuid();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.WriteValueSafe(messageContent);
Assert.Throws<ArgumentNullException>(
() =>
{
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(messageName, null, writer);
});
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50770f69eb9d3604184f918a2d0674e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,145 @@
using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using Unity.Collections;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests
{
public class UnnamedMessageTests : BaseMultiInstanceTest
{
protected override int NbClients => 2;
private NetworkManager FirstClient => m_ClientNetworkManagers[0];
private NetworkManager SecondClient => m_ClientNetworkManagers[1];
[UnityTest]
public IEnumerator UnnamedMessageIsReceivedOnClientWithContent()
{
var messageContent = Guid.NewGuid();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.WriteValueSafe(messageContent);
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(
FirstClient.LocalClientId,
writer);
}
ulong receivedMessageSender = 0;
var receivedMessageContent = new Guid();
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
(ulong sender, FastBufferReader reader) =>
{
receivedMessageSender = sender;
reader.ReadValueSafe(out receivedMessageContent);
};
yield return new WaitForSeconds(0.2f);
Assert.AreEqual(messageContent, receivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, receivedMessageSender);
}
[UnityTest]
public IEnumerator UnnamedMessageIsReceivedOnMultipleClientsWithContent()
{
var messageContent = Guid.NewGuid();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.WriteValueSafe(messageContent);
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(
new List<ulong> { FirstClient.LocalClientId, SecondClient.LocalClientId },
writer);
}
ulong firstReceivedMessageSender = 0;
var firstReceivedMessageContent = new Guid();
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
(ulong sender, FastBufferReader reader) =>
{
firstReceivedMessageSender = sender;
reader.ReadValueSafe(out firstReceivedMessageContent);
};
ulong secondReceivedMessageSender = 0;
var secondReceivedMessageContent = new Guid();
SecondClient.CustomMessagingManager.OnUnnamedMessage +=
(ulong sender, FastBufferReader reader) =>
{
secondReceivedMessageSender = sender;
reader.ReadValueSafe(out secondReceivedMessageContent);
};
yield return new WaitForSeconds(0.2f);
Assert.AreEqual(messageContent, firstReceivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
Assert.AreEqual(messageContent, secondReceivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
}
[UnityTest]
public IEnumerator WhenSendingUnnamedMessageToAll_AllClientsReceiveIt()
{
var messageContent = Guid.NewGuid();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.WriteValueSafe(messageContent);
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessageToAll(writer);
}
ulong firstReceivedMessageSender = 0;
var firstReceivedMessageContent = new Guid();
FirstClient.CustomMessagingManager.OnUnnamedMessage +=
(ulong sender, FastBufferReader reader) =>
{
firstReceivedMessageSender = sender;
reader.ReadValueSafe(out firstReceivedMessageContent);
};
ulong secondReceivedMessageSender = 0;
var secondReceivedMessageContent = new Guid();
SecondClient.CustomMessagingManager.OnUnnamedMessage +=
(ulong sender, FastBufferReader reader) =>
{
secondReceivedMessageSender = sender;
reader.ReadValueSafe(out secondReceivedMessageContent);
};
yield return new WaitForSeconds(0.2f);
Assert.AreEqual(messageContent, firstReceivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, firstReceivedMessageSender);
Assert.AreEqual(messageContent, secondReceivedMessageContent);
Assert.AreEqual(m_ServerNetworkManager.LocalClientId, secondReceivedMessageSender);
}
[Test]
public void WhenSendingNamedMessageToNullClientList_ArgumentNullExceptionIsThrown()
{
var messageContent = Guid.NewGuid();
var writer = new FastBufferWriter(1300, Allocator.Temp);
using (writer)
{
writer.WriteValueSafe(messageContent);
Assert.Throws<ArgumentNullException>(
() =>
{
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(null, writer);
});
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5affb61a1b56d44c80d0e2d55cc04aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4cd7fa97c73f3674b9cce18b1e0a6874
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,275 @@
#if MULTIPLAYER_TOOLS
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Unity.Collections;
using Unity.Multiplayer.Tools.MetricTypes;
using Unity.Netcode.RuntimeTests.Metrics.Utility;
using UnityEngine;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests.Metrics
{
public class MessagingMetricsTests : DualClientMetricTestBase
{
const uint MessageNameHashSize = 8;
const uint MessageOverhead = MessageNameHashSize;
protected override int NbClients => 2;
[UnityTest]
public IEnumerator TrackNetworkMessageSentMetric()
{
var waitForMetricValues = new WaitForMetricValues<NetworkMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageSent);
var messageName = Guid.NewGuid();
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(messageName);
Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), FirstClient.LocalClientId, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
var networkMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, networkMessageSentMetricValues.Count);
var networkMessageEvent = networkMessageSentMetricValues.First();
Assert.AreEqual(nameof(NamedMessage), networkMessageEvent.Name);
Assert.AreEqual(FirstClient.LocalClientId, networkMessageEvent.Connection.Id);
}
[UnityTest]
public IEnumerator TrackNetworkMessageSentMetricToMultipleClients()
{
var waitForMetricValues = new WaitForMetricValues<NetworkMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageSent);
var messageName = Guid.NewGuid();
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(messageName);
Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), new List<ulong> { FirstClient.LocalClientId, SecondClient.LocalClientId }, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
var networkMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(2, networkMessageSentMetricValues.Count(x => x.Name.Equals(nameof(NamedMessage))));
}
[UnityTest]
public IEnumerator TrackNetworkMessageReceivedMetric()
{
var messageName = Guid.NewGuid();
LogAssert.Expect(LogType.Log, $"Received from {Server.LocalClientId}");
FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(messageName.ToString(), (ulong sender, FastBufferReader payload) =>
{
Debug.Log($"Received from {sender}");
});
var waitForMetricValues = new WaitForMetricValues<NetworkMessageEvent>(FirstClientMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageReceived);
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(messageName);
Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), FirstClient.LocalClientId, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
var networkMessageReceivedValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, networkMessageReceivedValues.Count(x => x.Name.Equals(nameof(NamedMessage))));
var namedMessageReceived = networkMessageReceivedValues.First();
Assert.AreEqual(Server.LocalClientId, namedMessageReceived.Connection.Id);
}
[UnityTest]
public IEnumerator TrackNamedMessageSentMetric()
{
var waitForMetricValues = new WaitForMetricValues<NamedMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.NamedMessageSent);
var messageName = Guid.NewGuid();
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(messageName);
Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), FirstClient.LocalClientId, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
var namedMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, namedMessageSentMetricValues.Count);
var namedMessageSent = namedMessageSentMetricValues.First();
Assert.AreEqual(messageName.ToString(), namedMessageSent.Name);
Assert.AreEqual(FirstClient.LocalClientId, namedMessageSent.Connection.Id);
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead, namedMessageSent.BytesCount);
}
[UnityTest]
public IEnumerator TrackNamedMessageSentMetricToMultipleClients()
{
var waitForMetricValues = new WaitForMetricValues<NamedMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.NamedMessageSent);
var messageName = Guid.NewGuid();
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(messageName);
Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), new List<ulong> { FirstClient.LocalClientId, SecondClient.LocalClientId }, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
var namedMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(2, namedMessageSentMetricValues.Count);
Assert.That(namedMessageSentMetricValues.Select(x => x.Name), Has.All.EqualTo(messageName.ToString()));
Assert.That(namedMessageSentMetricValues.Select(x => x.BytesCount), Has.All.EqualTo(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead));
}
[UnityTest]
public IEnumerator TrackNamedMessageSentMetricToSelf()
{
var waitForMetricValues = new WaitForMetricValues<NamedMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.NamedMessageSent);
var messageName = Guid.NewGuid();
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(messageName);
Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), Server.LocalClientId, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
waitForMetricValues.AssertMetricValuesHaveNotBeenFound();
}
[UnityTest]
public IEnumerator TrackNamedMessageReceivedMetric()
{
var waitForMetricValues = new WaitForMetricValues<NamedMessageEvent>(FirstClientMetrics.Dispatcher, NetworkMetricTypes.NamedMessageReceived);
var messageName = Guid.NewGuid();
LogAssert.Expect(LogType.Log, $"Received from {Server.LocalClientId}");
FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(messageName.ToString(), (ulong sender, FastBufferReader payload) =>
{
Debug.Log($"Received from {sender}");
});
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(messageName);
Server.CustomMessagingManager.SendNamedMessage(messageName.ToString(), FirstClient.LocalClientId, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
var namedMessageReceivedValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, namedMessageReceivedValues.Count);
var namedMessageReceived = namedMessageReceivedValues.First();
Assert.AreEqual(messageName.ToString(), namedMessageReceived.Name);
Assert.AreEqual(Server.LocalClientId, namedMessageReceived.Connection.Id);
Assert.AreEqual(FastBufferWriter.GetWriteSize(messageName) + MessageOverhead, namedMessageReceived.BytesCount);
}
[UnityTest]
public IEnumerator TrackUnnamedMessageSentMetric()
{
var message = Guid.NewGuid();
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(message);
Server.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, writer);
}
var waitForMetricValues = new WaitForMetricValues<UnnamedMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.UnnamedMessageSent);
yield return waitForMetricValues.WaitForMetricsReceived();
var unnamedMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, unnamedMessageSentMetricValues.Count);
var unnamedMessageSent = unnamedMessageSentMetricValues.First();
Assert.AreEqual(FirstClient.LocalClientId, unnamedMessageSent.Connection.Id);
Assert.AreEqual(FastBufferWriter.GetWriteSize(message), unnamedMessageSent.BytesCount);
}
[UnityTest]
public IEnumerator TrackUnnamedMessageSentMetricToMultipleClients()
{
var message = Guid.NewGuid();
var waitForMetricValues = new WaitForMetricValues<UnnamedMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.UnnamedMessageSent);
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(message);
Server.CustomMessagingManager.SendUnnamedMessage(new List<ulong> { FirstClient.LocalClientId, SecondClient.LocalClientId }, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
var unnamedMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(2, unnamedMessageSentMetricValues.Count);
Assert.That(unnamedMessageSentMetricValues.Select(x => x.BytesCount), Has.All.EqualTo(FastBufferWriter.GetWriteSize(message)));
var clientIds = unnamedMessageSentMetricValues.Select(x => x.Connection.Id).ToList();
Assert.Contains(FirstClient.LocalClientId, clientIds);
Assert.Contains(SecondClient.LocalClientId, clientIds);
}
[UnityTest]
public IEnumerator TrackUnnamedMessageSentMetricToSelf()
{
var waitForMetricValues = new WaitForMetricValues<UnnamedMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.UnnamedMessageSent);
var messageName = Guid.NewGuid();
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(messageName);
Server.CustomMessagingManager.SendUnnamedMessage(Server.LocalClientId, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
waitForMetricValues.AssertMetricValuesHaveNotBeenFound();
}
[UnityTest]
public IEnumerator TrackUnnamedMessageReceivedMetric()
{
var message = Guid.NewGuid();
var waitForMetricValues = new WaitForMetricValues<UnnamedMessageEvent>(FirstClientMetrics.Dispatcher, NetworkMetricTypes.UnnamedMessageReceived);
using (var writer = new FastBufferWriter(1300, Allocator.Temp))
{
writer.WriteValueSafe(message);
Server.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, writer);
}
yield return waitForMetricValues.WaitForMetricsReceived();
var unnamedMessageReceivedValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, unnamedMessageReceivedValues.Count);
var unnamedMessageReceived = unnamedMessageReceivedValues.First();
Assert.AreEqual(Server.LocalClientId, unnamedMessageReceived.Connection.Id);
Assert.AreEqual(FastBufferWriter.GetWriteSize(message), unnamedMessageReceived.BytesCount);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2490ed51138306e4d92f8e9dcfc34462
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
#if MULTIPLAYER_TOOLS
using System;
using System.Collections;
using NUnit.Framework;
using Unity.Multiplayer.Tools.NetStats;
using UnityEngine.TestTools;
namespace Unity.Netcode.RuntimeTests.Metrics
{
public class MetricsDispatchTests
{
private int m_NbDispatches;
private NetworkManager m_NetworkManager;
[SetUp]
public void SetUp()
{
var networkManagerStarted = NetworkManagerHelper.StartNetworkManager(
out m_NetworkManager,
NetworkManagerHelper.NetworkManagerOperatingMode.Host,
new NetworkConfig
{
TickRate = 1,
});
Assert.IsTrue(networkManagerStarted);
var networkMetrics = m_NetworkManager.NetworkMetrics as NetworkMetrics;
networkMetrics.Dispatcher.RegisterObserver(new MockMetricsObserver(() => m_NbDispatches++));
}
[TearDown]
public void TearDown()
{
NetworkManagerHelper.ShutdownNetworkManager();
}
[UnityTest]
public IEnumerator VerifyNetworkMetricsDispatchesOncePerFrame()
{
var nbDispatchesBeforeFrame = m_NbDispatches;
yield return null; // Wait one frame so dispatch occurs
var nbDispatchesAfterFrame = m_NbDispatches;
Assert.AreEqual(1, nbDispatchesAfterFrame - nbDispatchesBeforeFrame);
}
private class MockMetricsObserver : IMetricObserver
{
private readonly Action m_OnObserve;
public MockMetricsObserver(Action onObserve)
{
m_OnObserve = onObserve;
}
public void Observe(MetricCollection collection)
{
m_OnObserve?.Invoke();
}
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4833f15c8a59407abbb8532ea64b5683
timeCreated: 1633451646

Some files were not shown because too many files have changed in this diff Show More