mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-17 18:40:33 +00:00
simplify
This commit is contained in:
parent
4367430b29
commit
318b8846bf
@ -29,7 +29,6 @@ public abstract class NetworkTransformBase : NetworkBehaviour
|
||||
[Tooltip("The Transform component to sync. May be on on this GameObject, or on a child.")]
|
||||
public Transform target;
|
||||
|
||||
// TODO SyncDirection { ClientToServer, ServerToClient } is easier?
|
||||
// Deprecated 2022-10-25
|
||||
[Obsolete("NetworkTransform clientAuthority was replaced with syncDirection. To enable client authority, set SyncDirection to ClientToServer in the Inspector.")]
|
||||
[Header("[Obsolete]")] // Unity doesn't show obsolete warning for fields. do it manually.
|
||||
|
@ -1,6 +1,4 @@
|
||||
// NetworkTransform V3 (reliable) by mischa (2022-10)
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror
|
||||
@ -8,15 +6,7 @@ namespace Mirror
|
||||
[AddComponentMenu("Network/Network Transform (Reliable)")]
|
||||
public class NetworkTransformReliable : NetworkTransformBase
|
||||
{
|
||||
[Header("Sync Only If Changed")]
|
||||
[Tooltip("When true, changes are not sent unless greater than sensitivity values below.")]
|
||||
public bool onlySyncOnChange = true;
|
||||
[Tooltip("If we only sync on change, then we need to correct old snapshots if more time than sendInterval * multiplier has elapsed.\n\nOtherwise the first move will always start interpolating from the last move sequence's time, which will make it stutter when starting every time.")]
|
||||
public float onlySyncOnChangeCorrectionMultiplier = 2;
|
||||
|
||||
[Header("Rotation")]
|
||||
[Tooltip("Sensitivity of changes needed before an updated state is sent over the network")]
|
||||
public float rotationSensitivity = 0.01f;
|
||||
[Tooltip("Apply smallest-three quaternion compression. This is lossy, you can disable it if the small rotation inaccuracies are noticeable in your project.")]
|
||||
public bool compressRotation = false;
|
||||
|
||||
@ -44,9 +34,6 @@ public class NetworkTransformReliable : NetworkTransformBase
|
||||
protected Vector3Long lastSerializedScale = Vector3Long.zero;
|
||||
protected Vector3Long lastDeserializedScale = Vector3Long.zero;
|
||||
|
||||
// Used to store last sent snapshots
|
||||
protected TransformSnapshot last;
|
||||
|
||||
// update //////////////////////////////////////////////////////////////
|
||||
// Update applies interpolation.
|
||||
void Update()
|
||||
@ -68,8 +55,7 @@ void LateUpdate()
|
||||
// set dirty to trigger OnSerialize. either always, or only if changed.
|
||||
if (isServer || (IsClientWithAuthority && NetworkClient.ready)) // is NetworkClient.ready even needed?
|
||||
{
|
||||
if (!onlySyncOnChange || Changed(Construct()))
|
||||
SetDirty();
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,29 +115,6 @@ protected virtual void UpdateClient()
|
||||
}
|
||||
}
|
||||
|
||||
// check if position / rotation / scale changed since last sync
|
||||
protected virtual bool Changed(TransformSnapshot current) =>
|
||||
// position is quantized and delta compressed.
|
||||
// only consider it changed if the quantized representation is changed.
|
||||
// careful: don't use 'serialized / deserialized last'. as it depends on sync mode etc.
|
||||
QuantizedChanged(last.position, current.position, positionPrecision) ||
|
||||
// rotation isn't quantized / delta compressed.
|
||||
// check with sensitivity.
|
||||
Quaternion.Angle(last.rotation, current.rotation) > rotationSensitivity ||
|
||||
// scale is quantized and delta compressed.
|
||||
// only consider it changed if the quantized representation is changed.
|
||||
// careful: don't use 'serialized / deserialized last'. as it depends on sync mode etc.
|
||||
QuantizedChanged(last.scale, current.scale, scalePrecision);
|
||||
|
||||
// helper function to compare quantized representations of a Vector3
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool QuantizedChanged(Vector3 u, Vector3 v, float precision)
|
||||
{
|
||||
Compression.ScaleToLong(u, precision, out Vector3Long uQuantized);
|
||||
Compression.ScaleToLong(v, precision, out Vector3Long vQuantized);
|
||||
return uQuantized != vQuantized;
|
||||
}
|
||||
|
||||
// NT may be used on client/server/host to Owner/Observers with
|
||||
// ServerToClient or ClientToServer.
|
||||
// however, OnSerialize should always delta against last.
|
||||
@ -217,9 +180,6 @@ public override void OnSerialize(NetworkWriter writer, bool initialState)
|
||||
// save serialized as 'last' for next delta compression
|
||||
if (syncPosition) Compression.ScaleToLong(snapshot.position, positionPrecision, out lastSerializedPosition);
|
||||
if (syncScale) Compression.ScaleToLong(snapshot.scale, scalePrecision, out lastSerializedScale);
|
||||
|
||||
// set 'last'
|
||||
last = snapshot;
|
||||
}
|
||||
|
||||
public override void OnDeserialize(NetworkReader reader, bool initialState)
|
||||
@ -287,21 +247,6 @@ protected virtual void OnClientToServerSync(Vector3? position, Quaternion? rotat
|
||||
// protect against ever growing buffer size attacks
|
||||
if (serverSnapshots.Count >= connectionToClient.snapshotBufferSizeLimit) return;
|
||||
|
||||
// 'only sync on change' needs a correction on every new move sequence.
|
||||
if (onlySyncOnChange &&
|
||||
NeedsCorrection(serverSnapshots, connectionToClient.remoteTimeStamp, NetworkServer.sendInterval, onlySyncOnChangeCorrectionMultiplier))
|
||||
{
|
||||
RewriteHistory(
|
||||
serverSnapshots,
|
||||
connectionToClient.remoteTimeStamp,
|
||||
NetworkTime.localTime, // arrival remote timestamp. NOT remote timeline.
|
||||
NetworkServer.sendInterval, // Unity 2019 doesn't have timeAsDouble yet
|
||||
target.localPosition,
|
||||
target.localRotation,
|
||||
target.localScale);
|
||||
// Debug.Log($"{name}: corrected history on server to fix initial stutter after not sending for a while.");
|
||||
}
|
||||
|
||||
// add a small timeline offset to account for decoupled arrival of
|
||||
// NetworkTime and NetworkTransform snapshots.
|
||||
// needs to be sendInterval. half sendInterval doesn't solve it.
|
||||
@ -317,21 +262,6 @@ protected virtual void OnServerToClientSync(Vector3? position, Quaternion? rotat
|
||||
// don't apply for local player with authority
|
||||
if (IsClientWithAuthority) return;
|
||||
|
||||
// 'only sync on change' needs a correction on every new move sequence.
|
||||
if (onlySyncOnChange &&
|
||||
NeedsCorrection(clientSnapshots, NetworkClient.connection.remoteTimeStamp, NetworkClient.sendInterval, onlySyncOnChangeCorrectionMultiplier))
|
||||
{
|
||||
RewriteHistory(
|
||||
clientSnapshots,
|
||||
NetworkClient.connection.remoteTimeStamp, // arrival remote timestamp. NOT remote timeline.
|
||||
NetworkTime.localTime, // Unity 2019 doesn't have timeAsDouble yet
|
||||
NetworkClient.sendInterval,
|
||||
target.localPosition,
|
||||
target.localRotation,
|
||||
target.localScale);
|
||||
// Debug.Log($"{name}: corrected history on client to fix initial stutter after not sending for a while.");
|
||||
}
|
||||
|
||||
// add a small timeline offset to account for decoupled arrival of
|
||||
// NetworkTime and NetworkTransform snapshots.
|
||||
// needs to be sendInterval. half sendInterval doesn't solve it.
|
||||
@ -342,47 +272,6 @@ protected virtual void OnServerToClientSync(Vector3? position, Quaternion? rotat
|
||||
}
|
||||
|
||||
// only sync on change /////////////////////////////////////////////////
|
||||
// snap interp. needs a continous flow of packets.
|
||||
// 'only sync on change' interrupts it while not changed.
|
||||
// once it restarts, snap interp. will interp from the last old position.
|
||||
// this will cause very noticeable stutter for the first move each time.
|
||||
// the fix is quite simple.
|
||||
|
||||
// 1. detect if the remaining snapshot is too old from a past move.
|
||||
static bool NeedsCorrection(
|
||||
SortedList<double, TransformSnapshot> snapshots,
|
||||
double remoteTimestamp,
|
||||
double bufferTime,
|
||||
double toleranceMultiplier) =>
|
||||
snapshots.Count == 1 &&
|
||||
remoteTimestamp - snapshots.Keys[0] >= bufferTime * toleranceMultiplier;
|
||||
|
||||
// 2. insert a fake snapshot at current position,
|
||||
// exactly one 'sendInterval' behind the newly received one.
|
||||
static void RewriteHistory(
|
||||
SortedList<double, TransformSnapshot> snapshots,
|
||||
// timestamp of packet arrival, not interpolated remote time!
|
||||
double remoteTimeStamp,
|
||||
double localTime,
|
||||
double sendInterval,
|
||||
Vector3 position,
|
||||
Quaternion rotation,
|
||||
Vector3 scale)
|
||||
{
|
||||
// clear the previous snapshot
|
||||
snapshots.Clear();
|
||||
|
||||
// insert a fake one at where we used to be,
|
||||
// 'sendInterval' behind the new one.
|
||||
SnapshotInterpolation.InsertIfNotExists(snapshots, new TransformSnapshot(
|
||||
remoteTimeStamp - sendInterval, // arrival remote timestamp. NOT remote time.
|
||||
localTime - sendInterval, // Unity 2019 doesn't have timeAsDouble yet
|
||||
position,
|
||||
rotation,
|
||||
scale
|
||||
));
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
@ -393,9 +282,6 @@ public override void Reset()
|
||||
|
||||
lastSerializedScale = Vector3Long.zero;
|
||||
lastDeserializedScale = Vector3Long.zero;
|
||||
|
||||
// reset 'last' for delta too
|
||||
last = new TransformSnapshot(0, 0, Vector3.zero, Quaternion.identity, Vector3.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
// NetworkTransform V2 by mischa (2021-07)
|
||||
// comment out the below line to quickly revert the onlySyncOnChange feature
|
||||
#define onlySyncOnChange_BANDWIDTH_SAVING
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror
|
||||
@ -8,31 +7,6 @@ namespace Mirror
|
||||
[AddComponentMenu("Network/Network Transform (Unreliable)")]
|
||||
public class NetworkTransform : NetworkTransformBase
|
||||
{
|
||||
// only sync when changed hack /////////////////////////////////////////
|
||||
#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 TransformSnapshot lastSnapshot;
|
||||
protected bool cachedSnapshotComparison;
|
||||
protected bool hasSentUnchangedPosition;
|
||||
#endif
|
||||
|
||||
double lastClientSendTime;
|
||||
double lastServerSendTime;
|
||||
|
||||
@ -84,39 +58,15 @@ void UpdateServerBroadcast()
|
||||
// send snapshot without timestamp.
|
||||
// receiver gets it from batch timestamp to save bandwidth.
|
||||
TransformSnapshot snapshot = Construct();
|
||||
#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 && 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
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,39 +140,15 @@ void UpdateClientBroadcast()
|
||||
// send snapshot without timestamp.
|
||||
// receiver gets it from batch timestamp to save bandwidth.
|
||||
TransformSnapshot snapshot = Construct();
|
||||
#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 && 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
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,17 +212,6 @@ public override void OnDeserialize(NetworkReader reader, bool initialState)
|
||||
}
|
||||
}
|
||||
|
||||
#if onlySyncOnChange_BANDWIDTH_SAVING
|
||||
// Returns true if position, rotation AND scale are unchanged, within given sensitivity range.
|
||||
protected virtual bool CompareSnapshots(TransformSnapshot 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)]
|
||||
@ -323,17 +238,6 @@ 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 * NetworkClient.sendInterval;
|
||||
|
||||
if (serverSnapshots.Count > 0 && serverSnapshots.Values[serverSnapshots.Count - 1].remoteTime + timeIntervalCheck < timestamp)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
AddSnapshot(serverSnapshots, timestamp, position, rotation, scale);
|
||||
}
|
||||
|
||||
@ -362,17 +266,6 @@ 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 * NetworkServer.sendInterval;
|
||||
|
||||
if (clientSnapshots.Count > 0 && clientSnapshots.Values[clientSnapshots.Count - 1].remoteTime + timeIntervalCheck < timestamp)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
AddSnapshot(clientSnapshots, timestamp, position, rotation, scale);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user