Skip to content

Commit 29b1616

Browse files
Merge pull request #419 from Unity-Technologies/feature/client-outer-loop
refactor: move client iteration to outermost loop
2 parents 35935e8 + 359462a commit 29b1616

4 files changed

Lines changed: 166 additions & 127 deletions

File tree

com.unity.multiplayer.mlapi/Runtime/Core/NetworkedBehaviour.cs

Lines changed: 159 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ internal void EndSendClientRpc(BitWriter writer, ClientRpcSendParams sendParams,
174174
/// <summary>
175175
/// Gets if we are executing as server
176176
/// </summary>
177-
protected bool IsServer => IsRunning && NetworkingManager.Singleton.IsServer;
177+
protected static bool IsServer => IsRunning && NetworkingManager.Singleton.IsServer;
178178
/// <summary>
179179
/// Gets if we are executing as client
180180
/// </summary>
@@ -195,7 +195,7 @@ internal void EndSendClientRpc(BitWriter writer, ClientRpcSendParams sendParams,
195195
/// Gets if we are executing as Host, I.E Server and Client
196196
/// </summary>
197197
protected bool IsHost => IsRunning && NetworkingManager.Singleton.IsHost;
198-
private bool IsRunning => NetworkingManager.Singleton != null && NetworkingManager.Singleton.IsListening;
198+
private static bool IsRunning => NetworkingManager.Singleton != null && NetworkingManager.Singleton.IsListening;
199199
/// <summary>
200200
/// Gets Whether or not the object has a owner
201201
/// </summary>
@@ -330,6 +330,7 @@ protected NetworkedBehaviour GetBehaviour(ushort id)
330330
private readonly List<string> channelsForNetworkedVarGroups = new List<string>();
331331
internal readonly List<INetworkedVar> networkedVarFields = new List<INetworkedVar>();
332332

333+
private static HashSet<MLAPI.NetworkedObject> touched = new HashSet<MLAPI.NetworkedObject>();
333334
private static readonly Dictionary<Type, FieldInfo[]> fieldTypes = new Dictionary<Type, FieldInfo[]>();
334335

335336
private static FieldInfo[] GetFieldInfoForType(Type type)
@@ -415,122 +416,194 @@ internal void InitializeVars()
415416
}
416417
}
417418

418-
internal void VarUpdate()
419+
#if DEVELOPMENT_BUILD || UNITY_EDITOR
420+
public static ProfilerMarker s_NetworkedBehaviourUpdate = new ProfilerMarker("MLAPI.NetworkedObject.NetworkedBehaviourUpdate");
421+
#endif
422+
423+
internal static void NetworkedBehaviourUpdate()
424+
{
425+
#if DEVELOPMENT_BUILD || UNITY_EDITOR
426+
s_NetworkedBehaviourUpdate.Begin();
427+
#endif
428+
try
429+
{
430+
if (IsServer)
431+
{
432+
touched.Clear();
433+
for (int i = 0; i < NetworkingManager.Singleton.ConnectedClientsList.Count; i++)
434+
{
435+
var client = NetworkingManager.Singleton.ConnectedClientsList[i];
436+
var spawnedObjs = SpawnManager.SpawnedObjectsList;
437+
touched.UnionWith(spawnedObjs);
438+
foreach (var sobj in spawnedObjs)
439+
{
440+
// Sync just the variables for just the objects this client sees
441+
for (int k = 0; k < sobj.childNetworkedBehaviours.Count; k++)
442+
{
443+
sobj.childNetworkedBehaviours[k].VarUpdate(client.ClientId);
444+
}
445+
}
446+
}
447+
448+
// Now, reset all the no-longer-dirty variables
449+
foreach (var sobj in touched)
450+
{
451+
for (int k = 0; k < sobj.childNetworkedBehaviours.Count; k++)
452+
{
453+
sobj.childNetworkedBehaviours[k].PostNetworkVariableWrite();
454+
}
455+
}
456+
}
457+
else
458+
{
459+
460+
// when client updates the sever, it tells it about all its objects
461+
foreach (var sobj in SpawnManager.SpawnedObjectsList)
462+
{
463+
for (int k = 0; k < sobj.childNetworkedBehaviours.Count; k++)
464+
{
465+
sobj.childNetworkedBehaviours[k].VarUpdate(NetworkingManager.Singleton.ServerClientId);
466+
}
467+
}
468+
469+
// Now, reset all the no-longer-dirty variables
470+
foreach (var sobj in SpawnManager.SpawnedObjectsList)
471+
{
472+
for (int k = 0; k < sobj.childNetworkedBehaviours.Count; k++)
473+
{
474+
sobj.childNetworkedBehaviours[k].PostNetworkVariableWrite();
475+
}
476+
}
477+
}
478+
479+
}
480+
finally
481+
{
482+
#if DEVELOPMENT_BUILD || UNITY_EDITOR
483+
s_NetworkedBehaviourUpdate.End();
484+
#endif
485+
}
486+
}
487+
488+
489+
internal void PreNetworkVariableWrite()
490+
{
491+
// reset our "which variables got written" data
492+
networkedVarIndexesToReset.Clear();
493+
networkedVarIndexesToResetSet.Clear();
494+
}
495+
496+
internal void PostNetworkVariableWrite()
497+
{
498+
// mark any variables we wrote as no longer dirty
499+
for (int i = 0; i < networkedVarIndexesToReset.Count; i++)
500+
{
501+
networkedVarFields[networkedVarIndexesToReset[i]].ResetDirty();
502+
}
503+
}
504+
505+
internal void VarUpdate(ulong clientId)
419506
{
420507
if (!varInit)
421508
InitializeVars();
422509

423-
NetworkedVarUpdate();
510+
PreNetworkVariableWrite();
511+
NetworkedVarUpdate(clientId);
424512
}
425513

