Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
## [1.3.6] - 2024-10-16

### Changed

* Improved XML document for `NetworkStreamDriver.ConnectionEventsForTick`.
* Updated entities packages dependencies

### Fixed

* an issue with netcode source generated files, causing multiple Burst.CompileAsync invocation, ending up in stalling the editor and the player for long time, and / or causing crashes.
* Issue where `OverrideAutomaticNetcodeBootstrap` instances in scenes would be ignored in the Editor if 'Fast Enter Play-Mode Options' is disabled (i.e. when domain reloads triggered after clicking to enter play-mode).
* Longstanding API documentation errors across Netcode for Entities API documentation.
  • Loading branch information
Unity Technologies committed Oct 16, 2024
1 parent 55e467f commit 25d32f8
Show file tree
Hide file tree
Showing 65 changed files with 909 additions and 478 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@
uid: changelog
---

# Changelog
## [1.3.6] - 2024-10-16

### Changed

* Improved XML document for `NetworkStreamDriver.ConnectionEventsForTick`.
* Updated entities packages dependencies

### Fixed

* an issue with netcode source generated files, causing multiple Burst.CompileAsync invocation, ending up in stalling the editor and the player for long time, and / or causing crashes.
* Issue where `OverrideAutomaticNetcodeBootstrap` instances in scenes would be ignored in the Editor if 'Fast Enter Play-Mode Options' is disabled (i.e. when domain reloads triggered after clicking to enter play-mode).
* Longstanding API documentation errors across Netcode for Entities API documentation.

## [1.3.2] - 2024-09-06

Expand Down
104 changes: 63 additions & 41 deletions Documentation~/playmode-tool.md

Large diffs are not rendered by default.

165 changes: 118 additions & 47 deletions Editor/Authoring/GhostComponentAnalytics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Unity.NetCode.Analytics;
using Unity.Networking.Transport;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEngine.Analytics;

