breaking: remove syncInterval

This commit is contained in:
vis2k 2022-11-13 18:09:47 +01:00
parent 9b39e31e8a
commit 9ba726011f
16 changed files with 19 additions and 177 deletions

View File

@ -64,7 +64,7 @@ void SendToServer()
double now = NetworkTime.localTime; // Unity 2019 doesn't have Time.timeAsDouble yet double now = NetworkTime.localTime; // Unity 2019 doesn't have Time.timeAsDouble yet
if (now > nextSyncTime) if (now > nextSyncTime)
{ {
nextSyncTime = now + syncInterval; nextSyncTime = now + NetworkClient.sendInterval;
CmdSendState(target.velocity, target.position); CmdSendState(target.velocity, target.position);
} }
} }

View File

@ -232,7 +232,7 @@ void SendVelocity()
// only update syncTime if either has changed // only update syncTime if either has changed
if (angularVelocityChanged || velocityChanged) if (angularVelocityChanged || velocityChanged)
{ {
previousValue.nextSyncTime = now + syncInterval; previousValue.nextSyncTime = now + NetworkClient.sendInterval;
} }
} }

View File

@ -231,7 +231,7 @@ void SendVelocity()
// only update syncTime if either has changed // only update syncTime if either has changed
if (angularVelocityChanged || velocityChanged) if (angularVelocityChanged || velocityChanged)
{ {
previousValue.nextSyncTime = now + syncInterval; previousValue.nextSyncTime = now + NetworkClient.sendInterval;
} }
} }

View File

