NetworkTransform Improvements Reeeeesubmission (#3025)

* NetworkTransform Improvements Reeeeesubmission

1: Optional boolean to allow onlySyncOnChange.

2: Checks if pos/rot/scale changed, if not, sends the value as null (1byte)

* NetworkTransform Improvements +defines

1: Optional boolean to allow onlySyncOnChange.

2: Checks if pos/rot/scale changed, if not, sends the value as null (1byte)

Contains the bundle of latest NT adjustments from the squad.
#3013
#3021
#3019
#3018

Along with a fix for applying latest snapshot, if it exists, before applying local data.
#3024
This commit is contained in:
JesusLuvsYooh 2021-12-11 14:59:34 +00:00 committed by GitHub
parent b0b01938e6
commit e941fd9e5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -17,6 +17,8 @@
// -> client gets Cmd() and X at the same time, but buffers X for bufferTime
// -> for unreliable, it would get X before the reliable Cmd(), still
// buffer for bufferTime but end up closer to the original time
// comment out the below line to quickly revert the onlySyncOnChange feature
#define onlySyncOnChange_BANDWIDTH_SAVING
using System;
using System.Collections.Generic;
using UnityEngine;
@ -75,6 +77,30 @@ public abstract class NetworkTransformBase : NetworkBehaviour
[Tooltip("Once buffer is larger catchupThreshold, accelerate by multiplier % per excess entry.")]
[Range(0, 1)] public float catchupMultiplier = 0.10f;
#if onlySyncOnChange_BANDWIDTH_SAVING
[Header("Sync Only If Changed")]
[Tooltip("When true, changes are not sent unless greater than sensitivity values below.")]
public bool onlySyncOnChange = true;
// 3 was original, but testing under really bad network conditions, 2%-5% packet loss and 250-1200ms ping, 5 proved to eliminate any twitching.
[Tooltip("How much time, as a multiple of send interval, has passed before clearing buffers.")]
public float bufferResetMultiplier = 5;
[Header("Sensitivity"), Tooltip("Sensitivity of changes needed before an updated state is sent over the network")]
public float positionSensitivity = 0.01f;
public float rotationSensitivity = 0.01f;
public float scaleSensitivity = 0.01f;
protected bool positionChanged;
protected bool rotationChanged;
protected bool scaleChanged;
// Used to store last sent snapshots
protected NTSnapshot lastSnapshot;
protected bool cachedSnapshotComparison;
protected bool hasSentUnchangedPosition;
#endif
// snapshots sorted by timestamp
// in the original article, glenn fiedler drops any snapshots older than
// the last received snapshot.
@ -148,7 +174,17 @@ protected virtual void ApplySnapshot(NTSnapshot start, NTSnapshot goal, NTSnapsh
if (syncScale)
targetComponent.localScale = interpolateScale ? interpolated.scale : goal.scale;
}
#if onlySyncOnChange_BANDWIDTH_SAVING
// Returns true if position, rotation AND scale are unchanged, within given sensitivity range.
protected virtual bool CompareSnapshots(NTSnapshot currentSnapshot)
{
positionChanged = Vector3.SqrMagnitude(lastSnapshot.position - currentSnapshot.position) > positionSensitivity * positionSensitivity;
rotationChanged = Quaternion.Angle(lastSnapshot.rotation, currentSnapshot.rotation) > rotationSensitivity;
scaleChanged = Vector3.SqrMagnitude(lastSnapshot.scale - currentSnapshot.scale) > scaleSensitivity * scaleSensitivity;
return (!positionChanged && !rotationChanged && !scaleChanged);
}
#endif
// cmd /////////////////////////////////////////////////////////////////
// only unreliable. see comment above of this file.
[Command(channel = Channels.Unreliable)]
@ -175,7 +211,17 @@ protected virtual void OnClientToServerSync(Vector3? position, Quaternion? rotat
// only player owned objects (with a connection) can send to
// server. we can get the timestamp from the connection.
double timestamp = connectionToClient.remoteTimeStamp;
#if onlySyncOnChange_BANDWIDTH_SAVING
if (onlySyncOnChange)
{
double timeIntervalCheck = bufferResetMultiplier * sendInterval;
if (serverBuffer.Count > 0 && serverBuffer.Values[serverBuffer.Count - 1].remoteTimestamp + timeIntervalCheck < timestamp)
{
Reset();
}
}
#endif
// position, rotation, scale can have no value if same as last time.
// saves bandwidth.
// but we still need to feed it to snapshot interpolation. we can't
@ -185,9 +231,9 @@ protected virtual void OnClientToServerSync(Vector3? position, Quaternion? rotat
// client sends snapshot at t=10
// then the server would assume that it's one super slow move and
// replay it for 10 seconds.
if (!position.HasValue) position = targetComponent.localPosition;
if (!rotation.HasValue) rotation = targetComponent.localRotation;
if (!scale.HasValue) scale = targetComponent.localScale;
if (!position.HasValue) position = serverBuffer.Count > 0 ? serverBuffer.Values[serverBuffer.Count - 1].position : targetComponent.localPosition;
if (!rotation.HasValue) rotation = serverBuffer.Count > 0 ? serverBuffer.Values[serverBuffer.Count - 1].rotation : targetComponent.localRotation;
if (!scale.HasValue) scale = serverBuffer.Count > 0 ? serverBuffer.Values[serverBuffer.Count - 1].scale : targetComponent.localScale;
// construct snapshot with batch timestamp to save bandwidth
NTSnapshot snapshot = new NTSnapshot(
@ -228,7 +274,17 @@ protected virtual void OnServerToClientSync(Vector3? position, Quaternion? rotat
// but all of them go through NetworkClient.connection.
// we can get the timestamp from there.
double timestamp = NetworkClient.connection.remoteTimeStamp;
#if onlySyncOnChange_BANDWIDTH_SAVING
if (onlySyncOnChange)
{
double timeIntervalCheck = bufferResetMultiplier * sendInterval;
if (clientBuffer.Count > 0 && clientBuffer.Values[clientBuffer.Count - 1].remoteTimestamp + timeIntervalCheck < timestamp)
{
Reset();
}
}
#endif
// position, rotation, scale can have no value if same as last time.
// saves bandwidth.
// but we still need to feed it to snapshot interpolation. we can't
@ -238,9 +294,9 @@ protected virtual void OnServerToClientSync(Vector3? position, Quaternion? rotat
// client sends snapshot at t=10
// then the server would assume that it's one super slow move and
// replay it for 10 seconds.
if (!position.HasValue) position = targetComponent.localPosition;
if (!rotation.HasValue) rotation = targetComponent.localRotation;
if (!scale.HasValue) scale = targetComponent.localScale;
if (!position.HasValue) position = clientBuffer.Count > 0 ? clientBuffer.Values[clientBuffer.Count - 1].position : targetComponent.localPosition;
if (!rotation.HasValue) rotation = clientBuffer.Count > 0 ? clientBuffer.Values[clientBuffer.Count - 1].rotation : targetComponent.localRotation;
if (!scale.HasValue) scale = clientBuffer.Count > 0 ? clientBuffer.Values[clientBuffer.Count - 1].scale : targetComponent.localScale;
// construct snapshot with batch timestamp to save bandwidth
NTSnapshot snapshot = new NTSnapshot(
@ -291,14 +347,39 @@ void UpdateServer()
// send snapshot without timestamp.
// receiver gets it from batch timestamp to save bandwidth.
NTSnapshot snapshot = ConstructSnapshot();
#if onlySyncOnChange_BANDWIDTH_SAVING
cachedSnapshotComparison = CompareSnapshots(snapshot);
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
#endif
#if onlySyncOnChange_BANDWIDTH_SAVING
RpcServerToClientSync(
// only sync what the user wants to sync
syncPosition ? snapshot.position : new Vector3?(),
syncRotation ? snapshot.rotation : new Quaternion?(),
syncScale ? snapshot.scale : new Vector3?()
syncPosition && positionChanged ? snapshot.position : default(Vector3?),
syncRotation && rotationChanged ? snapshot.rotation : default(Quaternion?),
syncScale && scaleChanged ? snapshot.scale : default(Vector3?)
);
#else
RpcServerToClientSync(
// only sync what the user wants to sync
syncPosition ? snapshot.position : default(Vector3?),
syncRotation ? snapshot.rotation : default(Quaternion?),
syncScale ? snapshot.scale : default(Vector3?)
);
#endif
lastServerSendTime = NetworkTime.localTime;
#if onlySyncOnChange_BANDWIDTH_SAVING
if (cachedSnapshotComparison)
{
hasSentUnchangedPosition = true;
}
else
{
hasSentUnchangedPosition = false;
lastSnapshot = snapshot;
}
#endif
}
// apply buffered snapshots IF client authority
@ -359,14 +440,39 @@ void UpdateClient()
// send snapshot without timestamp.
// receiver gets it from batch timestamp to save bandwidth.
NTSnapshot snapshot = ConstructSnapshot();
#if onlySyncOnChange_BANDWIDTH_SAVING
cachedSnapshotComparison = CompareSnapshots(snapshot);
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
#endif
#if onlySyncOnChange_BANDWIDTH_SAVING
CmdClientToServerSync(
// only sync what the user wants to sync
syncPosition ? snapshot.position : new Vector3?(),
syncRotation ? snapshot.rotation : new Quaternion?(),
syncScale ? snapshot.scale : new Vector3?()
syncPosition && positionChanged ? snapshot.position : default(Vector3?),
syncRotation && rotationChanged ? snapshot.rotation : default(Quaternion?),
syncScale && scaleChanged ? snapshot.scale : default(Vector3?)
);
#else
CmdClientToServerSync(
// only sync what the user wants to sync
syncPosition ? snapshot.position : default(Vector3?),
syncRotation ? snapshot.rotation : default(Quaternion?),
syncScale ? snapshot.scale : default(Vector3?)
);
#endif
lastClientSendTime = NetworkTime.localTime;
#if onlySyncOnChange_BANDWIDTH_SAVING
if (cachedSnapshotComparison)
{
hasSentUnchangedPosition = true;
}
else
{
hasSentUnchangedPosition = false;
lastSnapshot = snapshot;
}
#endif
}
}
// for all other clients (and for local player if !authority),