@@ -29,10 +29,13 @@ internal struct NetworkTransformState : INetworkSerializable
29
29
private const int k_ScaleXBit = 7 ;
30
30
private const int k_ScaleYBit = 8 ;
31
31
private const int k_ScaleZBit = 9 ;
32
+ private const int k_TeleportingBit = 10 ;
32
33
33
- // 10 -15: <unused>
34
+ // 11 -15: <unused>
34
35
private ushort m_Bitset ;
35
36
37
+
38
+
36
39
public bool InLocalSpace
37
40
{
38
41
get => ( m_Bitset & ( 1 << k_InLocalSpaceBit ) ) != 0 ;
@@ -136,6 +139,16 @@ public bool HasScaleZ
136
139
}
137
140
}
138
141
142
+ public bool IsTeleportingNextFrame
143
+ {
144
+ get => ( m_Bitset & ( 1 << k_TeleportingBit ) ) != 0 ;
145
+ set
146
+ {
147
+ if ( value ) { m_Bitset = ( ushort ) ( m_Bitset | ( 1 << k_TeleportingBit ) ) ; }
148
+ else { m_Bitset = ( ushort ) ( m_Bitset & ~ ( 1 << k_TeleportingBit ) ) ; }
149
+ }
150
+ }
151
+
139
152
public float PositionX , PositionY , PositionZ ;
140
153
public float RotAngleX , RotAngleY , RotAngleZ ;
141
154
public float ScaleX , ScaleY , ScaleZ ;
@@ -277,6 +290,10 @@ public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReade
277
290
278
291
private Transform m_Transform ; // cache the transform component to reduce unnecessary bounce between managed and native
279
292
private int m_LastSentTick ;
293
+ private NetworkTransformState m_LastSentState ;
294
+
295
+ private const string k_NoAuthorityMessage = "A local change to {dirtyField} without authority detected, reverting back to latest interpolated network state!" ;
296
+
280
297
281
298
/// <summary>
282
299
/// Tries updating the server authoritative transform, only if allowed.
@@ -288,17 +305,28 @@ public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReade
288
305
protected void TryCommitTransformToServer ( Transform transformToCommit , double dirtyTime )
289
306
{
290
307
var isDirty = ApplyTransformToNetworkState ( ref m_LocalAuthoritativeNetworkState , dirtyTime , transformToCommit ) ;
308
+ TryCommit ( isDirty ) ;
309
+ }
291
310
292
- void Send ( )
311
+ private void TryCommitValuesToServer ( Vector3 position , Vector3 rotation , Vector3 scale , double dirtyTime )
312
+ {
313
+ var isDirty = ApplyTransformToNetworkStateWithInfo ( ref m_LocalAuthoritativeNetworkState , dirtyTime , position , rotation , scale ) ;
314
+
315
+ TryCommit ( isDirty . isDirty ) ;
316
+ }
317
+
318
+ private void TryCommit ( bool isDirty )
319
+ {
320
+ void Send ( NetworkTransformState stateToSend )
293
321
{
294
322
if ( IsServer )
295
323
{
296
324
// server RPC takes a few frames to execute server side, we want this to execute immediately
297
- CommitLocallyAndReplicate ( m_LocalAuthoritativeNetworkState ) ;
325
+ CommitLocallyAndReplicate ( stateToSend ) ;
298
326
}
299
327
else
300
328
{
301
- CommitTransformServerRpc ( m_LocalAuthoritativeNetworkState ) ;
329
+ CommitTransformServerRpc ( stateToSend ) ;
302
330
}
303
331
}
304
332
@@ -311,14 +339,15 @@ void Send()
311
339
// making it immobile.
312
340
if ( isDirty )
313
341
{
314
- Send ( ) ;
342
+ Send ( m_LocalAuthoritativeNetworkState ) ;
315
343
m_HasSentLastValue = false ;
316
344
m_LastSentTick = NetworkManager . LocalTime . Tick ;
345
+ m_LastSentState = m_LocalAuthoritativeNetworkState ;
317
346
}
318
347
else if ( ! m_HasSentLastValue && NetworkManager . LocalTime . Tick >= m_LastSentTick + 1 ) // check for state.IsDirty since update can happen more than once per tick. No need for client, RPCs will just queue up
319
348
{
320
- m_LocalAuthoritativeNetworkState . SentTime = NetworkManager . LocalTime . Time ; // time 1+ tick later
321
- Send ( ) ;
349
+ m_LastSentState . SentTime = NetworkManager . LocalTime . Time ; // time 1+ tick later
350
+ Send ( m_LastSentState ) ;
322
351
m_HasSentLastValue = true ;
323
352
}
324
353
}
@@ -334,7 +363,6 @@ private void CommitTransformServerRpc(NetworkTransformState networkState, Server
334
363
335
364
private void CommitLocallyAndReplicate ( NetworkTransformState networkState )
336
365
{
337
- m_LocalAuthoritativeNetworkState = networkState ;
338
366
m_ReplicatedNetworkState . Value = networkState ;
339
367
AddInterpolatedState ( networkState ) ;
340
368
}
@@ -364,7 +392,11 @@ internal bool ApplyTransformToNetworkState(ref NetworkTransformState networkStat
364
392
var position = InLocalSpace ? transformToUse . localPosition : transformToUse . position ;
365
393
var rotAngles = InLocalSpace ? transformToUse . localEulerAngles : transformToUse . eulerAngles ;
366
394
var scale = InLocalSpace ? transformToUse . localScale : transformToUse . lossyScale ;
395
+ return ApplyTransformToNetworkStateWithInfo ( ref networkState , dirtyTime , position , rotAngles , scale ) ;
396
+ }
367
397
398
+ private ( bool isDirty , bool isPositionDirty , bool isRotationDirty , bool isScaleDirty ) ApplyTransformToNetworkStateWithInfo ( ref NetworkTransformState networkState , double dirtyTime , Vector3 position , Vector3 rotAngles , Vector3 scale )
399
+ {
368
400
var isDirty = false ;
369
401
var isPositionDirty = false ;
370
402
var isRotationDirty = false ;
@@ -484,17 +516,17 @@ private void ApplyInterpolatedNetworkStateToTransform(NetworkTransformState netw
484
516
// Position Read
485
517
if ( SyncPositionX )
486
518
{
487
- interpolatedPosition . x = Interpolate ? m_PositionXInterpolator . GetInterpolatedValue ( ) : networkState . Position . x ;
519
+ interpolatedPosition . x = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Position . x : m_PositionXInterpolator . GetInterpolatedValue ( ) ;
488
520
}
489
521
490
522
if ( SyncPositionY )
491
523
{
492
- interpolatedPosition . y = Interpolate ? m_PositionYInterpolator . GetInterpolatedValue ( ) : networkState . Position . y ;
524
+ interpolatedPosition . y = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Position . y : m_PositionYInterpolator . GetInterpolatedValue ( ) ;
493
525
}
494
526
495
527
if ( SyncPositionZ )
496
528
{
497
- interpolatedPosition . z = Interpolate ? m_PositionZInterpolator . GetInterpolatedValue ( ) : networkState . Position . z ;
529
+ interpolatedPosition . z = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Position . z : m_PositionZInterpolator . GetInterpolatedValue ( ) ;
498
530
}
499
531
500
532
// again, we should be using quats here
@@ -503,34 +535,34 @@ private void ApplyInterpolatedNetworkStateToTransform(NetworkTransformState netw
503
535
var eulerAngles = m_RotationInterpolator . GetInterpolatedValue ( ) . eulerAngles ;
504
536
if ( SyncRotAngleX )
505
537
{
506
- interpolatedRotAngles . x = Interpolate ? eulerAngles . x : networkState . Rotation . x ;
538
+ interpolatedRotAngles . x = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Rotation . x : eulerAngles . x ;
507
539
}
508
540
509
541
if ( SyncRotAngleY )
510
542
{
511
- interpolatedRotAngles . y = Interpolate ? eulerAngles . y : networkState . Rotation . y ;
543
+ interpolatedRotAngles . y = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Rotation . y : eulerAngles . y ;
512
544
}
513
545
514
546
if ( SyncRotAngleZ )
515
547
{
516
- interpolatedRotAngles . z = Interpolate ? eulerAngles . z : networkState . Rotation . z ;
548
+ interpolatedRotAngles . z = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Rotation . z : eulerAngles . z ;
517
549
}
518
550
}
519
551
520
552
// Scale Read
521
553
if ( SyncScaleX )
522
554
{
523
- interpolatedScale . x = Interpolate ? m_ScaleXInterpolator . GetInterpolatedValue ( ) : networkState . Scale . x ;
555
+ interpolatedScale . x = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Scale . x : m_ScaleXInterpolator . GetInterpolatedValue ( ) ;
524
556
}
525
557
526
558
if ( SyncScaleY )
527
559
{
528
- interpolatedScale . y = Interpolate ? m_ScaleYInterpolator . GetInterpolatedValue ( ) : networkState . Scale . y ;
560
+ interpolatedScale . y = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Scale . y : m_ScaleYInterpolator . GetInterpolatedValue ( ) ;
529
561
}
530
562
531
563
if ( SyncScaleZ )
532
564
{
533
- interpolatedScale . z = Interpolate ? m_ScaleZInterpolator . GetInterpolatedValue ( ) : networkState . Scale . z ;
565
+ interpolatedScale . z = networkState . IsTeleportingNextFrame || ! Interpolate ? networkState . Scale . z : m_ScaleZInterpolator . GetInterpolatedValue ( ) ;
534
566
}
535
567
536
568
// Position Apply
@@ -722,9 +754,11 @@ private void OnDestroy()
722
754
/// </summary>
723
755
/// <param name="posIn"></param> new position to move to. Can be null
724
756
/// <param name="rotIn"></param> new rotation to rotate to. Can be null
725
- /// <param name="scaleIn"></param> new scale to scale to. Can be null
757
+ /// <param name="scaleIn">new scale to scale to. Can be null</param>
758
+ /// <param name="shouldGhostsInterpolate">Should other clients interpolate this change or not. True by default</param>
759
+ /// new scale to scale to. Can be null
726
760
/// <exception cref="Exception"></exception>
727
- public void SetState ( Vector3 ? posIn = null , Quaternion ? rotIn = null , Vector3 ? scaleIn = null )
761
+ public void SetState ( Vector3 ? posIn = null , Quaternion ? rotIn = null , Vector3 ? scaleIn = null , bool shouldGhostsInterpolate = true )
728
762
{
729
763
if ( ! IsOwner )
730
764
{
@@ -744,29 +778,31 @@ public void SetState(Vector3? posIn = null, Quaternion? rotIn = null, Vector3? s
744
778
{
745
779
if ( ! IsServer )
746
780
{
747
- SetStateServerRpc ( pos , rot , scale ) ;
781
+ SetStateServerRpc ( pos , rot , scale , shouldGhostsInterpolate ) ;
748
782
}
749
783
}
750
784
else
751
785
{
752
- transform . position = pos ;
753
- transform . rotation = rot ;
754
- transform . localScale = scale ;
786
+ m_Transform . position = pos ;
787
+ m_Transform . rotation = rot ;
788
+ m_Transform . localScale = scale ;
789
+ m_LocalAuthoritativeNetworkState . IsTeleportingNextFrame = shouldGhostsInterpolate ;
755
790
}
756
791
}
757
792
758
793
[ ServerRpc ]
759
- private void SetStateServerRpc ( Vector3 pos , Quaternion rot , Vector3 scale )
794
+ private void SetStateServerRpc ( Vector3 pos , Quaternion rot , Vector3 scale , bool shouldTeleport )
760
795
{
761
796
// server has received this RPC request to move change transform. Give the server a chance to modify or
762
797
// even reject the move
763
798
if ( OnClientRequestChange != null )
764
799
{
765
800
( pos , rot , scale ) = OnClientRequestChange ( pos , rot , scale ) ;
766
801
}
767
- transform . position = pos ;
768
- transform . rotation = rot ;
769
- transform . localScale = scale ;
802
+ m_Transform . position = pos ;
803
+ m_Transform . rotation = rot ;
804
+ m_Transform . localScale = scale ;
805
+ m_LocalAuthoritativeNetworkState . IsTeleportingNextFrame = shouldTeleport ;
770
806
}
771
807
#endregion
772
808
@@ -818,23 +854,39 @@ protected virtual void Update()
818
854
// ignoring rotation dirty since quaternions will mess with euler angles, making this impossible to determine if the change to a single axis comes
819
855
// from an unauthorized transform change or euler to quaternion conversion artifacts.
820
856
var dirtyField = oldStateDirtyInfo . isPositionDirty ? "position" : oldStateDirtyInfo . isRotationDirty ? "rotation" : "scale" ;
821
- Debug . LogWarning ( $ "A local change to { dirtyField } without authority detected, reverting back to latest interpolated network state!" , this ) ;
857
+ Debug . LogWarning ( dirtyField + k_NoAuthorityMessage , this ) ;
822
858
}
823
859
824
860
// Apply updated interpolated value
825
861
ApplyInterpolatedNetworkStateToTransform ( m_ReplicatedNetworkState . Value , m_Transform ) ;
826
862
}
827
863
}
864
+
865
+ m_LocalAuthoritativeNetworkState . IsTeleportingNextFrame = false ;
828
866
}
829
867
830
868
/// <summary>
831
869
/// Teleports the transform to the given values without interpolating
832
870
/// </summary>
833
871
public void Teleport ( Vector3 newPosition , Quaternion newRotation , Vector3 newScale )
834
872
{
873
+ if ( ! CanCommitToTransform )
874
+ {
875
+ throw new Exception ( "Teleport not allowed, " + k_NoAuthorityMessage ) ;
876
+ }
877
+
878
+ var newRotationEuler = newRotation . eulerAngles ;
879
+ var stateToSend = m_LocalAuthoritativeNetworkState ;
880
+ stateToSend . IsTeleportingNextFrame = true ;
881
+ stateToSend . Position = newPosition ;
882
+ stateToSend . Rotation = newRotationEuler ;
883
+ stateToSend . Scale = newScale ;
884
+ ApplyInterpolatedNetworkStateToTransform ( stateToSend , transform ) ;
885
+ // set teleport flag in state to signal to ghosts not to interpolate
886
+ m_LocalAuthoritativeNetworkState . IsTeleportingNextFrame = true ;
835
887
// check server side
836
- // set teleport flag in state
837
- throw new NotImplementedException ( ) ; // TODO MTT-769
888
+ TryCommitValuesToServer ( newPosition , newRotationEuler , newScale , NetworkManager . LocalTime . Time ) ;
889
+ m_LocalAuthoritativeNetworkState . IsTeleportingNextFrame = false ;
838
890
}
839
891
}
840
892
}
0 commit comments