@ -189,9 +189,9 @@ bool CheckAnimStateChanged(out int stateHash, out float normalizedTime, int laye
void CheckSendRate() void CheckSendRate()
{ {
double now = NetworkTime.localTime; double now = NetworkTime.localTime;
if (SendMessagesAllowed && syncInterval >= 0 && now > nextSendTime) if (SendMessagesAllowed && NetworkServer.sendInterval >= 0 && now > nextSendTime)
{ {
nextSendTime = now + syncInterval; nextSendTime = now + NetworkServer.sendInterval;
using (NetworkWriterPooled writer = NetworkWriterPool.Get()) using (NetworkWriterPooled writer = NetworkWriterPool.Get())
{ {

View File

@ -94,13 +94,6 @@ protected virtual void OnValidate()
{ {
// set target to self if none yet // set target to self if none yet
if (target == null) target = transform; if (target == null) target = transform;
// time snapshot interpolation happens globally.
// value (transform) happens in here.
// both always need to be on the same send interval.
// force the setting to '0' in OnValidate to make it obvious that we
// actually use NetworkServer.sendInterval.
syncInterval = 0;
} }
// snapshot functions ////////////////////////////////////////////////// // snapshot functions //////////////////////////////////////////////////

View File

@ -94,13 +94,6 @@ protected virtual void OnValidate()
// set target to self if none yet // set target to self if none yet
if (target == null) target = transform; if (target == null) target = transform;
// time snapshot interpolation happens globally.
// value (transform) happens in here.
// both always need to be on the same send interval.
// force the setting to '0' in OnValidate to make it obvious that we
// actually use NetworkServer.sendInterval.
syncInterval = 0;
// obsolete clientAuthority compatibility: // obsolete clientAuthority compatibility:
// if it was used, then set the new SyncDirection automatically. // if it was used, then set the new SyncDirection automatically.
// if it wasn't used, then don't touch syncDirection. // if it wasn't used, then don't touch syncDirection.

View File

@ -36,10 +36,10 @@ public abstract class NetworkBehaviour : MonoBehaviour
/// <summary>sync interval for OnSerialize (in seconds)</summary> /// <summary>sync interval for OnSerialize (in seconds)</summary>
// hidden because NetworkBehaviourInspector shows it only if has OnSerialize. // hidden because NetworkBehaviourInspector shows it only if has OnSerialize.
// [0,2] should be enough. anything >2s is too laggy anyway. // [0,2] should be enough. anything >2s is too laggy anyway.
[Tooltip("Time in seconds until next change is synchronized to the client. '0' means send immediately if changed. '0.5' means only send changes every 500ms.\n(This is for state synchronization like SyncVars, SyncLists, OnSerialize. Not for Cmds, Rpcs, etc.)")] // DEPRECATED 2022-11-11
[Obsolete("NetworkBehaviour.syncInterval is not used anymore. Please configure the NetworkManager's send/tickRate globally instead. This has two advantages: snapshot interpolation is easier (no overlapping intervals), and it allows for a noticeable performance improvements because OnSerialize doesn't need to check dirty components anymore.")]
[Range(0, 2)] [Range(0, 2)]
[HideInInspector] public float syncInterval = 0.1f; [HideInInspector] public float syncInterval = 0.1f;
internal double lastSyncTime;
/// <summary>True if this object is on the server and has been spawned.</summary> /// <summary>True if this object is on the server and has been spawned.</summary>
// This is different from NetworkServer.active, which is true if the // This is different from NetworkServer.active, which is true if the
@ -172,21 +172,16 @@ public void SetSyncVarDirtyBit(ulong dirtyBit)
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetDirty() => SetSyncVarDirtyBit(ulong.MaxValue); public void SetDirty() => SetSyncVarDirtyBit(ulong.MaxValue);
// true if syncInterval elapsed and any SyncVar or SyncObject is dirty // true if any SyncVar or SyncObject is dirty.
// OR both bitmasks. != 0 if either was dirty. // OR both bitmasks. != 0 if either was dirty.
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsDirty() => public bool IsDirty() => (syncVarDirtyBits | syncObjectDirtyBits) != 0UL;
// check bits first. this is basically free.
(syncVarDirtyBits | syncObjectDirtyBits) != 0UL &&
// only check time if bits were dirty. this is more expensive.
NetworkTime.localTime - lastSyncTime >= syncInterval;
/// <summary>Clears all the dirty bits that were set by SetDirtyBits()</summary> /// <summary>Clears all the dirty bits that were set by SetDirtyBits()</summary>
// automatically invoked when an update is sent for this object, but can // automatically invoked when an update is sent for this object, but can
// be called manually as well. // be called manually as well.
public void ClearAllDirtyBits() public void ClearAllDirtyBits()
{ {
lastSyncTime = NetworkTime.localTime;
syncVarDirtyBits = 0L; syncVarDirtyBits = 0L;
syncObjectDirtyBits = 0L; syncObjectDirtyBits = 0L;

View File

@ -1467,7 +1467,7 @@ static void Broadcast()
// nothing to do in host mode. server already knows the state. // nothing to do in host mode. server already knows the state.
if (NetworkServer.active) return; if (NetworkServer.active) return;
// send time snapshot every sendInterval. // send time snapshot every sendInterval
BroadcastTimeSnapshot(); BroadcastTimeSnapshot();
// for each entity that the client owns // for each entity that the client owns
@ -1495,7 +1495,7 @@ static void Broadcast()
Send(message); Send(message);
// reset dirty bits so it's not resent next time. // reset dirty bits so it's not resent next time.
identity.ClearDirtyComponentsDirtyBits(); identity.ClearAllComponentsDirtyBits();
} }
} }
} }

View File

@ -1114,16 +1114,10 @@ internal NetworkIdentitySerialization GetServerSerializationAtTick(int tick)
// NOTE: not in Serializell as that should only do one // NOTE: not in Serializell as that should only do one
// thing: serialize data. // thing: serialize data.
// //
//
// NOTE: DO NOT clear ALL component's dirty bits, because
// components can have different syncIntervals and we
// don't want to reset dirty bits for the ones that were
// not synced yet.
//
// NOTE: this used to be very important to avoid ever growing // NOTE: this used to be very important to avoid ever growing
// SyncList changes if they had no observers, but we've // SyncList changes if they had no observers, but we've
// added SyncObject.isRecording since. // added SyncObject.isRecording since.
ClearDirtyComponentsDirtyBits(); ClearAllComponentsDirtyBits();
// set tick // set tick
lastSerialization.tick = tick; lastSerialization.tick = tick;
@ -1337,23 +1331,6 @@ internal void ClearAllComponentsDirtyBits()
} }
} }
// Clear only dirty component's dirty bits. ignores components which
// may be dirty but not ready to be synced yet (because of syncInterval)
//
// NOTE: this used to be very important to avoid ever
// growing SyncList changes if they had no observers,
// but we've added SyncObject.isRecording since.
internal void ClearDirtyComponentsDirtyBits()
{
foreach (NetworkBehaviour comp in NetworkBehaviours)
{
if (comp.IsDirty())
{
comp.ClearAllDirtyBits();
}
}
}
void ResetSyncObjects() void ResetSyncObjects()
{ {
// ResetSyncObjects is called by Reset, which is called by Unity. // ResetSyncObjects is called by Reset, which is called by Unity.

View File

@ -93,9 +93,6 @@ protected void DrawDefaultSyncSettings()
if (syncDirection.enumValueIndex == (int)SyncDirection.ServerToClient) if (syncDirection.enumValueIndex == (int)SyncDirection.ServerToClient)
EditorGUILayout.PropertyField(serializedObject.FindProperty("syncMode")); EditorGUILayout.PropertyField(serializedObject.FindProperty("syncMode"));
// sync interval
EditorGUILayout.PropertyField(serializedObject.FindProperty("syncInterval"));
// apply // apply
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
} }

