@@ -16,35 +16,47 @@ namespace Unity.Multiplayer.Samples.BossRoom
16
16
[ RequireComponent ( typeof ( NetworkObject ) ) ]
17
17
public class NetworkStats : NetworkBehaviour
18
18
{
19
+ // For a value like RTT an exponential moving average is a better indication of the current rtt and fluctuates less.
20
+ struct ExponentialMovingAverageCalculator
21
+ {
22
+ readonly float m_Alpha ;
23
+ float m_Average ;
24
+
25
+ public float Average => m_Average ;
26
+
27
+ public ExponentialMovingAverageCalculator ( float average )
28
+ {
29
+ m_Alpha = 2f / ( k_MaxWindowSize + 1 ) ;
30
+ m_Average = average ;
31
+ }
32
+
33
+ public float NextValue ( float value ) => m_Average = ( value - m_Average ) * m_Alpha + m_Average ;
34
+ }
35
+
19
36
// RTT
20
37
// Client sends a ping RPC to the server and starts it's timer.
21
38
// The server receives the ping and sends a pong response to the client.
22
39
// The client receives that pong response and stops its time.
23
40
// The RPC value is using a moving average, so we don't have a value that moves too much, but is still reactive to RTT changes.
24
- //
25
- // Note: when adding more stats, it might be worth it to abstract these in their own classes instead of having a bunch
26
- // of attributes floating around.
27
41
28
- public float LastRTT { get ; private set ; }
42
+ const int k_MaxWindowSizeSeconds = 3 ; // it should take x seconds for the value to react to change
43
+ const float k_PingIntervalSeconds = 0.1f ;
44
+ const float k_MaxWindowSize = k_MaxWindowSizeSeconds / k_PingIntervalSeconds ;
45
+
46
+ ExponentialMovingAverageCalculator m_BossRoomRTT = new ExponentialMovingAverageCalculator ( 0 ) ;
47
+ ExponentialMovingAverageCalculator m_UtpRTT = new ExponentialMovingAverageCalculator ( 0 ) ;
29
48
30
- [ SerializeField ]
31
- [ Tooltip ( "The interval to send ping RPCs to calculate the RTT. The bigger the number, the less reactive the stat will be to RTT changes" ) ]
32
- float m_PingIntervalSeconds = 0.1f ;
33
49
float m_LastPingTime ;
34
50
Text m_TextStat ;
35
51
Text m_TextHostType ;
36
52
37
53
// When receiving pong client RPCs, we need to know when the initiating ping sent it so we can calculate its individual RTT
38
54
int m_CurrentRTTPingId ;
39
55
40
- Queue < float > m_MovingWindow = new Queue < float > ( ) ;
41
- const int k_MaxWindowSizeSeconds = 3 ; // it should take x seconds for the value to react to change
42
- float m_MaxWindowSize => k_MaxWindowSizeSeconds / m_PingIntervalSeconds ;
43
56
Dictionary < int , float > m_PingHistoryStartTimes = new Dictionary < int , float > ( ) ;
44
57
45
58
ClientRpcParams m_PongClientParams ;
46
59
47
-
48
60
public override void OnNetworkSpawn ( )
49
61
{
50
62
bool isClientOnly = IsClient && ! IsServer ;
@@ -53,10 +65,12 @@ public override void OnNetworkSpawn()
53
65
Destroy ( this ) ;
54
66
return ;
55
67
}
68
+
56
69
if ( IsOwner )
57
70
{
58
71
CreateNetworkStatsText ( ) ;
59
72
}
73
+
60
74
m_PongClientParams = new ClientRpcParams ( ) { Send = new ClientRpcSendParams ( ) { TargetClientIds = new [ ] { OwnerClientId } } } ;
61
75
}
62
76
@@ -71,7 +85,7 @@ void CreateNetworkStatsText()
71
85
InitializeTextLine ( "No Stat" , out m_TextStat ) ;
72
86
}
73
87
74
- private void InitializeTextLine ( string defaultText , out Text textComponent )
88
+ void InitializeTextLine ( string defaultText , out Text textComponent )
75
89
{
76
90
GameObject rootGO = new GameObject ( "UI Stat Text" ) ;
77
91
textComponent = rootGO . AddComponent < Text > ( ) ;
@@ -91,19 +105,21 @@ void FixedUpdate()
91
105
var textToDisplay = string . Empty ;
92
106
if ( ! IsServer )
93
107
{
94
- if ( Time . realtimeSinceStartup - m_LastPingTime > m_PingIntervalSeconds )
108
+ if ( Time . realtimeSinceStartup - m_LastPingTime > k_PingIntervalSeconds )
95
109
{
96
110
// We could have had a ping/pong where the ping sends the pong and the pong sends the ping. Issue with this
97
111
// is the higher the latency, the lower the sampling would be. We need pings to be sent at a regular interval
98
112
PingServerRPC ( m_CurrentRTTPingId ) ;
99
113
m_PingHistoryStartTimes [ m_CurrentRTTPingId ] = Time . realtimeSinceStartup ;
100
114
m_CurrentRTTPingId ++ ;
101
115
m_LastPingTime = Time . realtimeSinceStartup ;
116
+
117
+ m_UtpRTT . NextValue ( NetworkManager . NetworkConfig . NetworkTransport . GetCurrentRtt ( NetworkManager . ServerClientId ) ) ;
102
118
}
103
119
104
120
if ( m_TextStat != null )
105
121
{
106
- textToDisplay = $ "{ textToDisplay } RTT: { ( LastRTT * 1000f ) . ToString ( ) } ms ";
122
+ textToDisplay = $ "{ textToDisplay } RTT: { ( m_BossRoomRTT . Average * 1000 ) . ToString ( "0" ) } ms; \n UTP RTT { m_UtpRTT . Average . ToString ( "0" ) } ms ";
107
123
}
108
124
}
109
125
@@ -118,36 +134,18 @@ void FixedUpdate()
118
134
}
119
135
}
120
136
121
-
122
137
[ ServerRpc ]
123
- public void PingServerRPC ( int pingId , ServerRpcParams serverParams = default )
138
+ void PingServerRPC ( int pingId , ServerRpcParams serverParams = default )
124
139
{
125
140
PongClientRPC ( pingId , m_PongClientParams ) ;
126
141
}
127
142
128
143
[ ClientRpc ]
129
- public void PongClientRPC ( int pingId , ClientRpcParams clientParams = default )
144
+ void PongClientRPC ( int pingId , ClientRpcParams clientParams = default )
130
145
{
131
146
var startTime = m_PingHistoryStartTimes [ pingId ] ;
132
147
m_PingHistoryStartTimes . Remove ( pingId ) ;
133
- m_MovingWindow . Enqueue ( Time . realtimeSinceStartup - startTime ) ;
134
- UpdateRTTSlidingWindowAverage ( ) ;
135
- }
136
-
137
- void UpdateRTTSlidingWindowAverage ( )
138
- {
139
- if ( m_MovingWindow . Count > m_MaxWindowSize )
140
- {
141
- m_MovingWindow . Dequeue ( ) ;
142
- }
143
-
144
- float rttSum = 0 ;
145
- foreach ( var singleRTT in m_MovingWindow )
146
- {
147
- rttSum += singleRTT ;
148
- }
149
-
150
- LastRTT = rttSum / m_MaxWindowSize ;
148
+ m_BossRoomRTT . NextValue ( Time . realtimeSinceStartup - startTime ) ;
151
149
}
152
150
153
151
public override void OnNetworkDespawn ( )
@@ -156,6 +154,7 @@ public override void OnNetworkDespawn()
156
154
{
157
155
Destroy ( m_TextStat . gameObject ) ;
158
156
}
157
+
159
158
if ( m_TextHostType != null )
160
159
{
161
160
Destroy ( m_TextHostType . gameObject ) ;
0 commit comments