Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
## [1.1.0-exp.1] - 2023-09-18

### Added

* source generator can now be configure to enable/disable logs, report timings. It also possible to set the minimal log level (by default is now Error).
* new public template specs and generator documentation
* Added convenience methods for getting the clientworld / serverworld (or thin client list) added to ClientServerBootstrap
* Additional analytics events. Multiplayer tools fields, prediction switching counters, tick rate configuration.
* New method on the `PredictedFixedStepSimulationSystemGroup` class to initialise the rate as a multiple of a base tick rate.
* `Packet Fuzz %` is now configurable via the Network Simulator. It's a security tool that should not be enabled during normal testing. It's purpose is to test against malicious MitM attacks, which aim to take down your server via triggering exceptions during packet deserialization. Thus, all deserialization code should be written with safeguards and tolerances, ensuring your logic will fail gracefully.
* CopyInputToCommandBufferSystemGroup group, that contains all the system that copy IInputCommandData to the underlying ICommand buffer. This let you now target this group with the guarantee that all inputs are not copied after it run.
* CopyCommandBufferToInputSystemGroup group, that contains all the system that copy ICommandData to their IInputCommandData representation. It runs first in the prediction loop and you can easily target it to do logic before or after the input are updated.
* GhostSpawnClassificationSystemGroup, that is aimed to contains all your classification systems in one place.
* Error messages to some missing `NetworkDriver.Begin/EndSend` locations.
* defining `ENABLE_UNITY_RPC_REGISTRATION_LOGGING` will now log information about registered RPCs during netcode startup
* We now automatically detect `Application.runInBackground` being set to false during multiplayer gameplay (a common error), and give advice via a suppressible error log as to why it should be enabled.
* We introduced the new InputBufferData<T> buffer, that is used as underlying container for all IInputComponentData.
* conditional compilation for some public interfaces in DefaultDriverBuilder to exclude the use of RegisterServer methods for WebGL build (they can't listen). It is possible to do everything manually, but the helper methods are not present anymore.
* new method for creating a NetworkDriver using WebSocketNetworkInterface.
* Added two new values to the `NetworkStreamDisconnectReason` enum: `AuthenticationFailure` and `ProtocolError`. The former is returned when the transport is configured to use DTLS or TLS and it fails to establish a secure session. The latter is returned for low-level unexpected transport errors (e.g. malformed packets in a TCP stream).

### Changed

* relaxed public field condition for variants. When declaring a ghost component variations, the variant fields are not required to be public. This make the type pretty much unusable for any other purpose but declaring the type serialisation.
* Increased the ThinClient cap on `MultiplayerPlayModePreferences.MaxNumThinClients` from 32 to 1k, to facilitate some amount of in-editor testing of high-player-counts.
* NetcodeTestWorld updates the worlds in the same way the package does: first server, then all clients worlds.
* When Dedicated Server package is installed, the PlayMode Type value is overridden by the active Multiplayer Role.

### Deprecated

* The public `PredictedFixedStepSimulationGroup.TimeStep`. You should always use the `PredictedFixedStepSimulationGroup.ConfigureTimeStep` to setup the rate of the `PredictedFixedStepSimulationSystemGroup.`.
* the IInputBufferData interface (internal for code-gen use but public) has been deprecated and will be removed in the 1.2 release.

### Fixed

* incorrect code generated serialization and calculated ChangeMask bits for component and buffers when the GhostFieldAttribute.Composite flag is set to true (in some cases).
* wrong check for typename in GhostComponentVariation
* missing region in unquantized float template, causing errors when used for interpolated field.
* improper check when the ClientServerSetting asset is saved, causing worker process not seeing the changes in the settings.
* The server world was not setting the correct rate to the group, if that was not done as part of the bootstrap.
* Exception thrown when the NetDbg tools is connecting to either the editor or player.
* Renamed (and marginally improved) the "Multiplayer PlayMode Tools" Window to the "PlayMode Tools" Window, to disambiguate it from "[MPPM] Multiplayer Play Mode" (an Engine feature).
* Attempting to access internals of Netcode for Entities (e.g. via Assembly Definition References) would cause compiler errors due to `MonoPInvokeCallbackAttribute` being ambiguous between AOT and Unity.Entities.
* Packet dump logging exception when using relevancy, despawns, and packet dumps enabled. Also fixed performance overhead (as it was erroneously logging stack traces).
* An issue with variant hash calculation in release build, causing ghost prefab hash being different in between development/editor and release build.
* GhostUpdateSystem.RestoreFromBackup does not always invalidate/bump the chunk version for a component, but only if the chunk as changed since the last time the restore occurred.
* Issue in TryGetHashElseZero, that was using the ComponentType.GetDebugName to calculate the variant hash, leading incorrect results in a release player build
* A `NetworkDriver.BeginSend` error causing an infinite loop in the `RpcSystem`.
* Deprecated Analytics API when using 2023.2 or newer.
* compilation issue when using 2023.2, caused by an ambiguous symbol (define in both Editor and in Entities.Editor assembly)
* Errant netcode systems no longer show up in the DefaultWorld: `PhysicsWorldHistory`, `SwitchPredictionSmoothingPhysicsOrderingSystem`, `SwitchPredictionSmoothingSystem`, `GhostPresentationGameObjectTransformSystem`, `GhostPresentationGameObjectSystem`, and `SetLocalPlayerGraphicsColorsSystem`.
* Previous was hard to retrieve the generated buffer for a given IInputComponentData. Now is easy as doing something like InputBufferData<MyInputComponent>.
* Compilation error when building for WebGL
* SnapshotDataLookupCache not created in the correct order, causing custom classification system using the SnapshotBufferHelper to throw exceptions, because the cache was not initialised.
* A replicated `[GhostEnabledBit]` flag component would throw an `ArgumentException` when added to a Prespawned Ghost due to `ArchetypeChunk.GetDynamicComponentDataArrayReinterpret`.
  • Loading branch information
Unity Technologies committed Sep 18, 2023
1 parent d48c2f3 commit a2764a0
Show file tree
Hide file tree
Showing 166 changed files with 4,670 additions and 2,334 deletions.
4 changes: 4 additions & 0 deletions .buginfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
system: jira
server: jira.unity3d.com
project: ECSB
issuetype: Bug
1 change: 1 addition & 0 deletions .footignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ValidationExceptions.json
168 changes: 140 additions & 28 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Documentation~/TableOfContents.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
* [Physics](physics.md)
* [Prediction](prediction.md)
* [Optimizations](optimizations.md)
* [Source Generators](sourcegenerators.md)
* [Debugging and Tools](debugging.md)
* [Playmode-Tool](playmode-tool.md)
* [Logging](logging.md)
* [Metrics](metrics.md)
* [Generator Debugging](sourcegenerators.md#how-to-debug-generator-problems)
101 changes: 89 additions & 12 deletions Documentation~/client-server-worlds.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,29 @@ In the example above, we declared that the `MySystem` system should **only** be
## Bootstrap
When the Netcode for Entities package is added to your project, a new default [bootstrap](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientServerBootstrap.html) is added to the project.

The default bootstrap creates client server Worlds automatically at startup.
It populates them with the systems defined in the attributes you have set. This is useful when you are working in the Editor and you enter play-mode with your game scene opened.
But in a standalone game, or when you want to use some sort of frontend menu, you might want to delay the World creation, i.e you can use the same executable as both a client and server.
The default bootstrap creates the client and server Worlds automatically at startup:
```c#
public virtual bool Initialize(string defaultWorldName)
{
CreateDefaultClientServerWorlds();
return true;
}
```

It populates them with the systems defined by the `[WorldSystemFilter(...)]` attributes you have set. This is useful when you are working in the Editor, and you enter play-mode with your game scene opened.
But in a standalone game - where you typically want to use some sort of frontend menu - you might want to delay the World creation, and/or choose which netcode worlds to spawn.

It it possible to create your own bootstrap class and customise your game flow by creating a class that extends `ClientServerBootstrap` and override the default `Initialize` method implementation.
You can re-use in your class mostly of the provided helper methods that can let you create `client`, `server`, `thin-client` and `local simulation` worlds. See for more details [ClientServerBootstrap methods](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientServerBootstrap.html).
E.g. Consider a "Hosting a Client Hosted Server" flow vs a "Connect as a client to a Dedicated Server via Matchmaking" flow.
In the former case, you want to add (and connect via IPC to) an in-proc server world. In the latter, you only want to create a Client world.

The following code example shows how to override the default bootstrap to prevent automatic creation of the client server worlds:
It it possible to create your own bootstrap class and customise your game flow.
Create a class that extends `ClientServerBootstrap` (e.g. `MyGameSpecificBootstrap`), and override the default `Initialize` method implementation.
In your derived class, you can mostly re-use the provided helper methods, which let you create `client`, `server`, `thin-client` and `local simulation` worlds. See for more details [ClientServerBootstrap methods](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.ClientServerBootstrap.html).

The following code example shows how to override the default bootstrap, to prevent automatic creation of the client server worlds:

```c#
public class ExampleBootstrap : ClientServerBootstrap
public class MyGameSpecificBootstrap : ClientServerBootstrap
{
public override bool Initialize(string defaultWorldName)
{
Expand All @@ -70,6 +82,29 @@ public class ExampleBootstrap : ClientServerBootstrap
}
```

Then, when you're ready to create the various netcode worlds, call:
```c#
void OnPlayButtonClicked()
{
// Typically this:
var clientWorld = ClientServerBoostrap.CreateClientWorld();
// And/Or this:
var serverWorld = ClientServerBoostrap.CreateServerWorld();

// And/Or something like this, for soak testing:
const int numThinClientWorldsForStressTest = 10;
for(int i = 0; i < numThinClientWorldsForStressTest; i++)
ClientServerBoostrap.CreateThinClientWorld();

// Or the following, which creates worlds smartly based on:
// - The Playmode Tool setting specified in the editor.
// - The current build type, if used in a player.
ClientServerBootstrap.CreateDefaultClientServerWorlds();
}
```

We have NetcodeSamples showcasing how to manage scene and sub-scene loading with this World creation setup, as well as proper netcode world disposal (when leaving the gameplay loop).

## Fixed and dynamic time-step

When you use Netcode for Entities, the server always updates **at a fixed time-step**. The package also limits the maximum number of fixed-step iterations per frame, to make sure that the server does not end up in a state where it takes several seconds to simulate a single frame.
Expand Down Expand Up @@ -155,12 +190,54 @@ public World MigrateWorld(World sourceWorld)

## Thin Clients

Thin clients are a tool to help test and debug in the editor by running simulated dummy clients with your normal client and server worlds. See the _Playmode Tools_ section above for how to configure them
Thin clients are a tool to help test and debug in the editor, by running simulated dummy clients alongside your normal client and server worlds.
See the _Playmode Tools_ section above for how to configure them.

These clients are heavily stripped down, and should run as little logic as possible (so they don't put a heavy load on the CPU while testing).
Each thin client added adds a little bit of extra work to be computed each frame.

Only systems which have explicitly been set up to run on thin client worlds will run, marked with the `WorldSystemFilterFlags.ThinClientSimulation` flag on the `WorldSystemFilter` attribute.
No rendering is done for thin client data, so they are invisible to the presentation.

These clients are heavily stripped down and should run as little logic as possible so they don't put a heavy load on the CPU while testing. Each thin client added adds a little bit of extra work to be computed each frame.
In some cases, you might need to check if your system logic should be running for thin clients, and then early out or cancel processing.
The `World.IsThinClient()` extension methods can be used in these cases.

Only systems which have explicitly been set up to run on thin client worlds will run, marked with the `WorldSystemFilterFlags.ThinClientSimulation` flag on the `WorldSystemFilter` attribute. No rendering is done for thin client data so they are invisible to the presentation.
### Thin Client Workflow Recommendations

In some cases like in `MonoBehaviour` scripts you might need to check if it's running on a thin client and then early out or cancel processing, the `World.IsThinClient()` can be used in those cases.
Thin clients can be used in a variety of ways to help test multiplayer games. We recommend the following:
1) Thin Clients allow you to quickly test client flows: Things like team assignment, spawn locations, leaderboards, UI etc.
2) Thin Clients created in built players, allowing stress and soak testing of your game servers. _E.g. You may wish to add a configuration option to automatically create N Thin Client worlds (alongside your normal client world). Have each thin client "follow the leader" and automatically attempt to join the same IP Address and Port as your main client world. Thus, you can use your existing UI flows (matchmaking, lobby, relay etc.) to get these thin clients into the stress test target server._
3) Thin Clients controlled by a second input source. I.e. Multiplayer games often have complex PvP interactions, and therefore you often wish to have an AI perform a specific action while your client is interacting with it. _Examples: Crouch, go prone, jump, run diagonally backwards, reload, enable shield, activate ability etc. Hooking thin client controls up to keyboard commands allows you to test these situations without requiring a play-test (or a second dev)._
You can also hookup thin clients to have mirrored inputs of the tester, with similarly good results.