View File

@ -28,6 +28,11 @@ public virtual void SetUp()
// need a transport to send & receive // need a transport to send & receive
Transport.active = transport = holder.AddComponent<MemoryTransport>(); Transport.active = transport = holder.AddComponent<MemoryTransport>();
// always set send interval to 'immediately' for easier testing
NetworkServer.tickRate = 0;
// NetworkServer.sendRate := tickRate
// NetworkClient.sendRate := NetworkServer.sendRate
} }
public virtual void TearDown() public virtual void TearDown()
@ -89,8 +94,6 @@ protected void CreateNetworked<T>(out GameObject go, out NetworkIdentity identit
go = new GameObject(); go = new GameObject();
identity = go.AddComponent<NetworkIdentity>(); identity = go.AddComponent<NetworkIdentity>();
component = go.AddComponent<T>(); component = go.AddComponent<T>();
// always set syncinterval = 0 for immediate testing
component.syncInterval = 0;
// Awake is only called in play mode. // Awake is only called in play mode.
// call manually for initialization. // call manually for initialization.
identity.Awake(); identity.Awake();
@ -108,9 +111,6 @@ protected void CreateNetworked<T, U>(out GameObject go, out NetworkIdentity iden
identity = go.AddComponent<NetworkIdentity>(); identity = go.AddComponent<NetworkIdentity>();
componentA = go.AddComponent<T>(); componentA = go.AddComponent<T>();
componentB = go.AddComponent<U>(); componentB = go.AddComponent<U>();
// always set syncinterval = 0 for immediate testing
componentA.syncInterval = 0;
componentB.syncInterval = 0;
// Awake is only called in play mode. // Awake is only called in play mode.
// call manually for initialization. // call manually for initialization.
identity.Awake(); identity.Awake();
@ -130,10 +130,6 @@ protected void CreateNetworked<T, U, V>(out GameObject go, out NetworkIdentity i
componentA = go.AddComponent<T>(); componentA = go.AddComponent<T>();
componentB = go.AddComponent<U>(); componentB = go.AddComponent<U>();
componentC = go.AddComponent<V>(); componentC = go.AddComponent<V>();
// always set syncinterval = 0 for immediate testing
componentA.syncInterval = 0;
componentB.syncInterval = 0;
componentC.syncInterval = 0;
// Awake is only called in play mode. // Awake is only called in play mode.
// call manually for initialization. // call manually for initialization.
identity.Awake(); identity.Awake();

View File

@ -94,10 +94,6 @@ public void IsDirty()
comp.list.Add(42); comp.list.Add(42);
Assert.That(comp.IsDirty(), Is.True); Assert.That(comp.IsDirty(), Is.True);
comp.ClearAllDirtyBits(); comp.ClearAllDirtyBits();
// it should only be dirty after syncInterval elapsed
comp.syncInterval = float.MaxValue;
Assert.That(comp.IsDirty(), Is.False);
} }
[Test] [Test]
@ -106,8 +102,6 @@ public void ClearAllDirtyBitsClearsSyncVarDirtyBits()
CreateNetworkedAndSpawn(out _, out _, out EmptyBehaviour emptyBehaviour, CreateNetworkedAndSpawn(out _, out _, out EmptyBehaviour emptyBehaviour,
out _, out _, out _); out _, out _, out _);
// set syncinterval so dirtybit works fine
emptyBehaviour.syncInterval = 0;
Assert.That(emptyBehaviour.IsDirty(), Is.False); Assert.That(emptyBehaviour.IsDirty(), Is.False);
// set one syncvar dirty bit // set one syncvar dirty bit
@ -125,8 +119,6 @@ public void ClearAllDirtyBitsClearsSyncObjectsDirtyBits()
CreateNetworkedAndSpawn(out _, out _, out NetworkBehaviourWithSyncVarsAndCollections comp, CreateNetworkedAndSpawn(out _, out _, out NetworkBehaviourWithSyncVarsAndCollections comp,
out _, out _, out _); out _, out _, out _);
// set syncinterval so dirtybit works fine
comp.syncInterval = 0;
Assert.That(comp.IsDirty(), Is.False); Assert.That(comp.IsDirty(), Is.False);
// dirty the synclist // dirty the synclist
@ -162,7 +154,6 @@ public void DirtyBitsAreClearedForSpawnedWithoutObservers()
Assert.That(monster.observers.Count, Is.EqualTo(0)); Assert.That(monster.observers.Count, Is.EqualTo(0));
// modify something in the monster so that dirty bit is set // modify something in the monster so that dirty bit is set
monsterComp.syncInterval = 0;
++monsterComp.health; ++monsterComp.health;
Assert.That(monsterComp.IsDirty(), Is.True); Assert.That(monsterComp.IsDirty(), Is.True);

View File

@ -276,7 +276,6 @@ public void SerializeServer_OwnerMode_ClientToServer()
// pretend to be owned // pretend to be owned
identity.isOwned = true; identity.isOwned = true;
comp.syncMode = SyncMode.Owner; comp.syncMode = SyncMode.Owner;
comp.syncInterval = 0;
// set to CLIENT with some unique values // set to CLIENT with some unique values
// and set connection to server to pretend we are the owner. // and set connection to server to pretend we are the owner.
@ -312,7 +311,6 @@ public void SerializeServer_ObserversMode_ClientToServer()
// pretend to be owned // pretend to be owned
identity.isOwned = true; identity.isOwned = true;
comp.syncMode = SyncMode.Observers; comp.syncMode = SyncMode.Observers;
comp.syncInterval = 0;
// set to CLIENT with some unique values // set to CLIENT with some unique values
// and set connection to server to pretend we are the owner. // and set connection to server to pretend we are the owner.

View File

@ -832,38 +832,6 @@ public void ClearObservers()
Assert.That(identity.observers.Count, Is.EqualTo(0)); Assert.That(identity.observers.Count, Is.EqualTo(0));
} }
[Test]
public void ClearDirtyComponentsDirtyBits()
{
CreateNetworked(out GameObject _, out NetworkIdentity identity,
out OnStartClientTestNetworkBehaviour compA,
out OnStartClientTestNetworkBehaviour compB);
// set syncintervals so one is always dirty, one is never dirty
compA.syncInterval = 0;
compB.syncInterval = Mathf.Infinity;
// set components dirty bits
compA.SetSyncVarDirtyBit(0x0001);
compB.SetSyncVarDirtyBit(0x1001);
// dirty because interval reached and mask != 0
Assert.That(compA.IsDirty(), Is.True);
// not dirty because syncinterval not reached
Assert.That(compB.IsDirty(), Is.False);
// call identity.ClearDirtyComponentsDirtyBits
identity.ClearDirtyComponentsDirtyBits();
// should be cleared now
Assert.That(compA.IsDirty(), Is.False);
// should be untouched
Assert.That(compB.IsDirty(), Is.False);
// set compB syncinterval to 0 to check if the masks were untouched
// (if they weren't, then it should be dirty now)
compB.syncInterval = 0;
Assert.That(compB.IsDirty(), Is.True);
}
[Test] [Test]
public void ClearAllComponentsDirtyBits() public void ClearAllComponentsDirtyBits()
{ {
@ -871,17 +839,13 @@ public void ClearAllComponentsDirtyBits()
out OnStartClientTestNetworkBehaviour compA, out OnStartClientTestNetworkBehaviour compA,
out OnStartClientTestNetworkBehaviour compB); out OnStartClientTestNetworkBehaviour compB);
// set syncintervals so one is always dirty, one is never dirty
compA.syncInterval = 0;
compB.syncInterval = Mathf.Infinity;
// set components dirty bits // set components dirty bits
compA.SetSyncVarDirtyBit(0x0001); compA.SetSyncVarDirtyBit(0x0001);
compB.SetSyncVarDirtyBit(0x1001); compB.SetSyncVarDirtyBit(0x1001);
// dirty because interval reached and mask != 0 // dirty because interval reached and mask != 0
Assert.That(compA.IsDirty(), Is.True); Assert.That(compA.IsDirty(), Is.True);
// not dirty because syncinterval not reached // dirty because interval reached and mask != 0
Assert.That(compB.IsDirty(), Is.False); Assert.That(compB.IsDirty(), Is.True);
// call identity.ClearAllComponentsDirtyBits // call identity.ClearAllComponentsDirtyBits
identity.ClearAllComponentsDirtyBits(); identity.ClearAllComponentsDirtyBits();
@ -889,11 +853,6 @@ public void ClearAllComponentsDirtyBits()
Assert.That(compA.IsDirty(), Is.False); Assert.That(compA.IsDirty(), Is.False);
// should be cleared now // should be cleared now
Assert.That(compB.IsDirty(), Is.False); Assert.That(compB.IsDirty(), Is.False);
// set compB syncinterval to 0 to check if the masks were cleared
// (if they weren't, then it would still be dirty now)
compB.syncInterval = 0;
Assert.That(compB.IsDirty(), Is.False);
} }
[Test] [Test]