426514
private readonly List<int> networkedVarIndexesToReset = new List<int>();
427515
private readonly HashSet<int> networkedVarIndexesToResetSet = new HashSet<int>();
428516

429-
private void NetworkedVarUpdate()
517+
private void NetworkedVarUpdate(ulong clientId)
430518
{
431-
if (!CouldHaveDirtyNetworkedVars())
432-
return;
519+
if (!CouldHaveDirtyNetworkedVars())
520+
return;
433521

434-
networkedVarIndexesToReset.Clear();
435-
networkedVarIndexesToResetSet.Clear();
436-
437-
for (int i = 0; i < (IsServer ? NetworkingManager.Singleton.ConnectedClientsList.Count : 1); i++)
522+
for (int j = 0; j < channelMappedNetworkedVarIndexes.Count; j++)
438523
{
439-
// Do this check here to prevent doing all the expensive dirty checks
440-
if (!IsServer || this.NetworkedObject.observers.Contains(NetworkingManager.Singleton.ConnectedClientsList[i].ClientId))
524+
using (PooledBitStream stream = PooledBitStream.Get())
441525
{
442-
// This iterates over every "channel group".
443-
for (int j = 0; j < channelMappedNetworkedVarIndexes.Count; j++)
526+
using (PooledBitWriter writer = PooledBitWriter.Get(stream))
444527
{
445-
using (PooledBitStream stream = PooledBitStream.Get())
528+
writer.WriteUInt64Packed(NetworkId);
529+
writer.WriteUInt16Packed(NetworkedObject.GetOrderIndex(this));
530+
531+
bool writtenAny = false;
532+
for (int k = 0; k < networkedVarFields.Count; k++)
446533
{
447-
using (PooledBitWriter writer = PooledBitWriter.Get(stream))
534+
if (!channelMappedNetworkedVarIndexes[j].Contains(k))
448535
{
449-
writer.WriteUInt64Packed(NetworkId);
450-
writer.WriteUInt16Packed(NetworkedObject.GetOrderIndex(this));
536+
// This var does not belong to the currently iterating channel group.
537+
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
538+
{
539+
writer.WriteUInt16Packed(0);
540+
}
541+
else
542+
{
543+
writer.WriteBool(false);
544+
}
545+
continue;
546+
}
451547

452-
ulong clientId = IsServer ? NetworkingManager.Singleton.ConnectedClientsList[i].ClientId : NetworkingManager.Singleton.ServerClientId;
453-
bool writtenAny = false;
454-
for (int k = 0; k < networkedVarFields.Count; k++)
548+
bool isDirty = networkedVarFields[k].IsDirty(); // cache this here. You never know what operations users will do in the dirty methods
549+
550+
// if I'm dirty AND a client, write (server always has all permissions)
551+
// if I'm dirty AND the server AND the client can read me, send.
552+
bool shouldWrite = isDirty && (!IsServer || networkedVarFields[k].CanClientRead(clientId));
553+
554+
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
555+
{
556+
if (!shouldWrite)
455557
{
456-
if (!channelMappedNetworkedVarIndexes[j].Contains(k))
457-
{
458-
// This var does not belong to the currently iterating channel group.
459-
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
460-
{
461-
writer.WriteUInt16Packed(0);
462-
}
463-
else
464-
{
465-
writer.WriteBool(false);
466-
}
467-
continue;
468-
}
558+
writer.WriteUInt16Packed(0);
559+
}
560+
}
561+
else
562+
{
563+
writer.WriteBool(shouldWrite);
564+
}
469565

470-
bool isDirty = networkedVarFields[k].IsDirty(); // cache this here. You never know what operations users will do in the dirty methods
471-
bool shouldWrite = isDirty && (!IsServer || networkedVarFields[k].CanClientRead(clientId));
566+
if (shouldWrite)
567+
{
568+
writtenAny = true;
472569

473-
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
474-
{
475-
if (!shouldWrite)
476-
{
477-
writer.WriteUInt16Packed(0);
478-
}
479-
}
480-
else
570+
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
571+
{
572+
using (PooledBitStream varStream = PooledBitStream.Get())
481573
{
482-
writer.WriteBool(shouldWrite);
483-
}
574+
networkedVarFields[k].WriteDelta(varStream);
575+
varStream.PadStream();
484576

485-
if (shouldWrite)
486-
{
487-
writtenAny = true;
488-
489-
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
490-
{
491-
using (PooledBitStream varStream = PooledBitStream.Get())
492-
{
493-
networkedVarFields[k].WriteDelta(varStream);
494-
varStream.PadStream();
495-
496-
writer.WriteUInt16Packed((ushort)varStream.Length);
497-
stream.CopyFrom(varStream);
498-
}
499-
}
500-
else
501-
{
502-
networkedVarFields[k].WriteDelta(stream);
503-
}
504-
505-
if (!networkedVarIndexesToResetSet.Contains(k))
506-
{
507-
networkedVarIndexesToResetSet.Add(k);
508-
networkedVarIndexesToReset.Add(k);
509-
}
577+
writer.WriteUInt16Packed((ushort)varStream.Length);
578+
stream.CopyFrom(varStream);
510579
}
511580
}
581+
else
582+
{
583+
networkedVarFields[k].WriteDelta(stream);
584+
}
512585

513-
if (writtenAny)
586+
if (!networkedVarIndexesToResetSet.Contains(k))
514587
{
515-
if (IsServer)
516-
InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_NETWORKED_VAR_DELTA, channelsForNetworkedVarGroups[j], stream, SecuritySendFlags.None);
517-
else
518-
InternalMessageSender.Send(NetworkingManager.Singleton.ServerClientId, MLAPIConstants.MLAPI_NETWORKED_VAR_DELTA, channelsForNetworkedVarGroups[j], stream, SecuritySendFlags.None);
588+
networkedVarIndexesToResetSet.Add(k);
589+
networkedVarIndexesToReset.Add(k);
519590
}
520591
}
521592
}
593+
594+
if (writtenAny)
595+
{
596+
InternalMessageSender.Send(clientId,
597+
MLAPIConstants.MLAPI_NETWORKED_VAR_DELTA,
598+
channelsForNetworkedVarGroups[j], stream, SecuritySendFlags.None);
599+
}
522600
}
523601
}
524602
}
525-
526-
for (int i = 0; i < networkedVarIndexesToReset.Count; i++)
527-
{
528-
networkedVarFields[networkedVarIndexesToReset[i]].ResetDirty();
529-
}
530603
}
531-
532604
private bool CouldHaveDirtyNetworkedVars()
533605
{
606+
// TODO: There should be a better way by reading one dirty variable vs. 'n'
534607
for (int i = 0; i < networkedVarFields.Count; i++)
535608
{
536609
if (networkedVarFields[i].IsDirty())
@@ -561,7 +634,7 @@ internal static void HandleNetworkedVarDeltas(List<INetworkedVar> networkedVarLi
561634
continue;
562635
}
563636

564-
if (NetworkingManager.Singleton.IsServer && !networkedVarList[i].CanClientWrite(clientId))
637+
if (IsServer && !networkedVarList[i].CanClientWrite(clientId))
565638
{
566639
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
567640
{
@@ -586,7 +659,7 @@ internal static void HandleNetworkedVarDeltas(List<INetworkedVar> networkedVarLi
586659

587660
long readStartPos = stream.Position;
588661

589-
networkedVarList[i].ReadDelta(stream, NetworkingManager.Singleton.IsServer);
662+
networkedVarList[i].ReadDelta(stream, IsServer);
590663
ProfilerStatManager.networkVarsRcvd.Record();
591664

592665
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
@@ -629,7 +702,7 @@ internal static void HandleNetworkedVarUpdate(List<INetworkedVar> networkedVarLi
629702
continue;
630703
}
631704

632-
if (NetworkingManager.Singleton.IsServer && !networkedVarList[i].CanClientWrite(clientId))
705+
if (IsServer && !networkedVarList[i].CanClientWrite(clientId))
633706
{
634707
if (NetworkingManager.Singleton.NetworkConfig.EnsureNetworkedVarLengthSafety)
635708
{

com.unity.multiplayer.mlapi/Runtime/Core/NetworkedObject.cs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ namespace MLAPI
2121
[AddComponentMenu("MLAPI/NetworkedObject", -99)]
2222
public sealed class NetworkedObject : MonoBehaviour
2323
{
24-
#if DEVELOPMENT_BUILD || UNITY_EDITOR
25-
public static ProfilerMarker s_NetworkedBehaviourUpdate = new ProfilerMarker("MLAPI.NetworkedObject.NetworkedBehaviourUpdate");
26-
#endif
27-
2824
private void OnValidate()
2925
{
3026
// Set this so the hash can be serialized on Scene objects. For prefabs, they are generated at runtime.
@@ -540,36 +536,6 @@ internal List<NetworkedBehaviour> childNetworkedBehaviours
540536
}
541537
}
542538

543-
internal static void NetworkedBehaviourUpdate()
544-
{
545-
#if DEVELOPMENT_BUILD || UNITY_EDITOR
546-
s_NetworkedBehaviourUpdate.Begin();
547-
#endif
548-
549-
try
550-
{
551-
if (SpawnManager.SpawnedObjectsList.Count == 0)
552-
return;
553-
554-
foreach (var sobj in SpawnManager.SpawnedObjectsList)
555-
{
556-
// Sync all vars
557-
for (int j = 0;
558-
j < sobj.childNetworkedBehaviours.Count;
559-
j++)
560-
{
561-
sobj.childNetworkedBehaviours[j].VarUpdate();
562-
}
563-
}
564-
}
565-
finally
566-
{
567-
#if DEVELOPMENT_BUILD || UNITY_EDITOR
568-
s_NetworkedBehaviourUpdate.End();
569-
#endif
570-
}
571-
}
572-
573539
internal void WriteNetworkedVarData(Stream stream, ulong clientId)
574540
{
575541
for (int i = 0; i < childNetworkedBehaviours.Count; i++)

com.unity.multiplayer.mlapi/Runtime/Core/NetworkingManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ private void Update()
728728
if (NetworkConfig.EnableNetworkedVar)
729729
{
730730
// Do NetworkedVar updates
731-
NetworkedObject.NetworkedBehaviourUpdate();
731+
NetworkedBehaviour.NetworkedBehaviourUpdate();
732732
}
733733

734734
if (!IsServer && NetworkConfig.EnableMessageBuffering)

0 commit comments

Comments
 (0)