Most commonly the only important work they need to do is generate random inputs for the server to process. These inputs usually need to be added to a manually created dummy entity as no ghost spawning is done on thin clients. Not even for it's own local ghost/player.
### Thin Client Samples
- [NetcodeSamples > HelloNetcode > ThinClient](https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/NetcodeSamples/Assets/Samples/HelloNetcode/2_Intermediate/06_ThinClients)
- [NetcodeSamples > Asteroids](https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/f22bb949b3865c68d5fc588a6e8d032096dc788a/NetcodeSamples/Assets/Samples/Asteroids/Client/Systems/InputSystem.cs#L66)

### Setting up inputs for Thin Clients

Thin Client do not work out of the box with `AutoCommandTarget`.
This is because `AutoCommandTarget` requires the same ghost to exist on both the client and the server.
But - because Thin Clients do not create ghosts - `AutoCommandTarget` does not have a client entity to hookup to.
Thus, you need to set up the `CommandTarget` component on the connection entity yourself.

`IInputComponentData` is our newest input API. It automatically handles writing out inputs (from your input struct) directly to the replicated Dynamic Buffer.
Additionally: When we bake the ghost entity - and said entity contains an `IInputCommandData` composed struct - we automatically add an underlying `ICommandData` dynamic buffer to the entity.
However: Once again, this baking process is not available on Thin Clients, as Thin Clients do not create ghosts entities.

`ICommandData` is also supported with Thin Clients ([details here](command-stream.md)), but note that you'll need to perform the same thin client hookup work (below) that you do with `IInputComponentData`.

Therefore, to support sending input from a Thin Client, you must do the following:

1) Create an entity containing your `IInputCommmandData` (or `ICommandData`) component, as well as the code-generated `YourNamespace.YouCommandNameInputBufferData` dynamic buffer. **This may appear to throw a missing assembly definition error in your IDE, but it will work.**
2) You need to setup the `CommandTarget` component to point to this entity. Therefore, in a `[WorldSystemFilter(WorldSystemFilterFlags.ThinClientSimulation)]` system:
```c#
var myDummyGhostCharacterControllerEntity = entityManager.CreateEntity(typeof(MyNamespace.MyInputComponent), typeof(InputBufferData<MyNamespace.MyInputComponent>));
var myConnectionEntity = SystemAPI.GetSingletonEntity<NetworkId>();
entityManager.SetComponentData(myConnectionEntity, new CommandTarget { targetEntity = myDummyGhostCharacterControllerEntity }); // This tells the netcode package which entity it should be sending inputs for.
```