namespace Unity.NetCode.Editor
Expand Down Expand Up @@ -40,14 +41,12 @@ static GhostScaleAnalyticsData ComputeScaleData()
{
var data = new GhostScaleAnalyticsData
{
PlayerCount = NetCodeAnalyticsState.GetPlayerCount(),
Settings = new PlaymodeSettings
{
ThinClientCount = MultiplayerPlayModePreferences.RequestedNumThinClients,
SimulatorEnabled = MultiplayerPlayModePreferences.SimulatorEnabled,
Delay = MultiplayerPlayModePreferences.PacketDelayMs,
DropPercentage = MultiplayerPlayModePreferences.PacketDropPercentage,
FuzzPercentage = MultiplayerPlayModePreferences.PacketFuzzPercentage,
Jitter = MultiplayerPlayModePreferences.PacketJitterMs,
PlayModeType = MultiplayerPlayModePreferences.RequestedPlayType.ToString(),
SimulatorPreset = MultiplayerPlayModePreferences.CurrentNetworkSimulatorPreset
Expand All @@ -62,39 +61,6 @@ static GhostScaleAnalyticsData ComputeScaleData()
var numServerWorlds = 0;
foreach (var world in World.All)
{
void CollectMainClientData()
{
TryGetSingleton(world, out data.ClientTickRate);
data.MainClientData.NumOfSpawnedGhost = CountSpawnedGhosts(world.EntityManager);
TryGetSingleton(world, out data.ClientServerTickRate);

var spawnedGhostCount = CountSpawnedGhosts(world.EntityManager);
TryGetSingleton<GhostRelevancy>(world, out var ghostRelevancy);
using var predictedGhostQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<PredictedGhost>());
var predictionCount = predictedGhostQuery.CalculateEntityCountWithoutFiltering();
TryGetSingleton<PredictionSwitchingAnalyticsData>(world, out var predictionSwitchingAnalyticsData);
data.MainClientData = new MainClientData
{
RelevancyMode = ghostRelevancy.GhostRelevancyMode,
NumOfSpawnedGhost = spawnedGhostCount,
NumOfPredictedGhosts = predictionCount,
NumSwitchToInterpolated = predictionSwitchingAnalyticsData.NumTimesSwitchedToInterpolated,
NumSwitchToPredicted = predictionSwitchingAnalyticsData.NumTimesSwitchedToPredicted,
};
}

void CollectServerData()
{
data.ServerSpawnedGhostCount = CountSpawnedGhosts(world.EntityManager);

data.GhostTypes = CollectGhostTypes(world.EntityManager);
data.GhostTypeCount = data.GhostTypes.Length;

data.SnapshotTargetSize = TryGetSingleton<NetworkStreamSnapshotTargetSize>(world, out var snapshotTargetSize)
? snapshotTargetSize.Value
: NetworkParameterConstants.MaxMessageSize;
}

var sent = NetCodeAnalyticsState.GetUpdateLength(world);
if (sent > 0)
{
Expand All @@ -118,6 +84,47 @@ void CollectServerData()
}
numMainClientWorlds++;
}

continue;

void CollectServerData()
{
data.ServerSpawnedGhostCount = CountSpawnedGhosts(world.EntityManager);

data.GhostTypes = CollectGhostTypes(world.EntityManager);
data.GhostTypeCount = data.GhostTypes.Length;

data.SnapshotTargetSize = TryGetSingleton<NetworkStreamSnapshotTargetSize>(world, out var snapshotTargetSize)
? snapshotTargetSize.Value
: NetworkParameterConstants.MaxMessageSize;
}

void CollectMainClientData()
{
if (TryGetSingleton(world, out ClientTickRate clientTickRate))
{
data.ClientTickRate = new WrappedClientTickRate(clientTickRate);
}
data.MainClientData.NumOfSpawnedGhost = CountSpawnedGhosts(world.EntityManager);
if (TryGetSingleton(world, out ClientServerTickRate clientServerTickRate))
{
data.ClientServerTickRate = new WrappedClientServerTickRate(clientServerTickRate);
}

var spawnedGhostCount = CountSpawnedGhosts(world.EntityManager);
TryGetSingleton<GhostRelevancy>(world, out var ghostRelevancy);
using var predictedGhostQuery = world.EntityManager.CreateEntityQuery(ComponentType.ReadOnly<PredictedGhost>());
var predictionCount = predictedGhostQuery.CalculateEntityCountWithoutFiltering();
TryGetSingleton<PredictionSwitchingAnalyticsData>(world, out var predictionSwitchingAnalyticsData);
data.MainClientData = new MainClientData
{
RelevancyMode = (int)ghostRelevancy.GhostRelevancyMode,
NumOfSpawnedGhost = spawnedGhostCount,
NumOfPredictedGhosts = predictionCount,
NumSwitchToInterpolated = (int)predictionSwitchingAnalyticsData.NumTimesSwitchedToInterpolated,
NumSwitchToPredicted = (int)predictionSwitchingAnalyticsData.NumTimesSwitchedToPredicted,
};
}
}

if (amount == 0)
Expand All @@ -126,7 +133,7 @@ void CollectServerData()
}
else
{
data.AverageGhostInSnapshot = serializedSent / amount;
data.AverageGhostInSnapshot = (int)(serializedSent / amount);
}
data.NumMainClientWorlds = numMainClientWorlds;
data.NumServerWorlds = numServerWorlds;
Expand Down Expand Up @@ -211,6 +218,74 @@ public override string ToString()
}
}

/// <summary>
/// This struct is used to wrap the <see cref="ClientServerTickRate"/> struct to be serializable.
/// We ensure here that the data matches the expected format for the analytics.
/// If you change this struct, you must also update the analytics event in schemata.
/// </summary>
[Serializable]
internal struct WrappedClientServerTickRate
{
public int MaxSimulationStepBatchSize;
public int MaxSimulationStepsPerFrame;
public int NetworkTickRate;
public int SimulationTickRate;
public int TargetFrameRateMode;

public WrappedClientServerTickRate(ClientServerTickRate clientServerTickRate)
{
this.MaxSimulationStepBatchSize = clientServerTickRate.MaxSimulationStepBatchSize;
this.MaxSimulationStepsPerFrame = clientServerTickRate.MaxSimulationStepsPerFrame;
this.NetworkTickRate = clientServerTickRate.NetworkTickRate;
this.SimulationTickRate = clientServerTickRate.SimulationTickRate;
this.TargetFrameRateMode = (int)clientServerTickRate.TargetFrameRateMode;
}
}

