Skip to content

Commit e80c159

Browse files
Update
Backporting #3081 to NGO v1
1 parent 2da4dd5 commit e80c159

File tree

9 files changed

+548
-84
lines changed

9 files changed

+548
-84
lines changed

com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,13 @@ public virtual void OnGainedOwnership() { }
765765
internal void InternalOnGainedOwnership()
766766
{
767767
UpdateNetworkProperties();
768+
// New owners need to assure any NetworkVariables they have write permissions
769+
// to are updated so the previous and original values are aligned with the
770+
// current value (primarily for collections).
771+
if (OwnerClientId == NetworkManager.LocalClientId)
772+
{
773+
UpdateNetworkVariableOnOwnershipChanged();
774+
}
768775
OnGainedOwnership();
769776
}
770777

@@ -946,20 +953,20 @@ internal void PreVariableUpdate()
946953
PreNetworkVariableWrite();
947954
}
948955

949-
internal void VariableUpdate(ulong targetClientId)
950-
{
951-
NetworkVariableUpdate(targetClientId, NetworkBehaviourId);
952-
}
953-
954956
internal readonly List<int> NetworkVariableIndexesToReset = new List<int>();
955957
internal readonly HashSet<int> NetworkVariableIndexesToResetSet = new HashSet<int>();
956958

957-
private void NetworkVariableUpdate(ulong targetClientId, int behaviourIndex)
959+
internal void NetworkVariableUpdate(ulong targetClientId, int behaviourIndex, bool forceSend = false)
958960
{
959-
if (!CouldHaveDirtyNetworkVariables())
961+
if (!forceSend && !CouldHaveDirtyNetworkVariables())
960962
{
961963
return;
962964
}
965+
// Getting these ahead of time actually improves performance
966+
var networkManager = NetworkManager;
967+
var networkObject = NetworkObject;
968+
var messageManager = networkManager.MessageManager;
969+
var connectionManager = networkManager.ConnectionManager;
963970

964971
for (int j = 0; j < m_DeliveryMappedNetworkVariableIndices.Count; j++)
965972
{
@@ -982,10 +989,14 @@ private void NetworkVariableUpdate(ulong targetClientId, int behaviourIndex)
982989
var message = new NetworkVariableDeltaMessage
983990
{
984991
NetworkObjectId = NetworkObjectId,
985-
NetworkBehaviourIndex = NetworkObject.GetNetworkBehaviourOrderIndex(this),
992+
NetworkBehaviourIndex = networkObject.GetNetworkBehaviourOrderIndex(this),
986993
NetworkBehaviour = this,
987994
TargetClientId = targetClientId,
988-
DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j]
995+
DeliveryMappedNetworkVariableIndex = m_DeliveryMappedNetworkVariableIndices[j],
996+
// By sending the network delivery we can forward messages immediately as opposed to processing them
997+
// at the end. While this will send updates to clients that cannot read, the handler will ignore anything
998+
// sent to a client that does not have read permissions.
999+
NetworkDelivery = m_DeliveryTypesForNetworkVariableGroups[j]
9891000
};
9901001
// TODO: Serialization is where the IsDirty flag gets changed.
9911002
// Messages don't get sent from the server to itself, so if we're host and sending to ourselves,
@@ -994,15 +1005,15 @@ private void NetworkVariableUpdate(ulong targetClientId, int behaviourIndex)
9941005
// so we don't have to do this serialization work if we're not going to use the result.
9951006
if (IsServer && targetClientId == NetworkManager.ServerClientId)
9961007
{
997-
var tmpWriter = new FastBufferWriter(NetworkManager.MessageManager.NonFragmentedMessageMaxSize, Allocator.Temp, NetworkManager.MessageManager.FragmentedMessageMaxSize);
1008+
var tmpWriter = new FastBufferWriter(messageManager.NonFragmentedMessageMaxSize, Allocator.Temp, messageManager.FragmentedMessageMaxSize);
9981009
using (tmpWriter)
9991010
{
10001011
message.Serialize(tmpWriter, message.Version);
10011012
}
10021013
}
10031014
else
10041015
{
1005-
NetworkManager.ConnectionManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], targetClientId);
1016+
connectionManager.SendMessage(ref message, m_DeliveryTypesForNetworkVariableGroups[j], targetClientId);
10061017
}
10071018
}
10081019
}
@@ -1029,6 +1040,26 @@ private bool CouldHaveDirtyNetworkVariables()
10291040
return false;
10301041
}
10311042

