mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
feat: NT-UR bit flag changed detection to lower bandwidth usage. (#3721)
* feat: NT-UR bit flag changed detection to lower bandwidth usage. Also major credits to our Ninja. * Tooltip updated * fix: NT-Unreliable Quaternion Compression Fix Credits to ninja of course :D * NT-U new improvements Credits to Ninja * Nothing to see here.. * Added comment to Quat Rotation Fix * Sensitivity check to improve value comparisons. Without this, X 0 and X -4.955753E-07 (0) would trigger as a change of value. Helps epsilon/floating point inaccuracies. * Moved around checks. rotationChanged not needed now for non-compressed bool, as we check individual rotation sensitivity changes. We can move this inside quat compress check. * Use Rot/All, not just RotX as a compress changed flag. * Set Just Rot. * Updated Reset to ResetState * Fixing PR 3571/3572/3572 in this new bitflag branch --------- Co-authored-by: ninjakickja <80569286+ninjakickja@users.noreply.github.com>
This commit is contained in:
parent
3e0a6ae64e
commit
fbd64dfb79
@ -1,4 +1,5 @@
|
|||||||
// NetworkTransform V2 by mischa (2021-07)
|
// NetworkTransform V2 by mischa (2021-07)
|
||||||
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Mirror
|
namespace Mirror
|
||||||
@ -12,6 +13,8 @@ public class NetworkTransformUnreliable : NetworkTransformBase
|
|||||||
// Testing under really bad network conditions, 2%-5% packet loss and 250-1200ms ping, 5 proved to eliminate any twitching, however this should not be the default as it is a rare case Developers may want to cover.
|
// Testing under really bad network conditions, 2%-5% packet loss and 250-1200ms ping, 5 proved to eliminate any twitching, however this should not be the default as it is a rare case Developers may want to cover.
|
||||||
[Tooltip("How much time, as a multiple of send interval, has passed before clearing buffers.\nA larger buffer means more delay, but results in smoother movement.\nExample: 1 for faster responses minimal smoothing, 5 covers bad pings but has noticable delay, 3 is recommended for balanced results,.")]
|
[Tooltip("How much time, as a multiple of send interval, has passed before clearing buffers.\nA larger buffer means more delay, but results in smoother movement.\nExample: 1 for faster responses minimal smoothing, 5 covers bad pings but has noticable delay, 3 is recommended for balanced results,.")]
|
||||||
public float bufferResetMultiplier = 3;
|
public float bufferResetMultiplier = 3;
|
||||||
|
[Tooltip("Detect and send only changed data, such as Position X and Z, not the full Vector3 of X Y Z. Lowers network data at cost of extra calculations.")]
|
||||||
|
public bool changedDetection = true;
|
||||||
|
|
||||||
[Header("Sensitivity"), Tooltip("Sensitivity of changes needed before an updated state is sent over the network")]
|
[Header("Sensitivity"), Tooltip("Sensitivity of changes needed before an updated state is sent over the network")]
|
||||||
public float positionSensitivity = 0.01f;
|
public float positionSensitivity = 0.01f;
|
||||||
@ -25,6 +28,7 @@ public class NetworkTransformUnreliable : NetworkTransformBase
|
|||||||
// Used to store last sent snapshots
|
// Used to store last sent snapshots
|
||||||
protected TransformSnapshot lastSnapshot;
|
protected TransformSnapshot lastSnapshot;
|
||||||
protected bool cachedSnapshotComparison;
|
protected bool cachedSnapshotComparison;
|
||||||
|
protected Changed cachedChangedComparison;
|
||||||
protected bool hasSentUnchangedPosition;
|
protected bool hasSentUnchangedPosition;
|
||||||
|
|
||||||
// update //////////////////////////////////////////////////////////////
|
// update //////////////////////////////////////////////////////////////
|
||||||
@ -61,7 +65,9 @@ protected virtual void CheckLastSendTime()
|
|||||||
// This fixes previous issue of, if sendIntervalMultiplier = 1, we send every frame,
|
// This fixes previous issue of, if sendIntervalMultiplier = 1, we send every frame,
|
||||||
// because intervalCounter is always = 1 in the previous version.
|
// because intervalCounter is always = 1 in the previous version.
|
||||||
|
|
||||||
if (sendIntervalCounter == sendIntervalMultiplier)
|
// Changing == to >= https://github.com/MirrorNetworking/Mirror/issues/3571
|
||||||
|
|
||||||
|
if (sendIntervalCounter >= sendIntervalMultiplier)
|
||||||
sendIntervalCounter = 0;
|
sendIntervalCounter = 0;
|
||||||
|
|
||||||
// timeAsDouble not available in older Unity versions.
|
// timeAsDouble not available in older Unity versions.
|
||||||
@ -109,6 +115,29 @@ void UpdateServerBroadcast()
|
|||||||
// send snapshot without timestamp.
|
// send snapshot without timestamp.
|
||||||
// receiver gets it from batch timestamp to save bandwidth.
|
// receiver gets it from batch timestamp to save bandwidth.
|
||||||
TransformSnapshot snapshot = Construct();
|
TransformSnapshot snapshot = Construct();
|
||||||
|
|
||||||
|
if (changedDetection)
|
||||||
|
{
|
||||||
|
cachedChangedComparison = CompareChangedSnapshots(snapshot);
|
||||||
|
|
||||||
|
if ((cachedChangedComparison == Changed.None || cachedChangedComparison == Changed.CompressRot) && hasSentUnchangedPosition && onlySyncOnChange) { return; }
|
||||||
|
|
||||||
|
SyncData syncData = new SyncData(cachedChangedComparison, snapshot);
|
||||||
|
|
||||||
|
RpcServerToClientSync(syncData);
|
||||||
|
|
||||||
|
if (cachedChangedComparison == Changed.None || cachedChangedComparison == Changed.CompressRot)
|
||||||
|
{
|
||||||
|
hasSentUnchangedPosition = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hasSentUnchangedPosition = false;
|
||||||
|
UpdateLastSentSnapshot(cachedChangedComparison, snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
cachedSnapshotComparison = CompareSnapshots(snapshot);
|
cachedSnapshotComparison = CompareSnapshots(snapshot);
|
||||||
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
|
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
|
||||||
|
|
||||||
@ -138,7 +167,16 @@ void UpdateServerBroadcast()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
hasSentUnchangedPosition = false;
|
hasSentUnchangedPosition = false;
|
||||||
lastSnapshot = snapshot;
|
|
||||||
|
// Fixes https://github.com/MirrorNetworking/Mirror/issues/3572
|
||||||
|
// This also fixes https://github.com/MirrorNetworking/Mirror/issues/3573
|
||||||
|
// with the exception of Quaternion.Angle sensitivity has to be > 0.16.
|
||||||
|
// Unity issue, we are leaving it as is.
|
||||||
|
|
||||||
|
if (positionChanged) lastSnapshot.position = snapshot.position;
|
||||||
|
if (rotationChanged) lastSnapshot.rotation = snapshot.rotation;
|
||||||
|
if (positionChanged) lastSnapshot.scale = snapshot.scale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,6 +243,29 @@ void UpdateClientBroadcast()
|
|||||||
// send snapshot without timestamp.
|
// send snapshot without timestamp.
|
||||||
// receiver gets it from batch timestamp to save bandwidth.
|
// receiver gets it from batch timestamp to save bandwidth.
|
||||||
TransformSnapshot snapshot = Construct();
|
TransformSnapshot snapshot = Construct();
|
||||||
|
|
||||||
|
if (changedDetection)
|
||||||
|
{
|
||||||
|
cachedChangedComparison = CompareChangedSnapshots(snapshot);
|
||||||
|
|
||||||
|
if ((cachedChangedComparison == Changed.None || cachedChangedComparison == Changed.CompressRot) && hasSentUnchangedPosition && onlySyncOnChange) { return; }
|
||||||
|
|
||||||
|
SyncData syncData = new SyncData(cachedChangedComparison, snapshot);
|
||||||
|
|
||||||
|
CmdClientToServerSync(syncData);
|
||||||
|
|
||||||
|
if (cachedChangedComparison == Changed.None || cachedChangedComparison == Changed.CompressRot)
|
||||||
|
{
|
||||||
|
hasSentUnchangedPosition = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hasSentUnchangedPosition = false;
|
||||||
|
UpdateLastSentSnapshot(cachedChangedComparison, snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
cachedSnapshotComparison = CompareSnapshots(snapshot);
|
cachedSnapshotComparison = CompareSnapshots(snapshot);
|
||||||
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
|
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
|
||||||
|
|
||||||
@ -234,7 +295,15 @@ void UpdateClientBroadcast()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
hasSentUnchangedPosition = false;
|
hasSentUnchangedPosition = false;
|
||||||
lastSnapshot = snapshot;
|
|
||||||
|
// Fixes https://github.com/MirrorNetworking/Mirror/issues/3572
|
||||||
|
// This also fixes https://github.com/MirrorNetworking/Mirror/issues/3573
|
||||||
|
// with the exception of Quaternion.Angle sensitivity has to be > 0.16.
|
||||||
|
// Unity issue, we are leaving it as is.
|
||||||
|
if (positionChanged) lastSnapshot.position = snapshot.position;
|
||||||
|
if (rotationChanged) lastSnapshot.rotation = snapshot.rotation;
|
||||||
|
if (positionChanged) lastSnapshot.scale = snapshot.scale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,5 +475,204 @@ protected virtual void OnServerToClientSync(Vector3? position, Quaternion? rotat
|
|||||||
|
|
||||||
AddSnapshot(clientSnapshots, NetworkClient.connection.remoteTimeStamp + timeStampAdjustment + offset, position, rotation, scale);
|
AddSnapshot(clientSnapshots, NetworkClient.connection.remoteTimeStamp + timeStampAdjustment + offset, position, rotation, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateLastSentSnapshot(Changed change, TransformSnapshot currentSnapshot)
|
||||||
|
{
|
||||||
|
if (change == Changed.None || change == Changed.CompressRot) return;
|
||||||
|
|
||||||
|
if ((change & Changed.PosX) > 0) lastSnapshot.position.x = currentSnapshot.position.x;
|
||||||
|
if ((change & Changed.PosY) > 0) lastSnapshot.position.y = currentSnapshot.position.y;
|
||||||
|
if ((change & Changed.PosZ) > 0) lastSnapshot.position.z = currentSnapshot.position.z;
|
||||||
|
|
||||||
|
if (compressRotation)
|
||||||
|
{
|
||||||
|
if ((change & Changed.Rot) > 0) lastSnapshot.rotation = currentSnapshot.rotation;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Vector3 newRotation;
|
||||||
|
newRotation.x = (change & Changed.RotX) > 0 ? currentSnapshot.rotation.eulerAngles.x : lastSnapshot.rotation.eulerAngles.x;
|
||||||
|
newRotation.y = (change & Changed.RotY) > 0 ? currentSnapshot.rotation.eulerAngles.y : lastSnapshot.rotation.eulerAngles.y;
|
||||||
|
newRotation.z = (change & Changed.RotZ) > 0 ? currentSnapshot.rotation.eulerAngles.z : lastSnapshot.rotation.eulerAngles.z;
|
||||||
|
|
||||||
|
lastSnapshot.rotation = Quaternion.Euler(newRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((change & Changed.Scale) > 0) lastSnapshot.scale = currentSnapshot.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if position, rotation AND scale are unchanged, within given sensitivity range.
|
||||||
|
// Note the sensitivity comparison are different for pos, rot and scale.
|
||||||
|
protected virtual Changed CompareChangedSnapshots(TransformSnapshot currentSnapshot)
|
||||||
|
{
|
||||||
|
Changed change = Changed.None;
|
||||||
|
|
||||||
|
if (syncPosition)
|
||||||
|
{
|
||||||
|
bool positionChanged = Vector3.SqrMagnitude(lastSnapshot.position - currentSnapshot.position) > positionSensitivity * positionSensitivity;
|
||||||
|
if (positionChanged)
|
||||||
|
{
|
||||||
|
if (Mathf.Abs(lastSnapshot.position.x - currentSnapshot.position.x) > positionSensitivity) change |= Changed.PosX;
|
||||||
|
if (Mathf.Abs(lastSnapshot.position.y - currentSnapshot.position.y) > positionSensitivity) change |= Changed.PosY;
|
||||||
|
if (Mathf.Abs(lastSnapshot.position.z - currentSnapshot.position.z) > positionSensitivity) change |= Changed.PosZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncRotation)
|
||||||
|
{
|
||||||
|
if (compressRotation)
|
||||||
|
{
|
||||||
|
bool rotationChanged = Quaternion.Angle(lastSnapshot.rotation, currentSnapshot.rotation) > rotationSensitivity;
|
||||||
|
if (rotationChanged)
|
||||||
|
{
|
||||||
|
// Here we set all Rot enum flags, to tell us if there was a change in rotation
|
||||||
|
// when using compression. If no change, we don't write the compressed Quat.
|
||||||
|
change |= Changed.CompressRot;
|
||||||
|
change |= Changed.Rot;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
change |= Changed.CompressRot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Mathf.Abs(lastSnapshot.rotation.eulerAngles.x - currentSnapshot.rotation.eulerAngles.x) > rotationSensitivity) change |= Changed.RotX;
|
||||||
|
if (Mathf.Abs(lastSnapshot.rotation.eulerAngles.y - currentSnapshot.rotation.eulerAngles.y) > rotationSensitivity) change |= Changed.RotY;
|
||||||
|
if (Mathf.Abs(lastSnapshot.rotation.eulerAngles.z - currentSnapshot.rotation.eulerAngles.z) > rotationSensitivity) change |= Changed.RotZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (syncScale)
|
||||||
|
{
|
||||||
|
if (Vector3.SqrMagnitude(lastSnapshot.scale - currentSnapshot.scale) > scaleSensitivity * scaleSensitivity) change |= Changed.Scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(channel = Channels.Unreliable)]
|
||||||
|
void CmdClientToServerSync(SyncData syncData)
|
||||||
|
{
|
||||||
|
OnClientToServerSync(syncData);
|
||||||
|
//For client authority, immediately pass on the client snapshot to all other
|
||||||
|
//clients instead of waiting for server to send its snapshots.
|
||||||
|
if (syncDirection == SyncDirection.ClientToServer)
|
||||||
|
RpcServerToClientSync(syncData);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnClientToServerSync(SyncData syncData)
|
||||||
|
{
|
||||||
|
// only apply if in client authority mode
|
||||||
|
if (syncDirection != SyncDirection.ClientToServer) return;
|
||||||
|
|
||||||
|
// protect against ever growing buffer size attacks
|
||||||
|
if (serverSnapshots.Count >= connectionToClient.snapshotBufferSizeLimit) return;
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
double timeIntervalCheck = bufferResetMultiplier * sendIntervalMultiplier * NetworkClient.sendInterval;
|
||||||
|
|
||||||
|
if (serverSnapshots.Count > 0 && serverSnapshots.Values[serverSnapshots.Count - 1].remoteTime + timeIntervalCheck < timestamp)
|
||||||
|
ResetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateSyncData(ref syncData, serverSnapshots);
|
||||||
|
|
||||||
|
AddSnapshot(serverSnapshots, connectionToClient.remoteTimeStamp + timeStampAdjustment + offset, syncData.position, syncData.quatRotation, syncData.scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[ClientRpc(channel = Channels.Unreliable)]
|
||||||
|
void RpcServerToClientSync(SyncData syncData) =>
|
||||||
|
OnServerToClientSync(syncData);
|
||||||
|
|
||||||
|
protected virtual void OnServerToClientSync(SyncData syncData)
|
||||||
|
{
|
||||||
|
// in host mode, the server sends rpcs to all clients.
|
||||||
|
// the host client itself will receive them too.
|
||||||
|
// -> host server is always the source of truth
|
||||||
|
// -> we can ignore any rpc on the host client
|
||||||
|
// => otherwise host objects would have ever growing clientBuffers
|
||||||
|
// (rpc goes to clients. if isServer is true too then we are host)
|
||||||
|
if (isServer) return;
|
||||||
|
|
||||||
|
// don't apply for local player with authority
|
||||||
|
if (IsClientWithAuthority) return;
|
||||||
|
|
||||||
|
// on the client, we receive rpcs for all entities.
|
||||||
|
// not all of them have a connectionToServer.
|
||||||
|
// but all of them go through NetworkClient.connection.
|
||||||
|
// we can get the timestamp from there.
|
||||||
|
double timestamp = NetworkClient.connection.remoteTimeStamp;
|
||||||
|
|
||||||
|
if (onlySyncOnChange)
|
||||||
|
{
|
||||||
|
double timeIntervalCheck = bufferResetMultiplier * sendIntervalMultiplier * NetworkServer.sendInterval;
|
||||||
|
|
||||||
|
if (clientSnapshots.Count > 0 && clientSnapshots.Values[clientSnapshots.Count - 1].remoteTime + timeIntervalCheck < timestamp)
|
||||||
|
ResetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateSyncData(ref syncData, clientSnapshots);
|
||||||
|
|
||||||
|
AddSnapshot(clientSnapshots, NetworkClient.connection.remoteTimeStamp + timeStampAdjustment + offset, syncData.position, syncData.quatRotation, syncData.scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateSyncData(ref SyncData syncData, SortedList<double, TransformSnapshot> snapshots)
|
||||||
|
{
|
||||||
|
if (syncData.changedDataByte == Changed.None || syncData.changedDataByte == Changed.CompressRot)
|
||||||
|
{
|
||||||
|
syncData.position = snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].position : GetPosition();
|
||||||
|
syncData.quatRotation = snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].rotation : GetRotation();
|
||||||
|
syncData.scale = snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].scale : GetScale();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Just going to update these without checking if syncposition or not,
|
||||||
|
// because if not syncing position, NT will not apply any position data
|
||||||
|
// to the target during Apply().
|
||||||
|
|
||||||
|
syncData.position.x = (syncData.changedDataByte & Changed.PosX) > 0 ? syncData.position.x : (snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].position.x : GetPosition().x);
|
||||||
|
syncData.position.y = (syncData.changedDataByte & Changed.PosY) > 0 ? syncData.position.y : (snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].position.y : GetPosition().y);
|
||||||
|
syncData.position.z = (syncData.changedDataByte & Changed.PosZ) > 0 ? syncData.position.z : (snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].position.z : GetPosition().z);
|
||||||
|
|
||||||
|
// If compressRot is true, we already have the Quat in syncdata.
|
||||||
|
if ((syncData.changedDataByte & Changed.CompressRot) == 0)
|
||||||
|
{
|
||||||
|
syncData.vecRotation.x = (syncData.changedDataByte & Changed.RotX) > 0 ? syncData.vecRotation.x : (snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].rotation.eulerAngles.x : GetRotation().eulerAngles.x);
|
||||||
|
syncData.vecRotation.y = (syncData.changedDataByte & Changed.RotY) > 0 ? syncData.vecRotation.y : (snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].rotation.eulerAngles.y : GetRotation().eulerAngles.y); ;
|
||||||
|
syncData.vecRotation.z = (syncData.changedDataByte & Changed.RotZ) > 0 ? syncData.vecRotation.z : (snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].rotation.eulerAngles.z : GetRotation().eulerAngles.z);
|
||||||
|
|
||||||
|
syncData.quatRotation = Quaternion.Euler(syncData.vecRotation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
syncData.quatRotation = (syncData.changedDataByte & Changed.Rot) > 0 ? syncData.quatRotation : (snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].rotation : GetRotation());
|
||||||
|
}
|
||||||
|
|
||||||
|
syncData.scale = (syncData.changedDataByte & Changed.Scale) > 0 ? syncData.scale : (snapshots.Count > 0 ? snapshots.Values[snapshots.Count - 1].scale : GetScale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is to extract position/rotation/scale data from payload. Override
|
||||||
|
// Construct and Deconstruct if you are implementing a different SyncData logic.
|
||||||
|
// Note however that snapshot interpolation still requires the basic 3 data
|
||||||
|
// position, rotation and scale, which are computed from here.
|
||||||
|
protected virtual void DeconstructSyncData(System.ArraySegment<byte> receivedPayload, out byte? changedFlagData, out Vector3? position, out Quaternion? rotation, out Vector3? scale)
|
||||||
|
{
|
||||||
|
using (NetworkReaderPooled reader = NetworkReaderPool.Get(receivedPayload))
|
||||||
|
{
|
||||||
|
SyncData syncData = reader.Read<SyncData>();
|
||||||
|
changedFlagData = (byte)syncData.changedDataByte;
|
||||||
|
position = syncData.position;
|
||||||
|
rotation = syncData.quatRotation;
|
||||||
|
scale = syncData.scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
156
Assets/Mirror/Components/NetworkTransform/TransformSyncData.cs
Normal file
156
Assets/Mirror/Components/NetworkTransform/TransformSyncData.cs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace Mirror
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public struct SyncData
|
||||||
|
{
|
||||||
|
public Changed changedDataByte;
|
||||||
|
public Vector3 position;
|
||||||
|
public Quaternion quatRotation;
|
||||||
|
public Vector3 vecRotation;
|
||||||
|
public Vector3 scale;
|
||||||
|
|
||||||
|
public SyncData(Changed _dataChangedByte, Vector3 _position, Quaternion _rotation, Vector3 _scale)
|
||||||
|
{
|
||||||
|
this.changedDataByte = _dataChangedByte;
|
||||||
|
this.position = _position;
|
||||||
|
this.quatRotation = _rotation;
|
||||||
|
this.vecRotation = quatRotation.eulerAngles;
|
||||||
|
this.scale = _scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyncData(Changed _dataChangedByte, TransformSnapshot _snapshot)
|
||||||
|
{
|
||||||
|
this.changedDataByte = _dataChangedByte;
|
||||||
|
this.position = _snapshot.position;
|
||||||
|
this.quatRotation = _snapshot.rotation;
|
||||||
|
this.vecRotation = quatRotation.eulerAngles;
|
||||||
|
this.scale = _snapshot.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyncData(Changed _dataChangedByte, Vector3 _position, Vector3 _vecRotation, Vector3 _scale)
|
||||||
|
{
|
||||||
|
this.changedDataByte = _dataChangedByte;
|
||||||
|
this.position = _position;
|
||||||
|
this.vecRotation = _vecRotation;
|
||||||
|
this.quatRotation = Quaternion.Euler(vecRotation);
|
||||||
|
this.scale = _scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum Changed : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
PosX = 1 << 0,
|
||||||
|
PosY = 1 << 1,
|
||||||
|
PosZ = 1 << 2,
|
||||||
|
CompressRot = 1 << 3,
|
||||||
|
RotX = 1 << 4,
|
||||||
|
RotY = 1 << 5,
|
||||||
|
RotZ = 1 << 6,
|
||||||
|
Scale = 1 << 7,
|
||||||
|
|
||||||
|
Pos = PosX | PosY | PosZ,
|
||||||
|
Rot = RotX | RotY | RotZ
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class SyncDataReaderWriter
|
||||||
|
{
|
||||||
|
public static void WriteSyncData(this NetworkWriter writer, SyncData syncData)
|
||||||
|
{
|
||||||
|
writer.WriteByte((byte)syncData.changedDataByte);
|
||||||
|
|
||||||
|
// Write position
|
||||||
|
if ((syncData.changedDataByte & Changed.PosX) > 0)
|
||||||
|
{
|
||||||
|
writer.WriteFloat(syncData.position.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((syncData.changedDataByte & Changed.PosY) > 0)
|
||||||
|
{
|
||||||
|
writer.WriteFloat(syncData.position.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((syncData.changedDataByte & Changed.PosZ) > 0)
|
||||||
|
{
|
||||||
|
writer.WriteFloat(syncData.position.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write rotation
|
||||||
|
if ((syncData.changedDataByte & Changed.CompressRot) > 0)
|
||||||
|
{
|
||||||
|
if((syncData.changedDataByte & Changed.Rot) > 0)
|
||||||
|
{
|
||||||
|
writer.WriteUInt(Compression.CompressQuaternion(syncData.quatRotation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((syncData.changedDataByte & Changed.RotX) > 0)
|
||||||
|
{
|
||||||
|
writer.WriteFloat(syncData.quatRotation.eulerAngles.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((syncData.changedDataByte & Changed.RotY) > 0)
|
||||||
|
{
|
||||||
|
writer.WriteFloat(syncData.quatRotation.eulerAngles.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((syncData.changedDataByte & Changed.RotZ) > 0)
|
||||||
|
{
|
||||||
|
writer.WriteFloat(syncData.quatRotation.eulerAngles.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write scale
|
||||||
|
if ((syncData.changedDataByte & Changed.Scale) > 0)
|
||||||
|
{
|
||||||
|
writer.WriteVector3(syncData.scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SyncData ReadSyncData(this NetworkReader reader)
|
||||||
|
{
|
||||||
|
Changed changedData = (Changed)reader.ReadByte();
|
||||||
|
|
||||||
|
// If we have nothing to read here, let's say because posX is unchanged, then we can write anything
|
||||||
|
// for now, but in the NT, we will need to check changedData again, to put the right values of the axis
|
||||||
|
// back. We don't have it here.
|
||||||
|
|
||||||
|
Vector3 position =
|
||||||
|
new Vector3(
|
||||||
|
(changedData & Changed.PosX) > 0 ? reader.ReadFloat() : 0,
|
||||||
|
(changedData & Changed.PosY) > 0 ? reader.ReadFloat() : 0,
|
||||||
|
(changedData & Changed.PosZ) > 0 ? reader.ReadFloat() : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector3 vecRotation = new Vector3();
|
||||||
|
Quaternion quatRotation = new Quaternion();
|
||||||
|
|
||||||
|
if ((changedData & Changed.CompressRot) > 0)
|
||||||
|
{
|
||||||
|
quatRotation = (changedData & Changed.RotX) > 0 ? Compression.DecompressQuaternion(reader.ReadUInt()) : new Quaternion();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vecRotation =
|
||||||
|
new Vector3(
|
||||||
|
(changedData & Changed.RotX) > 0 ? reader.ReadFloat() : 0,
|
||||||
|
(changedData & Changed.RotY) > 0 ? reader.ReadFloat() : 0,
|
||||||
|
(changedData & Changed.RotZ) > 0 ? reader.ReadFloat() : 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 scale = (changedData & Changed.Scale) == Changed.Scale ? reader.ReadVector3() : new Vector3();
|
||||||
|
|
||||||
|
SyncData _syncData = (changedData & Changed.CompressRot) > 0 ? new SyncData(changedData, position, quatRotation, scale) : new SyncData(changedData, position, vecRotation, scale);
|
||||||
|
|
||||||
|
return _syncData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a1c0832ca88e749ff96fe04cebb617ef
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
Reference in New Issue
Block a user