-
Notifications
You must be signed in to change notification settings - Fork 459
Expand file tree
/
Copy pathDestroyObjectMessage.cs
More file actions
216 lines (182 loc) · 8.75 KB
/
DestroyObjectMessage.cs
File metadata and controls
216 lines (182 loc) · 8.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
using System.Linq;
using System.Runtime.CompilerServices;
namespace Unity.Netcode
{
internal struct DestroyObjectMessage : INetworkMessage, INetworkSerializeByMemcpy
{
private const int k_OptimizeDestroyObjectMessage = 1;
private const int k_AllowDestroyGameInPlaced = 2;
public int Version => k_AllowDestroyGameInPlaced;
private const string k_Name = "DestroyObjectMessage";
public ulong NetworkObjectId;
private byte m_DestroyFlags;
internal int DeferredDespawnTick;
// Temporary until we make this a list
internal ulong TargetClientId;
internal bool IsDistributedAuthority;
internal bool IsTargetedDestroy
{
get => ByteUtility.GetBit(m_DestroyFlags, 0);
set => ByteUtility.SetBit(ref m_DestroyFlags, 0, value);
}
private bool IsDeferredDespawn
{
get => ByteUtility.GetBit(m_DestroyFlags, 1);
set => ByteUtility.SetBit(ref m_DestroyFlags, 1, value);
}
/// <summary>
/// Used to communicate whether to destroy the associated game object.
/// Should be false if the object is InScenePlaced and true otherwise
/// </summary>
public bool DestroyGameObject
{
get => ByteUtility.GetBit(m_DestroyFlags, 2);
set => ByteUtility.SetBit(ref m_DestroyFlags, 2, value);
}
public void Serialize(FastBufferWriter writer, int targetVersion)
{
// Set deferred despawn flag
IsDeferredDespawn = DeferredDespawnTick > 0;
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
if (IsDistributedAuthority)
{
writer.WriteByteSafe(m_DestroyFlags);
if (IsTargetedDestroy)
{
BytePacker.WriteValueBitPacked(writer, TargetClientId);
}
if (targetVersion < k_OptimizeDestroyObjectMessage || IsDeferredDespawn)
{
BytePacker.WriteValueBitPacked(writer, DeferredDespawnTick);
}
}
else if (targetVersion >= k_AllowDestroyGameInPlaced)
{
writer.WriteByteSafe(m_DestroyFlags);
}
if (targetVersion < k_OptimizeDestroyObjectMessage)
{
writer.WriteValueSafe(DestroyGameObject);
}
}
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
{
var networkManager = (NetworkManager)context.SystemOwner;
if (!networkManager.IsClient)
{
return false;
}
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
if (networkManager.DistributedAuthorityMode)
{
reader.ReadByteSafe(out m_DestroyFlags);
if (IsTargetedDestroy)
{
ByteUnpacker.ReadValueBitPacked(reader, out TargetClientId);
}
if (receivedMessageVersion < k_OptimizeDestroyObjectMessage || IsDeferredDespawn)
{
ByteUnpacker.ReadValueBitPacked(reader, out DeferredDespawnTick);
}
}
else if (receivedMessageVersion >= k_AllowDestroyGameInPlaced)
{
reader.ReadByteSafe(out m_DestroyFlags);
}
if (receivedMessageVersion < k_OptimizeDestroyObjectMessage)
{
reader.ReadValueSafe(out bool destroyGameObject);
DestroyGameObject = destroyGameObject;
}
if (networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId))
{
return true;
}
// Client-Server mode we always defer where in distributed authority mode we only defer if it is not a targeted destroy
if (!networkManager.DistributedAuthorityMode || (networkManager.DistributedAuthorityMode && !IsTargetedDestroy))
{
networkManager.DeferredMessageManager.DeferMessage(IDeferredNetworkMessageManager.TriggerType.OnSpawn, NetworkObjectId, reader, ref context, k_Name);
}
return true;
}
public void Handle(ref NetworkContext context)
{
var networkManager = (NetworkManager)context.SystemOwner;
networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject);
// The DAHost needs to forward despawn messages to the other clients
if (networkManager.DAHost)
{
HandleDAHostForwardMessage(context.SenderId, ref networkManager, networkObject);
// DAHost adds the object to the queue only if it is not a targeted destroy, or it is and the target is the DAHost client.
if (networkObject && DeferredDespawnTick > 0 && (!IsTargetedDestroy || (IsTargetedDestroy && TargetClientId == 0)))
{
HandleDeferredDespawn(ref networkManager, ref networkObject);
return;
}
}
// If this NetworkObject does not exist on this instance then exit early
if (!networkObject)
{
if (networkManager.LogLevel <= LogLevel.Developer)
{
NetworkLog.LogWarning($"[{nameof(DestroyObjectMessage)}] Received destroy object message for NetworkObjectId ({NetworkObjectId}) on Client-{networkManager.LocalClientId}, but that {nameof(NetworkObject)} does not exist!");
}
return;
}
if (networkManager.DistributedAuthorityMode)
{
// If we are deferring the despawn, then add it to the deferred despawn queue
// If DAHost has reached this point, it is not valid to add to the queue
if (DeferredDespawnTick > 0 && !networkManager.DAHost)
{
HandleDeferredDespawn(ref networkManager, ref networkObject);
return;
}
// If this is targeted and we are not the target, then just update our local observers for this object
if (IsTargetedDestroy && TargetClientId != networkManager.LocalClientId)
{
networkObject.Observers.Remove(TargetClientId);
return;
}
}
// Otherwise just despawn the NetworkObject right now
networkManager.SpawnManager.OnDespawnNonAuthorityObject(networkObject, DestroyGameObject);
networkManager.NetworkMetrics.TrackObjectDestroyReceived(context.SenderId, networkObject, context.MessageSize);
}
/// <summary>
/// Handles forwarding the <see cref="DestroyObjectMessage"/> when acting as the DA Host
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void HandleDAHostForwardMessage(ulong senderId, ref NetworkManager networkManager, NetworkObject networkObject)
{
var message = new DestroyObjectMessage
{
NetworkObjectId = NetworkObjectId,
DestroyGameObject = DestroyGameObject,
IsDistributedAuthority = true,
IsTargetedDestroy = IsTargetedDestroy,
TargetClientId = TargetClientId, // Just always populate this value whether we write it or not
DeferredDespawnTick = DeferredDespawnTick,
};
var ownerClientId = networkObject == null ? senderId : networkObject.OwnerClientId;
var clientIds = networkObject == null ? networkManager.ConnectedClientsIds.ToList() : networkObject.Observers.ToList();
foreach (var clientId in clientIds)
{
if (clientId != networkManager.LocalClientId && clientId != ownerClientId)
{
networkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.ReliableSequenced, clientId);
}
}
}
/// <summary>
/// Handles adding to the deferred despawn queue when the <see cref="DestroyObjectMessage"/> indicates a deferred despawn
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void HandleDeferredDespawn(ref NetworkManager networkManager, ref NetworkObject networkObject)
{
networkObject.DeferredDespawnTick = DeferredDespawnTick;
var hasCallback = networkObject.OnDeferredDespawnComplete != null;
networkManager.SpawnManager.DeferDespawnNetworkObject(NetworkObjectId, DeferredDespawnTick, hasCallback, DestroyGameObject);
}
}
}