-
Notifications
You must be signed in to change notification settings - Fork 459
Expand file tree
/
Copy pathNetworkPrefabs.cs
More file actions
331 lines (294 loc) · 12.4 KB
/
NetworkPrefabs.cs
File metadata and controls
331 lines (294 loc) · 12.4 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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace Unity.Netcode
{
/// <summary>
/// A class that represents the runtime aspect of network prefabs.
/// This class contains processed prefabs from the NetworkPrefabsList, as
/// well as additional modifications (additions and removals) made at runtime.
/// </summary>
[Serializable]
public class NetworkPrefabs
{
/// <summary>
/// Edit-time scripted object containing a list of NetworkPrefabs.
/// </summary>
/// <remarks>
/// This field can be null if no prefabs are pre-configured.
/// Runtime usages of <see cref="NetworkPrefabs"/> should not depend on this edit-time field for execution.
/// </remarks>
[SerializeField]
public List<NetworkPrefabsList> NetworkPrefabsLists = new List<NetworkPrefabsList>();
/// <summary>
/// This dictionary provides a quick way to check and see if a NetworkPrefab has a NetworkPrefab override.
/// Generated at runtime and OnValidate
/// </summary>
[NonSerialized]
public Dictionary<uint, NetworkPrefab> NetworkPrefabOverrideLinks = new Dictionary<uint, NetworkPrefab>();
/// <summary>
/// This is used for the legacy way of spawning NetworkPrefabs with an override when manually instantiating and spawning.
/// To handle multiple source NetworkPrefab overrides that all point to the same target NetworkPrefab use
/// <see cref="NetworkSpawnManager.InstantiateAndSpawn(NetworkObject, ulong, bool, bool, bool, Vector3, Quaternion)"/>
/// or <see cref="NetworkObject.InstantiateAndSpawn(NetworkManager, ulong, bool, bool, bool, Vector3, Quaternion)"/>.
/// </summary>
[NonSerialized]
public Dictionary<uint, uint> OverrideToNetworkPrefab = new Dictionary<uint, uint>();
/// <summary>
/// Gets the read-only list of all registered network prefabs.
/// </summary>
public IReadOnlyList<NetworkPrefab> Prefabs => m_Prefabs;
[NonSerialized]
private List<NetworkPrefab> m_Prefabs = new List<NetworkPrefab>();
[NonSerialized]
private List<NetworkPrefab> m_RuntimeAddedPrefabs = new List<NetworkPrefab>();
private void AddTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
{
if (AddPrefabRegistration(networkPrefab))
{
// Don't add this to m_RuntimeAddedPrefabs
// This prefab is now in the PrefabList, so if we shutdown and initialize again, we'll pick it up from there.
m_Prefabs.Add(networkPrefab);
}
}
private void RemoveTriggeredByNetworkPrefabList(NetworkPrefab networkPrefab)
{
m_Prefabs.Remove(networkPrefab);
}
/// <summary>
/// Destructor that cleans up network prefab resources.
/// </summary>
~NetworkPrefabs()
{
Shutdown();
}
/// <summary>
/// Deregister from add and remove events and clear the events.
/// </summary>
internal void Shutdown()
{
foreach (var list in NetworkPrefabsLists)
{
list.OnAdd -= AddTriggeredByNetworkPrefabList;
list.OnRemove -= RemoveTriggeredByNetworkPrefabList;
}
}
/// <summary>
/// Processes the <see cref="NetworkPrefabsList"/> if one is present for use during runtime execution,
/// else processes <see cref="Prefabs"/>.
/// </summary>
/// <param name="warnInvalid">When true, logs warnings about invalid prefabs that are removed during initialization.</param>
public void Initialize(bool warnInvalid = true)
{
m_Prefabs.Clear();
NetworkPrefabsLists.RemoveAll(x => x == null);
foreach (var list in NetworkPrefabsLists)
{
list.OnAdd += AddTriggeredByNetworkPrefabList;
list.OnRemove += RemoveTriggeredByNetworkPrefabList;
}
NetworkPrefabOverrideLinks.Clear();
OverrideToNetworkPrefab.Clear();
var prefabs = new List<NetworkPrefab>();
if (NetworkPrefabsLists.Count != 0)
{
foreach (var list in NetworkPrefabsLists)
{
foreach (var networkPrefab in list.PrefabList)
{
prefabs.Add(networkPrefab);
}
}
}
m_Prefabs = new List<NetworkPrefab>();
List<NetworkPrefab> removeList = null;
if (warnInvalid)
{
removeList = new List<NetworkPrefab>();
}
foreach (var networkPrefab in prefabs)
{
if (AddPrefabRegistration(networkPrefab))
{
m_Prefabs.Add(networkPrefab);
}
else
{
removeList?.Add(networkPrefab);
}
}
foreach (var networkPrefab in m_RuntimeAddedPrefabs)
{
if (AddPrefabRegistration(networkPrefab))
{
m_Prefabs.Add(networkPrefab);
}
else
{
removeList?.Add(networkPrefab);
}
}
// Clear out anything that is invalid or not used
if (removeList?.Count > 0)
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
{
var sb = new StringBuilder("Removing invalid prefabs from Network Prefab registration: ");
sb.Append(string.Join(", ", removeList));
NetworkLog.LogWarning(sb.ToString());
}
}
}
/// <summary>
/// Add a new NetworkPrefab instance to the list.
/// </summary>
/// <param name="networkPrefab">The <see cref="NetworkPrefab"/> to add.</param>
/// <returns>True if the prefab was successfully added, false if it was invalid or already registered</returns>
/// <remarks>
/// The framework does not synchronize this list between clients. Any runtime changes must be handled manually.<br />
/// Any modifications made here are not persisted. Permanent configuration changes should be done
/// through the <see cref="NetworkPrefabsList"/> scriptable object property.
/// </remarks>
public bool Add(NetworkPrefab networkPrefab)
{
if (AddPrefabRegistration(networkPrefab))
{
m_Prefabs.Add(networkPrefab);
m_RuntimeAddedPrefabs.Add(networkPrefab);
return true;
}
return false;
}
/// <summary>
/// Remove a NetworkPrefab instance from the list.
/// </summary>
/// <param name="prefab">The <see cref="NetworkPrefab"/> to remove.</param>
/// <remarks>
/// The framework does not synchronize this list between clients. Any runtime changes must be handled manually.<br />
/// Any modifications made here are not persisted. Permanent configuration changes should be done
/// through the <see cref="NetworkPrefabsList"/> scriptable object property.
/// </remarks>
public void Remove(NetworkPrefab prefab)
{
if (prefab == null)
{
throw new ArgumentNullException(nameof(prefab));
}
m_Prefabs.Remove(prefab);
m_RuntimeAddedPrefabs.Remove(prefab);
OverrideToNetworkPrefab.Remove(prefab.TargetPrefabGlobalObjectIdHash);
NetworkPrefabOverrideLinks.Remove(prefab.SourcePrefabGlobalObjectIdHash);
}
/// <summary>
/// Remove a NetworkPrefab instance with matching <see cref="NetworkPrefab.Prefab"/> from the list.
/// </summary>
/// <param name="prefab">The <see cref="GameObject"/> to match against for removal.</param>
/// <remarks>
/// The framework does not synchronize this list between clients. Any runtime changes must be handled manually.<br />
/// Any modifications made here are not persisted. Permanent configuration changes should be done
/// through the <see cref="NetworkPrefabsList"/> scriptable object property.
/// </remarks>
public void Remove(GameObject prefab)
{
if (prefab == null)
{
throw new ArgumentNullException(nameof(prefab));
}
for (int i = 0; i < m_Prefabs.Count; i++)
{
if (m_Prefabs[i].Prefab == prefab)
{
Remove(m_Prefabs[i]);
return;
}
}
for (int i = 0; i < m_RuntimeAddedPrefabs.Count; i++)
{
if (m_RuntimeAddedPrefabs[i].Prefab == prefab)
{
Remove(m_RuntimeAddedPrefabs[i]);
return;
}
}
}
/// <summary>
/// Check if the given GameObject is present as a prefab within the list.
/// </summary>
/// <param name="prefab">The prefab to check.</param>
/// <returns>True if the prefab exists or false if it does not.</returns>
public bool Contains(GameObject prefab)
{
for (int i = 0; i < m_Prefabs.Count; i++)
{
// Check both values as Prefab and be different than SourcePrefabToOverride
if (m_Prefabs[i].Prefab == prefab || m_Prefabs[i].SourcePrefabToOverride == prefab)
{
return true;
}
}
return false;
}
/// <summary>
/// Check if the given NetworkPrefab is present within the list.
/// </summary>
/// <param name="prefab">The prefab to check.</param>
/// <returns>True if the prefab exists or false if it does not.</returns>
public bool Contains(NetworkPrefab prefab)
{
for (int i = 0; i < m_Prefabs.Count; i++)
{
if (m_Prefabs[i].Equals(prefab))
{
return true;
}
}
return false;
}
/// <summary>
/// Configures <see cref="NetworkPrefabOverrideLinks"/> for the given <see cref="NetworkPrefab"/>.
/// </summary>
private bool AddPrefabRegistration(NetworkPrefab networkPrefab)
{
if (networkPrefab == null)
{
return false;
}
// Safeguard validation check since this method is called from outside of NetworkConfig and we can't control what's passed in.
if (!networkPrefab.Validate())
{
return false;
}
uint source = networkPrefab.SourcePrefabGlobalObjectIdHash;
uint target = networkPrefab.TargetPrefabGlobalObjectIdHash;
// Make sure the prefab isn't already registered.
if (NetworkPrefabOverrideLinks.ContainsKey(source))
{
var networkObject = networkPrefab.Prefab.GetComponent<NetworkObject>();
// This should never happen, but in the case it somehow does log an error and remove the duplicate entry
Debug.LogError($"{nameof(NetworkPrefab)} ({networkObject.name}) has a duplicate {nameof(NetworkObject.GlobalObjectIdHash)} source entry value of: {source}!");
return false;
}
// If we don't have an override configured, registration is simple!
if (networkPrefab.Override == NetworkPrefabOverride.None)
{
NetworkPrefabOverrideLinks.Add(source, networkPrefab);
return true;
}
switch (networkPrefab.Override)
{
case NetworkPrefabOverride.Prefab:
case NetworkPrefabOverride.Hash:
{
NetworkPrefabOverrideLinks.Add(source, networkPrefab);
if (!OverrideToNetworkPrefab.ContainsKey(target))
{
OverrideToNetworkPrefab.Add(target, source);
}
}
break;
}
return true;
}
}
}