diff --git a/.buginfo b/.buginfo
deleted file mode 100644
index 6fa36b8..0000000
--- a/.buginfo
+++ /dev/null
@@ -1,4 +0,0 @@
-system: jira
-server: jira.unity3d.com
-project: ECSB
-issuetype: Bug
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3cbd769..c32959b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,23 @@
+---
+uid: changelog
+---
+
# Changelog
+## [1.1.0-pre.3] - 2023-10-17
+
+### Changed
+
+* the DefaultTranslationSmoothingAction.DefaultStaticUserParams is now public and can be used by user code (to either change the defaults or use them in their own custom smoothing methods).
+
+### Fixed
+
+* issue when using prediction error smoothing, causing wrong component data retrieved from the backup buffer and consequently not making the smoothing function work as expected.
+* an issue in the reported elapsed time when executing the PredictedFixedStepSystemGroup in presence of partial ticks and PredictedFixedStepSimulationTickRatio grater than 1, causing problem with physics and character controller interpolation.
+* An issue with the change mask not read correctly when serializing/deserializing components with more than 32 fields.
+* `InvalidOperationException: Comparison function is incorrect` inside `GhostComponentSerializerCollectionSystemGroup` due to `ComponentTypeSerializationStrategy.DefaultType` being a `byte` flag enum (so it erroneously matched `128 - 0` the same as `0 - 128` due to wrapping).
+
+
## [1.1.0-exp.1] - 2023-09-18
### Added
diff --git a/Editor/Templates/GhostComponentSerializer.cs b/Editor/Templates/GhostComponentSerializer.cs
index 5d6bf4a..8ab7a3c 100644
--- a/Editor/Templates/GhostComponentSerializer.cs
+++ b/Editor/Templates/GhostComponentSerializer.cs
@@ -548,11 +548,10 @@ private static void CalculateChangeMask(ref Snapshot snapshot, in Snapshot basel
#region __GHOST_CALCULATE_CHANGE_MASK__
#endregion
#region __GHOST_FLUSH_COMPONENT_CHANGE_MASK__
- GhostComponentSerializer.CopyToChangeMask(bits, changeMask, startOffset, 32);
- startOffset += 32;
+ GhostComponentSerializer.CopyToChangeMask(bits, changeMask, startOffset + __GHOST_CURRENT_MASK_BITS__, 32);
#endregion
#region __GHOST_FLUSH_FINAL_COMPONENT_CHANGE_MASK__
- GhostComponentSerializer.CopyToChangeMask(bits, changeMask, startOffset, __GHOST_CHANGE_MASK_BITS__);
+ GhostComponentSerializer.CopyToChangeMask(bits, changeMask, startOffset + __GHOST_CHANGE_MASK_BITS__, __GHOST_CURRENT_MASK_BITS__);
#endregion
#endif
}
diff --git a/Runtime/Command/IInputComponentData.cs b/Runtime/Command/IInputComponentData.cs
index b551874..9277467 100644
--- a/Runtime/Command/IInputComponentData.cs
+++ b/Runtime/Command/IInputComponentData.cs
@@ -1,6 +1,8 @@
using System;
+using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
+using Unity.NetCode.LowLevel.Unsafe;
namespace Unity.NetCode
{
@@ -50,12 +52,124 @@ public void Set()
/// Interface used to handle automatic input command data setup with the IInputComponentData
/// style inputs. This is used internally by code generation, don't use this directly.
///
- [Obsolete("The IInputBufferData interface has been deprecated. It was meant for internal use and any reference to it is considered an error. Please always use ICommandData instead", true)]
+ [Obsolete("The IInputBufferData interface has been deprecated. It was meant for internal use and any reference to it is considered an error. " +
+ "Please always use ICommandData instead.", false)]
public interface IInputBufferData : ICommandData
{
+ ///
+ /// Take the stored input data we have and copy to the given input data pointed to. Decrement
+ /// any event counters by the counter value in the previous command buffer data element.
+ ///
+ /// Command data from the previous tick
+ /// Our stored input data will be copied over to this location
+ public void DecrementEventsAndAssignToInput(IntPtr prevInputBufferDataPtr, IntPtr inputPtr);
+ ///
+ /// Save the input data with any event counters incremented by the counter from the last stored
+ /// input in the command buffer for the current tick. See .
+ ///
+ /// Pointer to the last command data in the buffer
+ /// Pointer to input data to be saved in this command data
+ public void IncrementEventsAndSetCurrentInputData(IntPtr lastInputBufferDataPtr, IntPtr inputPtr);
}
- ///
+ ///
+ /// For internal use only, helper struct that should be used to implement systems that copy the content of an
+ /// into the code-generated buffer.
+ ///
+ ///
+ ///
+ [Obsolete("CopyInputToCommandBuffer has been deprecated. There is no replacement, being the method meant to be used only by code-generated systems.", false)]
+ public partial struct CopyInputToCommandBuffer
+ where TInputBufferData : unmanaged, IInputBufferData
+ where TInputComponentData : unmanaged, IInputComponentData
+ {
+ ///
+ /// For internal use only, simplify the creation of system jobs that copies data to the underlying buffer.
+ ///
+ [Obsolete("CopyInputToBufferJob has been deprecated.", false)]
+ public struct CopyInputToBufferJob
+ {
+ ///
+ /// Implements the component copy and input event management.
+ /// Should be called your job method.
+ ///
+ ///
+ ///
+ public void Execute(ArchetypeChunk chunk, int orderIndex)
+ {
+ }
+ }
+
+ ///
+ /// Initialize the CopyInputToCommandBuffer by updating all the component type handles and create a
+ /// a new instance.
+ ///
+ ///
+ /// a new instance.
+ public CopyInputToBufferJob InitJobData(ref SystemState state)
+ {
+ return default;
+ }
+
+ ///
+ /// Creates the internal component type handles, register to system state the component queries.
+ /// Very important, add an implicity constraint for running the parent system only when the client
+ /// is connected to the server, by requiring at least one connection with a components.
+ ///
+ /// Should be called inside your the system OnCreate method.
+ ///
+ ///
+ ///
+ ///
+ public EntityQuery Create(ref SystemState state)
+ {
+ return default;
+ }
+ }
+
+ ///
+ /// For internal use only, helper struct that should be used to implements systems that copies
+ /// commands from the buffer to the component
+ /// present on the entity.
+ ///
+ ///
+ ///
+ [Obsolete("ApplyCurrentInputBufferElementToInputData has been deprecated. There is no replacement, being the method meant to be used only by code-generated systems.", false)]
+ public partial struct ApplyCurrentInputBufferElementToInputData
+ where TInputBufferData : unmanaged, IInputBufferData
+ where TInputComponentData : unmanaged, IInputComponentData
+ {
+ ///
+ /// Helper struct that should be used to implement jobs that copies commands from an buffer
+ /// to the respective .
+ ///
+ [Obsolete("ApplyInputDataFromBufferJob has been deprecated.", false)]
+ public struct ApplyInputDataFromBufferJob
+ {
+ ///
+ /// Copy the command for current server tick to the input component.
+ /// Should be called your job method.
+ ///
+ ///
+ ///
+ public void Execute(ArchetypeChunk chunk, int orderIndex)
+ {
+ }
+ }
+
+ ///
+ /// Update the component type handles and create a new
+ /// that can be passed to your job.
+ ///
+ ///
+ /// a new instance.
+ public ApplyInputDataFromBufferJob InitJobData(ref SystemState state)
+ {
+ return default;
+ }
+ }
+
+ ///
/// The underlying buffer used to store the .
///
///
diff --git a/Runtime/Debug/DebugGhostDrawer.cs b/Runtime/Debug/DebugGhostDrawer.cs
index cd99dd1..915652e 100644
--- a/Runtime/Debug/DebugGhostDrawer.cs
+++ b/Runtime/Debug/DebugGhostDrawer.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
+using Unity.Entities;
namespace Unity.NetCode
{
@@ -15,6 +16,12 @@ public class DebugGhostDrawer
static DebugGhostDrawer s_Instance;
public static List CustomDrawers = new List(2);
+
+ [Obsolete("Use ClientServerBootstrap.ServerWorld instead. RemoveAfter Entities 1.x")]
+ public static World FirstServerWorld => ClientServerBootstrap.ServerWorld;
+
+ [Obsolete("Use ClientServerBootstrap.ClientWorld instead. RemoveAfter Entities 1.x")]
+ public static World FirstClientWorld => ClientServerBootstrap.ClientWorld;
static ulong s_LastNextSequenceNumber;
@@ -29,6 +36,9 @@ public static void RegisterDrawAction(CustomDrawer newDrawAction)
CustomDrawers.Add(newDrawAction);
CustomDrawers.Sort();
}
+
+ [Obsolete("This functionality is obsolete, worlds are no longer cached here. RemoveAfter Entities 1.x")]
+ public static void RefreshWorldCaches() {}
public static bool HasRequiredWorlds => ClientServerBootstrap.ServerWorld != default && ClientServerBootstrap.ClientWorld != default;
diff --git a/Runtime/Simulator/MultiplayerPlayModePreferences.cs b/Runtime/Simulator/MultiplayerPlayModePreferences.cs
index 860ee2a..3d83e6c 100644
--- a/Runtime/Simulator/MultiplayerPlayModePreferences.cs
+++ b/Runtime/Simulator/MultiplayerPlayModePreferences.cs
@@ -273,10 +273,10 @@ public static void ApplySimulatorPresetToPrefs(SimulatorPreset preset)
/// For the PlayMode Tools Window.
public enum SimulatorView
{
- [Obsolete("RemovedAfter Entities 1.0")]
- Disabled = -1,
- PingView = 0,
- PerPacketView = 1,
+ [Obsolete("Disabled is no longer supported. Use MultiplayerPlayModePreferences.SimulatorEnabled instead. RemovedAfter Entities 1.x")]
+ Disabled = 0,
+ PingView = 1,
+ PerPacketView = 2,
}
}
#endif
diff --git a/Runtime/Snapshot/DefaultTranslationSmoothingAction.cs b/Runtime/Snapshot/DefaultTranslationSmoothingAction.cs
index de40b89..aa57579 100644
--- a/Runtime/Snapshot/DefaultTranslationSmoothingAction.cs
+++ b/Runtime/Snapshot/DefaultTranslationSmoothingAction.cs
@@ -50,8 +50,16 @@ public unsafe struct DefaultTranslationSmoothingAction
///
public sealed class DefaultStaticUserParams
{
- internal static readonly SharedStatic maxDist = SharedStatic.GetOrCreate();
- internal static readonly SharedStatic delta = SharedStatic.GetOrCreate();
+ ///
+ /// If the prediction error is larger than this value, the entity position is snapped to the new value.
+ /// The default threshold is 10 units.
+ ///
+ public static readonly SharedStatic maxDist = SharedStatic.GetOrCreate();
+ ///
+ /// If the prediction error is smaller than this value, the entity position is snapped to the new value.
+ /// The default threshold is 1 units.
+ ///
+ public static readonly SharedStatic delta = SharedStatic.GetOrCreate();
static DefaultStaticUserParams()
{
@@ -66,9 +74,7 @@ class DeltaKey {}
/// Return a the burst compatible function pointer that can be used to register the smoothing action to the
/// singleton.
///
- public static readonly PortableFunctionPointer
- Action =
- new PortableFunctionPointer(SmoothingAction);
+ public static readonly PortableFunctionPointer Action = new PortableFunctionPointer(SmoothingAction);
[BurstCompile(DisableDirectCall = true)]
[AOT.MonoPInvokeCallback(typeof(GhostPredictionSmoothing.SmoothingActionDelegate))]
diff --git a/Runtime/Snapshot/GhostComponentSerializerCollectionSystemGroup.cs b/Runtime/Snapshot/GhostComponentSerializerCollectionSystemGroup.cs
index 2a5c64b..8ec8c14 100644
--- a/Runtime/Snapshot/GhostComponentSerializerCollectionSystemGroup.cs
+++ b/Runtime/Snapshot/GhostComponentSerializerCollectionSystemGroup.cs
@@ -113,7 +113,7 @@ public int CompareTo(ComponentTypeSerializationStrategy other)
if (IsSerialized != other.IsSerialized)
return IsSerialized - other.IsSerialized;
if (DefaultRule != other.DefaultRule)
- return DefaultRule - other.DefaultRule;
+ return (int)DefaultRule - (int)other.DefaultRule;
if (Hash != other.Hash)
return Hash < other.Hash ? -1 : 1;
return 0;
@@ -356,6 +356,20 @@ public void ThrowIfCollectionNotFinalized(in FixedString512Bytes context)
#endif
}
+ ///
+ /// Lookup a component type to use as a buffer for a given IInputComponentData.
+ ///
+ ///
+ ///
+ /// True if the component has an assosiated buffer to use, false if it does not.
+ [Obsolete("TryGetBufferForInputComponent has been deprecated. In order to find the buffer associated with an IInputComponentData please just use" +
+ "IInputBuffer where T is the IInputComponentData type you are looking for.", false)]
+ public bool TryGetBufferForInputComponent(ComponentType inputType, out ComponentType bufferType)
+ {
+ bufferType = default;
+ return false;
+ }
+
///
/// Used by code-generated systems and meant for internal use only.
/// Adds a mapping from an IInputComponentData to the buffer it should use.
diff --git a/Runtime/Snapshot/GhostPredictionSmoothingSystem.cs b/Runtime/Snapshot/GhostPredictionSmoothingSystem.cs
index 0472858..8048994 100644
--- a/Runtime/Snapshot/GhostPredictionSmoothingSystem.cs
+++ b/Runtime/Snapshot/GhostPredictionSmoothingSystem.cs
@@ -34,15 +34,15 @@ internal GhostPredictionSmoothing(NativeParallelHashMap action;
}
@@ -78,7 +78,7 @@ public bool RegisterSmoothingAction(EntityManager entityManager, PortableFunc
compSize = -1,
serializerIndex = -1,
entityIndex = -1,
- compBackupOffset = -1,
+ backupData = null,
userTypeId = -1,
userTypeSize = -1
};
@@ -306,27 +306,20 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo
return; // serialization data has not been loaded yet. This can only happen for prespawn objects
var typeData = GhostTypeCollection[ghostTypeId];
-
- var headerSize = PredictionBackupState.GetHeaderSize();
- var entitySize = PredictionBackupState.GetEntitiesSize(chunk.Capacity, out var singleEntitySize);
-
Entity* backupEntities = PredictionBackupState.GetEntities(state);
var entities = chunk.GetNativeArray(entityType);
var PredictedGhosts = chunk.GetNativeArray(ref predictedGhostType);
int numBaseComponents = typeData.NumComponents - typeData.NumChildComponents;
- int baseOffset = typeData.FirstComponent;
-
var actions = new NativeList(Allocator.Temp);
var childActions = new NativeList(Allocator.Temp);
- int backupOffset = headerSize + entitySize;
-
+ byte* dataPtr = PredictionBackupState.GetData(state);
// todo: this loop could be cached on chunk.capacity, because now we are re-calculating it everytime.
for (int comp = 0; comp < typeData.NumComponents; ++comp)
{
- int index = baseOffset + comp;
+ int index = typeData.FirstComponent + comp;
int compIdx = GhostComponentIndex[index].ComponentIndex;
int serializerIdx = GhostComponentIndex[index].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
@@ -336,28 +329,27 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo
if ((GhostComponentIndex[index].SendMask&requiredSendMask) == 0)
continue;
+ //Buffer does not have any smoothing
if (GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
{
- backupOffset += PredictionBackupState.GetDataSize(GhostSystemConstants.DynamicBufferComponentSnapshotSize, chunk.Capacity);
+ dataPtr = PredictionBackupState.GetNextData(dataPtr, GhostSystemConstants.DynamicBufferComponentSnapshotSize, chunk.Capacity);
continue;
}
var compSize = GhostComponentCollection[serializerIdx].ComponentSize;
-
- if (smoothingActions.TryGetValue(GhostComponentCollection[serializerIdx].ComponentType,
- out var action))
+ if (smoothingActions.TryGetValue(GhostComponentCollection[serializerIdx].ComponentType, out var action))
{
action.compIndex = compIdx;
action.compSize = compSize;
action.serializerIndex = serializerIdx;
action.entityIndex = GhostComponentIndex[index].EntityIndex;
- action.compBackupOffset = backupOffset;
+ action.backupData = dataPtr;
if (comp < numBaseComponents)
actions.Add(action);
else
childActions.Add(action);
}
- backupOffset += PredictionBackupState.GetDataSize(compSize, chunk.Capacity);
+ dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
}
foreach (var action in actions)
@@ -382,8 +374,7 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo
usrDataPtr = usrData + action.userTypeSize * ent;
}
- byte* dataPtr = ((byte*) state) + action.compBackupOffset;
- action.action.Ptr.Invoke((IntPtr)(compData + action.compSize * ent), (IntPtr)(dataPtr + action.compSize * ent),
+ action.action.Ptr.Invoke((IntPtr)(compData + action.compSize * ent), (IntPtr)(action.backupData + action.compSize * ent),
(IntPtr)usrDataPtr);
}
}
@@ -412,9 +403,7 @@ public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bo
var usrData = (byte*)chunk.GetDynamicComponentDataArrayReinterpret(ref userTypes[action.userTypeId], action.userTypeSize).GetUnsafeReadOnlyPtr();
usrDataPtr = usrData + action.userTypeSize * ent;
}
-
- byte* dataPtr = ((byte*) state) + action.compBackupOffset;
- action.action.Ptr.Invoke((IntPtr)(compData + action.compSize * childChunk.IndexInChunk), (IntPtr)(dataPtr + action.compSize * ent), (IntPtr)usrDataPtr);
+ action.action.Ptr.Invoke((IntPtr)(compData + action.compSize * childChunk.IndexInChunk), (IntPtr)(action.backupData + action.compSize * ent), (IntPtr)usrDataPtr);
}
}
}
diff --git a/Runtime/Snapshot/GhostPredictionSystemGroup.cs b/Runtime/Snapshot/GhostPredictionSystemGroup.cs
index 0c36d36..fbd6e8b 100644
--- a/Runtime/Snapshot/GhostPredictionSystemGroup.cs
+++ b/Runtime/Snapshot/GhostPredictionSystemGroup.cs
@@ -424,6 +424,8 @@ public float Timestep
int m_RemainingUpdates;
float m_TimeStep;
+ double m_ElapsedTime;
+ private EntityQuery networkTimeQuery;
//used to track invalid usage of the TimeStep setter.
#if UNITY_EDITOR || NETCODE_DEBUG
float m_DeprecatedTimeStep;
@@ -441,6 +443,11 @@ public NetcodePredictionFixedRateManager(float defaultTimeStep)
SetTimeStep(defaultTimeStep);
}
+ public void OnCreate(ComponentSystemGroup group)
+ {
+ networkTimeQuery = group.EntityManager.CreateEntityQuery(typeof(NetworkTime));
+ }
+
public void SetTimeStep(float timeStep)
{
m_TimeStep = timeStep;
@@ -462,11 +469,23 @@ public bool ShouldGroupUpdate(ComponentSystemGroup group)
{
// Add epsilon to account for floating point inaccuracy
m_RemainingUpdates = (int)((group.World.Time.DeltaTime + 0.001f) / m_TimeStep);
+ if (m_RemainingUpdates > 0)
+ {
+ var networkTime = networkTimeQuery.GetSingleton();
+ m_ElapsedTime = group.World.Time.ElapsedTime;
+ if (networkTime.IsPartialTick)
+ {
+ //dt = m_FixedTimeStep * networkTime.ServerTickFraction;
+ //elapsed since last full tick = m_ElapsedTime - dt;
+ m_ElapsedTime -= group.World.Time.DeltaTime;
+ m_ElapsedTime += m_RemainingUpdates * m_TimeStep;
+ }
+ }
}
if (m_RemainingUpdates == 0)
return false;
group.World.PushTime(new TimeData(
- elapsedTime: group.World.Time.ElapsedTime - (m_RemainingUpdates-1)*m_TimeStep,
+ elapsedTime: m_ElapsedTime - (m_RemainingUpdates-1)*m_TimeStep,
deltaTime: m_TimeStep));
m_OldGroupAllocators = group.World.CurrentGroupAllocators;
group.World.SetGroupAllocator(group.RateGroupAllocators);
@@ -574,6 +593,7 @@ public PredictedFixedStepSimulationSystemGroup()
protected override void OnCreate()
{
base.OnCreate();
+ ((NetcodePredictionFixedRateManager)RateManager).OnCreate(this);
m_BeginFixedStepSimulationEntityCommandBufferSystem = World.GetExistingSystemManaged();
m_EndFixedStepSimulationEntityCommandBufferSystem = World.GetExistingSystemManaged();
}
@@ -584,4 +604,19 @@ protected override void OnUpdate()
m_EndFixedStepSimulationEntityCommandBufferSystem.Update();
}
}
+
+ ///
+ /// Temporary type for upgradability, to be removed before 1.0
+ ///
+ [Obsolete("'GhostPredictionSystemGroup' has been renamed to 'PredictedSimulationSystemGroup'. (UnityUpgradable) -> PredictedSimulationSystemGroup")]
+ [DisableAutoCreation]
+ public partial class GhostPredictionSystemGroup : ComponentSystemGroup
+ {}
+ ///
+ /// Temporary type for upgradability, to be removed before 1.0
+ ///
+ [Obsolete("'FixedStepGhostPredictionSystemGroup' has been renamed to 'PredictedFixedStepSimulationSystemGroup'. (UnityUpgradable) -> PredictedFixedStepSimulationSystemGroup")]
+ [DisableAutoCreation]
+ public partial class FixedStepGhostPredictionSystemGroup : ComponentSystemGroup
+ {}
}
diff --git a/Runtime/SourceGenerators/NetCodeSourceGenerator.dll b/Runtime/SourceGenerators/NetCodeSourceGenerator.dll
index e4b72bf..c5b88a6 100644
Binary files a/Runtime/SourceGenerators/NetCodeSourceGenerator.dll and b/Runtime/SourceGenerators/NetCodeSourceGenerator.dll differ
diff --git a/Runtime/SourceGenerators/NetCodeSourceGenerator.pdb b/Runtime/SourceGenerators/NetCodeSourceGenerator.pdb
index e7ef0f0..cc2ecb4 100644
Binary files a/Runtime/SourceGenerators/NetCodeSourceGenerator.pdb and b/Runtime/SourceGenerators/NetCodeSourceGenerator.pdb differ
diff --git a/Runtime/SourceGenerators/Source~/Tests/SourceGeneratorTests.cs b/Runtime/SourceGenerators/Source~/Tests/SourceGeneratorTests.cs
index 7acdbe1..9bd21a4 100644
--- a/Runtime/SourceGenerators/Source~/Tests/SourceGeneratorTests.cs
+++ b/Runtime/SourceGenerators/Source~/Tests/SourceGeneratorTests.cs
@@ -1619,5 +1619,135 @@ public int myProperty {
foreach (var serializerMethod in commandDeserializerSyntax)
Assert.AreEqual(3, serializerMethod.GetText().Lines.Where((line => line.ToString().Contains("data."))).Count());
}
+
+ [Test]
+ public void SourceGenerator_ChangeMaskIncreaseCorrectly()
+ {
+ var testData = @"
+ using Unity.Entities;
+ using Unity.NetCode;
+ namespace Unity.NetCode
+ {
+ public struct TestLargeNumberOfFields : IComponentData
+ {
+[GhostField] public int Value1;
+[GhostField] public int Value2;
+[GhostField] public int Value3;
+[GhostField] public int Value4;
+[GhostField] public int Value5;
+[GhostField] public int Value6;
+[GhostField] public int Value7;
+[GhostField] public int Value8;
+[GhostField] public int Value9;
+[GhostField] public int Value11;
+[GhostField] public int Value12;
+[GhostField] public int Value13;
+[GhostField] public int Value14;
+[GhostField] public int Value15;
+[GhostField] public int Value16;
+[GhostField] public int Value17;
+[GhostField] public int Value18;
+[GhostField] public int Value19;
+[GhostField] public int Value21;
+[GhostField] public int Value22;
+[GhostField] public int Value23;
+[GhostField] public int Value24;
+[GhostField] public int Value25;
+[GhostField] public int Value26;
+[GhostField] public int Value27;
+[GhostField] public int Value28;
+[GhostField] public int Value29;
+[GhostField] public int Value31;
+[GhostField] public int Value32;
+[GhostField] public int Value33;
+[GhostField] public int Value34;
+[GhostField] public int Value35;
+[GhostField] public int Value36;
+[GhostField] public int Value37;
+[GhostField] public int Value38;
+[GhostField] public int Value39;
+[GhostField] public int Value41;
+[GhostField] public int Value42;
+[GhostField] public int Value43;
+[GhostField] public int Value44;
+[GhostField] public int Value45;
+[GhostField] public int Value46;
+[GhostField] public int Value47;
+[GhostField] public int Value48;
+[GhostField] public int Value49;
+[GhostField] public int Value51;
+[GhostField] public int Value52;
+[GhostField] public int Value53;
+[GhostField] public int Value54;
+[GhostField] public int Value55;
+[GhostField] public int Value56;
+[GhostField] public int Value57;
+[GhostField] public int Value58;
+[GhostField] public int Value59;
+[GhostField] public int Value51;
+[GhostField] public int Value52;
+[GhostField] public int Value53;
+[GhostField] public int Value54;
+[GhostField] public int Value55;
+[GhostField] public int Value56;
+[GhostField] public int Value57;
+[GhostField] public int Value58;
+[GhostField] public int Value59;
+[GhostField] public int Value61;
+[GhostField] public int Value62;
+[GhostField] public int Value63;
+[GhostField] public int Value64;
+[GhostField] public int Value65;
+[GhostField] public int Value66;
+[GhostField] public int Value67;
+[GhostField] public int Value68;
+[GhostField] public int Value69;
+[GhostField] public int Value71;
+[GhostField] public int Value72;
+[GhostField] public int Value73;
+[GhostField] public int Value74;
+[GhostField] public int Value75;
+[GhostField] public int Value76;
+[GhostField] public int Value77;
+[GhostField] public int Value78;
+[GhostField] public int Value79;
+[GhostField] public int Value81;
+[GhostField] public int Value82;
+[GhostField] public int Value83;
+[GhostField] public int Value84;
+[GhostField] public int Value85;
+[GhostField] public int Value86;
+[GhostField] public int Value87;
+[GhostField] public int Value88;
+[GhostField] public int Value89;
+[GhostField] public int Value91;
+[GhostField] public int Value92;
+[GhostField] public int Value93;
+[GhostField] public int Value94;
+[GhostField] public int Value95;
+[GhostField] public int Value96;
+[GhostField] public int Value97;
+[GhostField] public int Value98;
+[GhostField] public int Value99;
+ }
+ }";
+
+ var tree = CSharpSyntaxTree.ParseText(testData);
+ var results = GeneratorTestHelpers.RunGenerators(tree);
+ var diagnostics = results.Diagnostics;
+ Assert.AreEqual(0, diagnostics.Count(d => d.Severity == DiagnosticSeverity.Error));
+
+ //There should be three increments for the change mask i
+ var text = results.GeneratedSources[0].SourceText.ToString();
+ Assert.IsTrue(text.Contains("CopyToChangeMask(bits, changeMask, startOffset + 0, 32)"));
+ Assert.IsTrue(text.Contains("CopyToChangeMask(bits, changeMask, startOffset + 32, 32)"));
+ Assert.IsTrue(text.Contains("CopyToChangeMask(bits, changeMask, startOffset + 64, 32)"));
+ Assert.IsTrue(text.Contains("CopyToChangeMask(bits, changeMask, startOffset + 96, 3)"));
+
+ Assert.IsTrue(text.Contains("CopyFromChangeMask(changeMaskData, startOffset, ChangeMaskBits)"));
+ Assert.IsTrue(text.Contains("CopyFromChangeMask(changeMaskData, startOffset + 32, ChangeMaskBits - 32)"));
+ Assert.IsTrue(text.Contains("CopyFromChangeMask(changeMaskData, startOffset + 64, ChangeMaskBits - 64)"));
+ Assert.IsTrue(text.Contains("CopyFromChangeMask(changeMaskData, startOffset + 96, ChangeMaskBits - 96)"));
+ }
}
}
diff --git a/Tests/Editor/PredictionTests.cs b/Tests/Editor/PredictionTests.cs
index 9edc7bc..01f1104 100644
--- a/Tests/Editor/PredictionTests.cs
+++ b/Tests/Editor/PredictionTests.cs
@@ -147,6 +147,25 @@ protected override void OnUpdate()
}
}
+ [DisableAutoCreation]
+ [UpdateInGroup(typeof(PredictedFixedStepSimulationSystemGroup))]
+ partial struct CheckElapsedTime : ISystem
+ {
+ private double ElapsedTime;
+ public void OnUpdate(ref SystemState state)
+ {
+ var timestep = state.World.GetExistingSystemManaged().Timestep;
+ var time = SystemAPI.Time;
+ if (ElapsedTime == 0.0)
+ {
+ ElapsedTime = time.ElapsedTime;
+ }
+ var totalElapsed = math.fmod(time.ElapsedTime - ElapsedTime, timestep);
+ //the elapsed time must be always an integral multiple of the time step
+ Assert.LessOrEqual(totalElapsed, 1e-6);
+ }
+ }
+
public class PredictionTests
{
[TestCase((uint)0x229321)]
@@ -354,5 +373,44 @@ public void NetcodeClientPredictionRateManager_WillWarnWhenMismatchSimulationTic
}
}
}
+
+ [TestCase(1)]
+ [TestCase(2)]
+ [TestCase(3)]
+ public void PredictedFixedStepSimulation_ElapsedTimeReportedCorrectly(int ratio)
+ {
+ using (var testWorld = new NetCodeTestWorld())
+ {
+ testWorld.Bootstrap(true, typeof(CheckElapsedTime));
+ testWorld.CreateWorlds(true, 1);
+ var tickRate = testWorld.ServerWorld.EntityManager.CreateEntity(typeof(ClientServerTickRate));
+ testWorld.ServerWorld.EntityManager.SetComponentData(tickRate, new ClientServerTickRate
+ {
+ PredictedFixedStepSimulationTickRatio = ratio
+ });
+ const float frameTime = 1f / 60f;
+ testWorld.Connect(frameTime);
+ //Check that the simulation tick rate are the same
+ var clientRate = testWorld.GetSingleton(testWorld.ClientWorlds[0]);
+ Assert.AreEqual(60, clientRate.SimulationTickRate);
+ Assert.AreEqual(ratio, clientRate.PredictedFixedStepSimulationTickRatio);
+ for (int i = 0; i < 16; ++i)
+ {
+ testWorld.Tick(1f / 60f);
+ }
+ for (int i = 0; i < 16; ++i)
+ {
+ testWorld.Tick(1f / 30f);
+ }
+ for (int i = 0; i < 16; ++i)
+ {
+ testWorld.Tick(1f / 45f);
+ }
+ for (int i = 0; i < 16; ++i)
+ {
+ testWorld.Tick(1f / 117f);
+ }
+ }
+ }
}
}
diff --git a/ValidationExceptions.json b/ValidationExceptions.json
index 735224e..c02f2c6 100644
--- a/ValidationExceptions.json
+++ b/ValidationExceptions.json
@@ -2,8 +2,8 @@
"ErrorExceptions": [
{
"ValidationTest": "API Validation",
- "ExceptionMessage": "For Experimental or Preview Packages, breaking changes require a new minor version.",
- "PackageVersion": "1.1.0-exp.1"
+ "ExceptionMessage": "Breaking changes require a new major version.",
+ "PackageVersion": "1.1.0-pre.3"
}
],
"WarningExceptions": []
diff --git a/package.json b/package.json
index f837b0b..d70a2d8 100644
--- a/package.json
+++ b/package.json
@@ -1,26 +1,26 @@
{
"name": "com.unity.netcode",
"displayName": "Netcode for Entities",
- "version": "1.1.0-exp.1",
+ "version": "1.1.0-pre.3",
"unity": "2022.3",
"unityRelease": "0f1",
"description": "Unity's Data Oriented Technology Stack (DOTS) multiplayer netcode layer - a high level netcode system built on entities. This package provides a foundation for creating networked multiplayer applications within DOTS.",
"dependencies": {
"com.unity.transport": "2.0.1",
- "com.unity.entities": "1.1.0-exp.1",
- "com.unity.logging": "1.1.0-exp.1",
+ "com.unity.entities": "1.1.0-pre.3",
+ "com.unity.logging": "1.1.0-pre.3",
"com.unity.modules.animation": "1.0.0"
},
"_upm": {
- "changelog": "### Added\n\n* 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).\n* new public template specs and generator documentation\n* Added convenience methods for getting the clientworld / serverworld (or thin client list) added to ClientServerBootstrap\n* Additional analytics events. Multiplayer tools fields, prediction switching counters, tick rate configuration.\n* New method on the `PredictedFixedStepSimulationSystemGroup` class to initialise the rate as a multiple of a base tick rate.\n* `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.\n* 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.\n* 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.\n* GhostSpawnClassificationSystemGroup, that is aimed to contains all your classification systems in one place.\n* Error messages to some missing `NetworkDriver.Begin/EndSend` locations.\n* defining `ENABLE_UNITY_RPC_REGISTRATION_LOGGING` will now log information about registered RPCs during netcode startup\n* 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.\n* We introduced the new InputBufferData buffer, that is used as underlying container for all IInputComponentData.\n* 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.\n* new method for creating a NetworkDriver using WebSocketNetworkInterface.\n* 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).\n\n### Changed\n\n* 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.\n* Increased the ThinClient cap on `MultiplayerPlayModePreferences.MaxNumThinClients` from 32 to 1k, to facilitate some amount of in-editor testing of high-player-counts.\n* NetcodeTestWorld updates the worlds in the same way the package does: first server, then all clients worlds.\n* When Dedicated Server package is installed, the PlayMode Type value is overridden by the active Multiplayer Role.\n\n### Deprecated\n\n* The public `PredictedFixedStepSimulationGroup.TimeStep`. You should always use the `PredictedFixedStepSimulationGroup.ConfigureTimeStep` to setup the rate of the `PredictedFixedStepSimulationSystemGroup.`.\n* the IInputBufferData interface (internal for code-gen use but public) has been deprecated and will be removed in the 1.2 release.\n\n### Fixed\n\n* incorrect code generated serialization and calculated ChangeMask bits for component and buffers when the GhostFieldAttribute.Composite flag is set to true (in some cases).\n* wrong check for typename in GhostComponentVariation\n* missing region in unquantized float template, causing errors when used for interpolated field.\n* improper check when the ClientServerSetting asset is saved, causing worker process not seeing the changes in the settings.\n* The server world was not setting the correct rate to the group, if that was not done as part of the bootstrap.\n* Exception thrown when the NetDbg tools is connecting to either the editor or player.\n* 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).\n* 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.\n* Packet dump logging exception when using relevancy, despawns, and packet dumps enabled. Also fixed performance overhead (as it was erroneously logging stack traces).\n* An issue with variant hash calculation in release build, causing ghost prefab hash being different in between development/editor and release build.\n* 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.\n* Issue in TryGetHashElseZero, that was using the ComponentType.GetDebugName to calculate the variant hash, leading incorrect results in a release player build\n* A `NetworkDriver.BeginSend` error causing an infinite loop in the `RpcSystem`.\n* Deprecated Analytics API when using 2023.2 or newer.\n* compilation issue when using 2023.2, caused by an ambiguous symbol (define in both Editor and in Entities.Editor assembly)\n* Errant netcode systems no longer show up in the DefaultWorld: `PhysicsWorldHistory`, `SwitchPredictionSmoothingPhysicsOrderingSystem`, `SwitchPredictionSmoothingSystem`, `GhostPresentationGameObjectTransformSystem`, `GhostPresentationGameObjectSystem`, and `SetLocalPlayerGraphicsColorsSystem`.\n* Previous was hard to retrieve the generated buffer for a given IInputComponentData. Now is easy as doing something like InputBufferData.\n* Compilation error when building for WebGL\n* SnapshotDataLookupCache not created in the correct order, causing custom classification system using the SnapshotBufferHelper to throw exceptions, because the cache was not initialised.\n* A replicated `[GhostEnabledBit]` flag component would throw an `ArgumentException` when added to a Prespawned Ghost due to `ArchetypeChunk.GetDynamicComponentDataArrayReinterpret`."
+ "changelog": "### Changed\n\n* the DefaultTranslationSmoothingAction.DefaultStaticUserParams is now public and can be used by user code (to either change the defaults or use them in their own custom smoothing methods).\n\n### Fixed\n\n* issue when using prediction error smoothing, causing wrong component data retrieved from the backup buffer and consequently not making the smoothing function work as expected.\n* an issue in the reported elapsed time when executing the PredictedFixedStepSystemGroup in presence of partial ticks and PredictedFixedStepSimulationTickRatio grater than 1, causing problem with physics and character controller interpolation.\n* An issue with the change mask not read correctly when serializing/deserializing components with more than 32 fields.\n* `InvalidOperationException: Comparison function is incorrect` inside `GhostComponentSerializerCollectionSystemGroup` due to `ComponentTypeSerializationStrategy.DefaultType` being a `byte` flag enum (so it erroneously matched `128 - 0` the same as `0 - 128` due to wrapping)."
},
"upmCi": {
- "footprint": "142b6d5f84e0d29136fe7e018a8ed953c8e74762"
+ "footprint": "87dd944372c7e5179109b8bfb0401b6c09dc3a9e"
},
"documentationUrl": "https://docs.unity3d.com/Packages/com.unity.netcode@1.1/manual/index.html",
"repository": {
"url": "https://github.cds.internal.unity3d.com/unity/dots.git",
"type": "git",
- "revision": "c3274ed2015eeec85914d62bfbd32e39f0dd112b"
+ "revision": "5edeb315aa3ca0d26060290a69660b2afdabe2ff"
}
}