View File

@ -40,8 +40,6 @@ public override void SetUp()
// create a networked object with NetworkTransform // create a networked object with NetworkTransform
CreateNetworkedAndSpawn(out GameObject go, out NetworkIdentity _, out component, connectionToClient); CreateNetworkedAndSpawn(out GameObject go, out NetworkIdentity _, out component, connectionToClient);
// sync immediately
component.syncInterval = 0;
// remember transform for convenience // remember transform for convenience
transform = go.transform; transform = go.transform;
} }

View File

@ -84,9 +84,6 @@ public void TestSettingStruct()
{ {
CreateNetworked(out _, out _, out MockPlayer player); CreateNetworked(out _, out _, out MockPlayer player);
// synchronize immediately
player.syncInterval = 0f;
Assert.That(player.IsDirty(), Is.False, "First time object should not be dirty"); Assert.That(player.IsDirty(), Is.False, "First time object should not be dirty");
MockPlayer.Guild myGuild = new MockPlayer.Guild MockPlayer.Guild myGuild = new MockPlayer.Guild
@ -105,58 +102,6 @@ public void TestSettingStruct()
Assert.That(player.IsDirty(), "Clearing struct should mark object as dirty"); Assert.That(player.IsDirty(), "Clearing struct should mark object as dirty");
} }
[Test]
public void TestSyncIntervalAndClearDirtyComponents()
{
CreateNetworked(out _, out _, out MockPlayer player);
player.lastSyncTime = NetworkTime.localTime;
// synchronize immediately
player.syncInterval = 1f;
player.guild = new MockPlayer.Guild
{
name = "Back street boys"
};
Assert.That(player.IsDirty(), Is.False, "Sync interval not met, so not dirty yet");
// ClearDirtyComponents should do nothing since syncInterval is not
// elapsed yet
player.netIdentity.ClearDirtyComponentsDirtyBits();
// set lastSyncTime far enough back to be ready for syncing
player.lastSyncTime = NetworkTime.localTime - player.syncInterval;
// should be dirty now
Assert.That(player.IsDirty(), Is.True, "Sync interval met, should be dirty");
}
[Test]
public void TestSyncIntervalAndClearAllComponents()
{
CreateNetworked(out _, out _, out MockPlayer player);
player.lastSyncTime = NetworkTime.localTime;
// synchronize immediately
player.syncInterval = 1f;
player.guild = new MockPlayer.Guild
{
name = "Back street boys"
};
Assert.That(player.IsDirty(), Is.False, "Sync interval not met, so not dirty yet");
// ClearAllComponents should clear dirty even if syncInterval not
// elapsed yet
player.netIdentity.ClearAllComponentsDirtyBits();
// set lastSyncTime far enough back to be ready for syncing
player.lastSyncTime = NetworkTime.localTime - player.syncInterval;
// should be dirty now
Assert.That(player.IsDirty(), Is.False, "Sync interval met, should still not be dirty");
}
[Test] [Test]
public void SyncsGameobject() public void SyncsGameobject()
{ {