1043+
/// <summary>
1044+
/// Invoked on a new client to assure the previous and original values
1045+
/// are synchronized with the current known value.
1046+
/// </summary>
1047+
/// <remarks>
1048+
/// Primarily for collections to assure the previous value(s) is/are the
1049+
/// same as the current value(s) in order to not re-send already known entries.
1050+
/// </remarks>
1051+
internal void UpdateNetworkVariableOnOwnershipChanged()
1052+
{
1053+
for (int j = 0; j < NetworkVariableFields.Count; j++)
1054+
{
1055+
// Only invoke OnInitialize on NetworkVariables the owner can write to
1056+
if (NetworkVariableFields[j].CanClientWrite(OwnerClientId))
1057+
{
1058+
NetworkVariableFields[j].OnInitialize();
1059+
}
1060+
}
1061+
}
1062+
10321063
internal void MarkVariablesDirty(bool dirty)
10331064
{
10341065
for (int j = 0; j < NetworkVariableFields.Count; j++)
@@ -1037,6 +1068,17 @@ internal void MarkVariablesDirty(bool dirty)
10371068
}
10381069
}
10391070

1071+
internal void MarkOwnerReadVariablesDirty()
1072+
{
1073+
for (int j = 0; j < NetworkVariableFields.Count; j++)
1074+
{
1075+
if (NetworkVariableFields[j].ReadPerm == NetworkVariableReadPermission.Owner)
1076+
{
1077+
NetworkVariableFields[j].SetDirty(true);
1078+
}
1079+
}
1080+
}
1081+
10401082
/// <summary>
10411083
/// Synchronizes by setting only the NetworkVariable field values that the client has permission to read.
10421084
/// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject)
@@ -1067,15 +1109,23 @@ internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClie
10671109
// The way we do packing, any value > 63 in a ushort will use the full 2 bytes to represent.
10681110
writer.WriteValueSafe((ushort)0);
10691111
var startPos = writer.Position;
1070-
NetworkVariableFields[j].WriteField(writer);
1112+
// Write the NetworkVariable field value
1113+
// WriteFieldSynchronization will write the current value only if there are no pending changes.
1114+
// Otherwise, it will write the previous value if there are pending changes since the pending
1115+
// changes will be sent shortly after the client's synchronization.
1116+
NetworkVariableFields[j].WriteFieldSynchronization(writer);
10711117
var size = writer.Position - startPos;
10721118
writer.Seek(writePos);
10731119
writer.WriteValueSafe((ushort)size);
10741120
writer.Seek(startPos + size);
10751121
}
10761122
else
10771123
{
1078-
NetworkVariableFields[j].WriteField(writer);
1124+
// Write the NetworkVariable field value
1125+
// WriteFieldSynchronization will write the current value only if there are no pending changes.
1126+
// Otherwise, it will write the previous value if there are pending changes since the pending
1127+
// changes will be sent shortly after the client's synchronization.
1128+
NetworkVariableFields[j].WriteFieldSynchronization(writer);
10791129
}
10801130
}
10811131
else // Only if EnsureNetworkVariableLengthSafety, otherwise just skip

com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@ public class NetworkBehaviourUpdater
1919

2020
internal void AddForUpdate(NetworkObject networkObject)
2121
{
22+
// Since this is a HashSet, we don't need to worry about duplicate entries
2223
m_PendingDirtyNetworkObjects.Add(networkObject);
2324
}
2425