/// <summary>
/// This struct is used to wrap the <see cref="ClientTickRate"/> struct to be serializable.
/// We ensure here that the data matches the expected format for the analytics.
/// If you change this struct, you must also update the analytics event in schemata.
/// </summary>
[Serializable]
internal struct WrappedClientTickRate
{
public int CommandAgeCorrectionFraction;
public int InterpolationDelayCorrectionFraction;
public int InterpolationDelayJitterScale;
public int InterpolationDelayMaxDeltaTicksFraction;
public int InterpolationTimeMS;
public int InterpolationTimeNetTicks;
public int InterpolationTimeScaleMax;
public int InterpolationTimeScaleMin;
public int MaxExtrapolationTimeSimTicks;
public int MaxPredictAheadTimeMS;
public int MaxPredictionStepBatchSizeFirstTimeTick;
public int MaxPredictionStepBatchSizeRepeatedTick;
public int PredictionTimeScaleMax;
public int PredictionTimeScaleMin;
public int TargetCommandSlack;

public WrappedClientTickRate(ClientTickRate clientTickRate)
{
this.CommandAgeCorrectionFraction = (int)clientTickRate.CommandAgeCorrectionFraction;
this.InterpolationDelayCorrectionFraction = (int)clientTickRate.InterpolationDelayCorrectionFraction;
this.InterpolationDelayJitterScale = (int)clientTickRate.InterpolationDelayJitterScale;
this.InterpolationDelayMaxDeltaTicksFraction = (int)clientTickRate.InterpolationDelayMaxDeltaTicksFraction;
this.InterpolationTimeMS = (int)clientTickRate.InterpolationTimeMS;
this.InterpolationTimeNetTicks = (int)clientTickRate.InterpolationTimeNetTicks;
this.InterpolationTimeScaleMax = (int)clientTickRate.InterpolationTimeScaleMax;
this.InterpolationTimeScaleMin = (int)clientTickRate.InterpolationTimeScaleMin;
this.MaxExtrapolationTimeSimTicks = (int)clientTickRate.MaxExtrapolationTimeSimTicks;
this.MaxPredictAheadTimeMS = (int)clientTickRate.MaxPredictAheadTimeMS;
this.MaxPredictionStepBatchSizeFirstTimeTick = clientTickRate.MaxPredictionStepBatchSizeFirstTimeTick;
this.MaxPredictionStepBatchSizeRepeatedTick = clientTickRate.MaxPredictionStepBatchSizeRepeatedTick;
this.PredictionTimeScaleMax = (int)clientTickRate.PredictionTimeScaleMax;
this.PredictionTimeScaleMin = (int)clientTickRate.PredictionTimeScaleMin;
this.TargetCommandSlack = (int)clientTickRate.TargetCommandSlack;
}
}