And on the server (where you spawn the actual character controller ghost for the thinClient, which will be replicated to all proper clients), you **_only_** need to setup the `CommandTarget` for Thin Clients (as presumably your player ghosts all use `AutoCommandTarget`. If you're **_not_** using `AutoCommandTarget`, you probably already perform this action for all clients already).
```c#
entityManager.SetComponentData(thinClientConnectionEntity, new CommandTarget { targetEntity = thinClientsCharacterControllerGhostEntity });
```
11 changes: 6 additions & 5 deletions Documentation~/debugging.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Debugging

| **Topic** | **Description** |
|:-------------------------------------| :----------------------- |
| **[PlayModeTool](playmode-tool.md)** | Logging in Netcode for Entities |
| **[Logging](logging.md)** | Logging in Netcode for Entities |
| **[Metrics](metrics.md)** | Metrics in Netcode for Entities |
| **Topic** | **Description** |
|:--------------------------------------------|:--------------------------------------|
| **[PlayModeTool](playmode-tool.md)** | Configure logging and packet dumps |
| **[Logging](logging.md)** | Logging in Netcode for Entities |
| **[Metrics](metrics.md)** | Metrics in Netcode for Entities |
| **[SourceGenerators](sourcegenerators.md)** | SourceGenerator debugging and logging |
26 changes: 12 additions & 14 deletions Documentation~/ghost-snapshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ For a component to support serialization, the following conditions must be met:
* `GhostField` `MaxSmoothingDistance` allows you to disable interpolation when the values change more than the specified limit between two snapshots. This is useful for dealing with teleportation, for example.
* Finally the `GhostField` has a `SubType` property which can be set to an integer value to use special serialization rules supplied for that specific field.

>![NOTE] Speaking of teleportation: To support _short range_ teleportation, you'd need some other replicated bit to distinguish a teleport from a move (lerp).
>[!NOTE] Speaking of teleportation: To support _short range_ teleportation, you'd need some other replicated bit to distinguish a teleport from a move (lerp).
## Authoring dynamic buffer serialization
Dynamic buffers serialization is natively supported. **Unlike components, to replicate a buffer, all public fields must be marked with at `[GhostField]` attribute.**
>![NOTE] This restriction has been added to guarantee that: In the case where an element is added to the buffer, when it is replicated to the client, all fields on said element will have meaningful values.
>[!NOTE] This restriction has been added to guarantee that: In the case where an element is added to the buffer, when it is replicated to the client, all fields on said element will have meaningful values.
> This restriction may be removed in the future (e.g. by instead, defaulting this undefined behaviour to `default(T)`).
```csharp
Expand Down Expand Up @@ -210,8 +210,7 @@ A component can also set __SendToOwner__ in the __GhostComponentAttribute__ to s
* __SendToNonOwner__ - the component is sent to all clients except the one who owns the ghost
* __All__ - the component is sent to all clients.

>![NOTE] By setting either the `SendTypeOptimisation` and/or `SendToOwner` (to specify to which types of client(s) the component should be replicated to), will not
> affect the presence of the component on the prefab, nor modify the component on the clients which did not receive it.
>[!NOTE] By setting either the `SendTypeOptimisation` and/or `SendToOwner` (to specify to which types of client(s) the component should be replicated to), will not affect the presence of the component on the prefab, nor modify the component on the clients which did not receive it.
---

Expand All @@ -223,15 +222,15 @@ In addition to the default out-of-the-box types you can also:

Please check how to [use and write templates](ghost-types-templates.md#Defining additional templates) for more information on the topic.

>![NOTE] Writing templates is non-trivial. If it is possible to replicate the type simply by adding GhostFields, it's often easier to just do so.
> If you do not have access to a type, create a Variant instead (see section below).
>[!NOTE] Writing templates is non-trivial. If it is possible to replicate the type simply by adding GhostFields, it's often easier to just do so. If you do not have access to a type, create a Variant instead (see section below).
## Ghost Component Variants
The [GhostComponentVariationAttribute](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostComponentVariationAttribute.html) is special attribute tha can be used to declare at compile time
a "replication schema" for a type, without the need to markup the fields in the original type, or the original type itself. <br/>
>![NOTE]This new declared type act as proxy from a code-generation perspective. Instead of using the original type, the code-generation system use the declared "variant" to generate a specific
> version of the serialization code.
> ![NOTE] **Ghost components variants for `IBufferElementData` are not fully supported.**

>[!NOTE]This new declared type act as proxy from a code-generation perspective. Instead of using the original type, the code-generation system use the declared "variant" to generate a specific version of the serialization code.
>[!NOTE] **Ghost components variants for `IBufferElementData` are not fully supported.**
The `GhostComponentVariationAttribute` has some specific use-cases in mind:
- Variants allow user-code (you) to declare serialization rules for a component that you don't have direct write access too (_i.e. components in a package or external assembly_). _Example: Making `Unity.Entities.LocalTransform` replicated._
Expand All @@ -257,7 +256,7 @@ The attribute constructor takes a few arguments:
* The user-friendly `string variantName`, which will allow you to better interpret `GhostAuthoringInspectionComponent` UI.

Then, for each field in the original struct (in this case, `LocalTransform`) that you wish to replicate, you should add a `GhostField` attribute like you usually do, and define the field identically to that of the base struct.
>~[NOTE] Only members that are present in the component type are allowed. Validation occurs at compile time, and exceptions are thrown in case this rule is not respected.
>[!NOTE] Only members that are present in the component type are allowed. Validation occurs at compile time, and exceptions are thrown in case this rule is not respected.
An optional `GhostComponentAttribute` attribute can be added to the variant to further specify the component serialization properties.

Expand Down Expand Up @@ -364,8 +363,7 @@ When present, the component can't be customized in the inspector, nor can a prog
For example: The Netcode for Entities package requires the [GhostOwner](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/api/Unity.NetCode.GhostOwner.html)
to be added to all ghost types, sent for all ghost types, and serialized using the default variant. Thus, we add the `[DontSupportPrefabOverride]` attribute to it.

>![NOTE] Components on child entities are not serialised by default, thus by default when you look to `GhostAuthoringInspectionComponent` on a child GameObject you will
> see that the selected variant for the type is the `DontSerializeVariant`.
>[!NOTE] Components on child entities are not serialised by default, thus by default when you look to `GhostAuthoringInspectionComponent` on a child GameObject you will see that the selected variant for the type is the `DontSerializeVariant`.
<img src="images/dontserialize-variant.png" alt="DontSerializeVariant" width=600/>

Expand All @@ -380,5 +378,5 @@ To understand what is being put on the wire in the Netcode, you can use the snap
To open the tool, go to menu: __Multiplayer &gt; Open NetDbg__, and the tool opens in a browser window. It displays a vertical bar for each received snapshot, with a breakdown of the snapshot’s ghost types, size etc.

To see more detailed information about the snapshot, click on one of the bars.
> [!NOTE]
> This tool is a prototype. In future versions of the package, it will integrate with the Unity Profiler so you can easily correlate network traffic with memory usage and CPU performance.

>[!NOTE] This tool is a prototype. In future versions of the package, it will integrate with the Unity Profiler so you can easily correlate network traffic with memory usage and CPU performance.
Loading

0 comments on commit a2764a0

Please sign in to comment.