25-
internal void NetworkBehaviourUpdate()
26+
/// <summary>
27+
/// Sends NetworkVariable deltas
28+
/// </summary>
29+
/// <param name="forceSend">internal only, when changing ownership we want to send this before the change in ownership message</param>
30+
internal void NetworkBehaviourUpdate(bool forceSend = false)
2631
{
2732
#if DEVELOPMENT_BUILD || UNITY_EDITOR
2833
m_NetworkBehaviourUpdate.Begin();
@@ -54,7 +59,7 @@ internal void NetworkBehaviourUpdate()
5459
// Sync just the variables for just the objects this client sees
5560
for (int k = 0; k < dirtyObj.ChildNetworkBehaviours.Count; k++)
5661
{
57-
dirtyObj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId);
62+
dirtyObj.ChildNetworkBehaviours[k].NetworkVariableUpdate(client.ClientId, k, forceSend);
5863
}
5964
}
6065
}
@@ -73,7 +78,7 @@ internal void NetworkBehaviourUpdate()
7378
}
7479
for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++)
7580
{
76-
sobj.ChildNetworkBehaviours[k].VariableUpdate(NetworkManager.ServerClientId);
81+
sobj.ChildNetworkBehaviours[k].NetworkVariableUpdate(NetworkManager.ServerClientId, k, forceSend);
7782
}
7883
}
7984
}
@@ -86,19 +91,28 @@ internal void NetworkBehaviourUpdate()
8691
var behaviour = dirtyObj.ChildNetworkBehaviours[k];
8792
for (int i = 0; i < behaviour.NetworkVariableFields.Count; i++)
8893
{
94+
// Set to true for NetworkVariable to ignore duplication of the
95+
// "internal original value" for collections support.
96+
behaviour.NetworkVariableFields[i].NetworkUpdaterCheck = true;
8997
if (behaviour.NetworkVariableFields[i].IsDirty() &&
9098
!behaviour.NetworkVariableIndexesToResetSet.Contains(i))
9199
{
92100
behaviour.NetworkVariableIndexesToResetSet.Add(i);
93101
behaviour.NetworkVariableIndexesToReset.Add(i);
94102
}
103+
// Set to true for NetworkVariable to ignore duplication of the
104+
// "internal original value" for collections support.
105+
behaviour.NetworkVariableFields[i].NetworkUpdaterCheck = false;
95106
}
96107
}
97108
}
109+
98110
// Now, reset all the no-longer-dirty variables
99111
foreach (var dirtyobj in m_DirtyNetworkObjects)
100112
{
101-
dirtyobj.PostNetworkVariableWrite();
113+
dirtyobj.PostNetworkVariableWrite(forceSend);
114+
// Once done processing, we set the previous owner id to the current owner id
115+
dirtyobj.PreviousOwnerId = dirtyobj.OwnerClientId;
102116
}
103117
m_DirtyNetworkObjects.Clear();
104118
}

com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ private GlobalObjectId GetGlobalId()
272272
/// </summary>
273273
public ulong OwnerClientId { get; internal set; }
274274

275+
internal ulong PreviousOwnerId;
276+
275277
/// <summary>
276278
/// If true, the object will always be replicated as root on clients and the parent will be ignored.
277279
/// </summary>
@@ -1484,6 +1486,14 @@ internal void MarkVariablesDirty(bool dirty)
14841486
}
14851487
}
14861488

1489+
internal void MarkOwnerReadVariablesDirty()
1490+
{
1491+
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
1492+
{
1493+
ChildNetworkBehaviours[i].MarkOwnerReadVariablesDirty();
1494+
}
1495+
}
1496+
14871497
// NGO currently guarantees that the client will receive spawn data for all objects in one network tick.
14881498
// Children may arrive before their parents; when they do they are stored in OrphanedChildren and then
14891499
// resolved when their parents arrived. Because we don't send a partial list of spawns (yet), something
@@ -1725,7 +1735,7 @@ public void Deserialize(FastBufferReader reader)
17251735
}
17261736
}
17271737

1728-
internal void PostNetworkVariableWrite()
1738+
internal void PostNetworkVariableWrite(bool forceSend)
17291739
{
17301740
for (int k = 0; k < ChildNetworkBehaviours.Count; k++)
17311741
{

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ChangeOwnershipMessage.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,6 @@ public void Handle(ref NetworkContext context)
4545
networkObject.InvokeBehaviourOnLostOwnership();
4646
}
4747

48-
// We are new owner.
49-
if (OwnerClientId == networkManager.LocalClientId)
50-
{
51-
networkObject.InvokeBehaviourOnGainedOwnership();
52-
}
53-
5448
// For all other clients that are neither the former or current owner, update the behaviours' properties
5549
if (OwnerClientId != networkManager.LocalClientId && originalOwner != networkManager.LocalClientId)
5650
{
@@ -60,6 +54,21 @@ public void Handle(ref NetworkContext context)
6054
}
6155
}
6256

57+
// We are new owner.
58+
if (OwnerClientId == networkManager.LocalClientId)
59+
{
60+
networkObject.InvokeBehaviourOnGainedOwnership();
61+
}
62+
63+
if (originalOwner == networkManager.LocalClientId)
64+
{
65+
// Mark any owner read variables as dirty
66+
networkObject.MarkOwnerReadVariablesDirty();
67+
// Immediately queue any pending deltas and order the message before the
68+
// change in ownership message.
69+
networkManager.BehaviourUpdater.NetworkBehaviourUpdate(true);
70+
}
71+
6372
networkObject.InvokeOwnershipChanged(originalOwner, OwnerClientId);
6473

6574
networkManager.NetworkMetrics.TrackOwnershipChangeReceived(context.SenderId, networkObject, context.MessageSize);

0 commit comments

Comments
 (0)