[Serializable]
#if UNITY_2023_2_OR_NEWER
struct GhostScaleAnalyticsData : IAnalytic.IData
Expand All @@ -219,13 +294,12 @@ struct GhostScaleAnalyticsData
#endif
{
public PlaymodeSettings Settings;
public int PlayerCount;
public int ServerSpawnedGhostCount;
public int GhostTypeCount;
public uint AverageGhostInSnapshot;
public int AverageGhostInSnapshot;
public GhostTypeData[] GhostTypes;
public ClientServerTickRate ClientServerTickRate;
public ClientTickRate ClientTickRate;
public WrappedClientServerTickRate ClientServerTickRate;
public WrappedClientTickRate ClientTickRate;
public MainClientData MainClientData;
public int SnapshotTargetSize;
public int NumMainClientWorlds;
Expand All @@ -235,7 +309,6 @@ public override string ToString()
{
var builder = new StringBuilder();
builder.Append($"{nameof(Settings)}: {Settings}, " +
$"{nameof(PlayerCount)}: {PlayerCount}, " +
$"{nameof(ServerSpawnedGhostCount)}: {ServerSpawnedGhostCount}, " +
$"{nameof(GhostTypeCount)}: {GhostTypeCount}, " +
$"{nameof(AverageGhostInSnapshot)}: {AverageGhostInSnapshot}, " +
Expand All @@ -259,10 +332,10 @@ public override string ToString()
[Serializable]
struct MainClientData
{
public GhostRelevancyMode RelevancyMode;
public int RelevancyMode;
public int NumOfPredictedGhosts;
public long NumSwitchToPredicted;
public long NumSwitchToInterpolated;
public int NumSwitchToPredicted;
public int NumSwitchToInterpolated;
public int NumOfSpawnedGhost;

public override string ToString()
Expand All @@ -282,7 +355,6 @@ struct PlaymodeSettings
public bool SimulatorEnabled;
public int Delay;
public int DropPercentage;
public int FuzzPercentage;
public int Jitter;
public string PlayModeType;
public string SimulatorPreset;
Expand All @@ -293,7 +365,6 @@ public override string ToString()
$"{nameof(SimulatorEnabled)}: {SimulatorEnabled}, " +
$"{nameof(Delay)}: {Delay}, " +
$"{nameof(DropPercentage)}: {DropPercentage}, " +
$"{nameof(FuzzPercentage)}: {FuzzPercentage}, " +
$"{nameof(Jitter)}: {Jitter}, " +
$"{nameof(PlayModeType)}: {PlayModeType}, " +
$"{nameof(SimulatorPreset)}: {SimulatorPreset}, ";
Expand All @@ -306,7 +377,7 @@ static class GhostComponentAnalytics
public const int k_MaxItems = 1000;
public const string k_VendorKey = "unity.netcode";
public const string k_Scale = "NetcodeGhostComponentScale";
public const int k_ScaleVersion = 2;
public const int k_ScaleVersion = 3;
public const int k_ConfigurationVersion = 1;
public const string k_Configuration = "NetcodeGhostComponentConfiguration";

Expand Down
3 changes: 2 additions & 1 deletion Editor/Templates/GhostComponentSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ static internal GhostComponentSerializer.State GetState(ref SystemState state)
var innerMarker = new Unity.Profiling.ProfilerMarker("__GHOST_NAME__");
innerMarker.Begin();
#endif
SetupFunctionPointers(ref s_State, ref state);
s_StateInitialized = SetupFunctionPointers(ref s_State, ref state);
//TODO: DOTS-7926 we should understand why we have in some cases a StackOverflowException.
//UNCOMMENT THIS LINE TO DEBUG IF A BIG COMPONENT MAY BE CAUSING A STACK OVERFLOW
// const int maxSizeToAvoidStackOverflow = 4_500;
Expand All @@ -356,6 +356,7 @@ static internal GhostComponentSerializer.State GetState(ref SystemState state)
}
return s_State;
}

private static bool s_StateInitialized;
private static GhostComponentSerializer.State s_State;

Expand Down
12 changes: 0 additions & 12 deletions Runtime/Analytics/NetCodeAnalyticsState.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
#if UNITY_EDITOR
using System.Linq;
using Unity.Entities;
using UnityEditor;
using UnityEngine.Assertions;

namespace Unity.NetCode.Analytics
{
internal static class NetCodeAnalyticsState
{
const string NetCodePlayerCount = "netcode_player_count";
public static void SetPlayerCount(int playerCount)
{
SessionState.SetInt(NetCodePlayerCount, playerCount);
}

public static int GetPlayerCount()
{
return SessionState.GetInt(NetCodePlayerCount, 0);
}

public static uint GetUpdateLength(World world)
{
if (!world.EntityManager.CanBeginExclusiveEntityTransaction())
Expand Down
2 changes: 1 addition & 1 deletion Runtime/Authoring/DefaultVariantSystemBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ protected sealed override void OnUpdate()
}

/// <summary>
/// Implement this method by adding to the <param name="defaultVariants"></param> mapping your
/// Implement this method by adding to the <paramref name="defaultVariants"/> mapping your
/// default type->variant <see cref="Rule"/>.
/// </summary>
/// <param name="defaultVariants"></param>
Expand Down
2 changes: 1 addition & 1 deletion Runtime/Authoring/DefaultVariantSystemGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ namespace Unity.NetCode
/// The system group OnCreate method finalizes the default mapping inside its own `OnCreate` method, by collecting from all the registered
/// <see cref="DefaultVariantSystemBase"/> systems the set of variant to use.
/// The order in which variants are set in the map is governed by the creation order (see <see cref="CreateAfterAttribute"/>, <see cref="CreateBeforeAttribute"/>).
/// </summary>
/// <remarks>
/// The group is present in both baking and client/server worlds.
/// </remarks>
/// </summary>
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation | WorldSystemFilterFlags.ClientSimulation |
WorldSystemFilterFlags.ThinClientSimulation | WorldSystemFilterFlags.BakingSystem)]
public partial class DefaultVariantSystemGroup : ComponentSystemGroup
Expand Down
2 changes: 1 addition & 1 deletion Runtime/Authoring/GhostComponentVariation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace Unity.NetCode
/// present in variant declaration.
/// The component variant can be assigned at authoring time using the GhostAuthoringComponent editor.</para>
/// <para>Note: This is incompatible with any type implementing <see cref="DontSupportPrefabOverridesAttribute"/>.</para>
/// </summary>
/// <remarks>
/// When declaring a variant, all fields that should be serialized must be declared. Any missing field or new field
/// not present in the original struct will not be serialized.
/// </remarks>
/// </summary>
[AttributeUsage(AttributeTargets.Struct)]
public class GhostComponentVariationAttribute : Attribute
{
Expand Down
2 changes: 1 addition & 1 deletion Runtime/Authoring/GhostFieldAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ public class GhostFieldAttribute : Attribute
public bool Composite { get; set; } = false;

/// <summary>
/// <inheritdoc cref="SmoothingAction"/>
/// Default is <see cref="SmoothingAction.Clamp"/>.
/// </summary>
/// <inheritdoc cref="SmoothingAction"/>
public SmoothingAction Smoothing { get; set; } = SmoothingAction.Clamp;

/// <summary>Allows you to specify a custom serializer for this GhostField using the <see cref="GhostFieldSubType"/> API.</summary>
Expand Down
4 changes: 2 additions & 2 deletions Runtime/Authoring/GhostModifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Unity.NetCode
{
/// <summary>
/// Assign to every <see cref="GhostInstance"/>, and denotes which Ghost prefab version this component is allowed to exist on.
/// <example>Use this to disable rendering components on the Server version of the Ghost.</example>
/// Use this to disable rendering components on the Server version of the Ghost.
/// If you cannot change the ComponentType, use the `GhostAuthoringInspectionComponent` to manually override on a specific Ghost prefab.
/// </summary>
[Flags]
Expand Down Expand Up @@ -71,7 +71,7 @@ public enum GhostSendType
/// <para><b>Meta-data of a <see cref="ICommandData"/> component, denoting whether or not the server should replicate the
/// input commands back down to clients.
/// Configure via <see cref="GhostComponentAttribute"/>.</b></para>
/// <para>Docs for ICommandData:<inheritdoc cref="ICommandData"/></para>
/// <para>See the documentation for ICommandData:<see cref="ICommandData"/></para>
/// </summary>
[Flags]
public enum SendToOwnerType
Expand Down
4 changes: 2 additions & 2 deletions Runtime/Authoring/Hybrid/BakerExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public static class BakerExtensions
/// <param name="self">an instance of the baker</param>
/// <param name="isPrefab">state is we are converting a prefab or not</param>
/// <typeparam name="T"></typeparam>
/// <remarks>In the editor, if a <see cref="NetCodeConversionSettings"/> is present in the build configuration used for conversion,
/// the target specified by the build component is used.
/// <remarks><para>In the editor, if a <see cref="NetCodeConversionSettings"/> is present in the build configuration used for conversion,
/// the target specified by the build component is used.</para>
/// <para>
/// Otherwise, the conversion target will be determined by the destination world for runtime conversion, and fallback to always be
/// <see cref="NetcodeConversionTarget.ClientAndServer"/> is nothing apply or for prefabs.
Expand Down
Loading

0 comments on commit 25d32f8

Please sign in to comment.