Skip to content

Commit e9ec067

Browse files
committed
feat: add ManagedNetworkList and ManagedNetworkArray
1 parent 014a09f commit e9ec067

File tree

6 files changed

+804
-1
lines changed

6 files changed

+804
-1
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Additional documentation and release notes are available at [Multiplayer Documen
88

99
## [Unreleased] - Pitou
1010

11+
### Added
12+
13+
- Added `ManagedNetworkList` that allow to use managed objects in a networked list.
14+
- Added `ManagedNetworkArray` that allow to use managed objects in a networked array.
15+
1116
### Fixed
1217

1318
- Removed anoying warnings when using `NetworkList` in offline mode. (#2279)

com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
11611161
//var type = field.FieldType;
11621162
if (type.IsGenericInstance)
11631163
{
1164-
if (type.Resolve().Name == typeof(NetworkVariable<>).Name || type.Resolve().Name == typeof(NetworkList<>).Name)
1164+
if (type.Resolve().Name == typeof(NetworkVariable<>).Name || type.Resolve().Name == typeof(NetworkList<>).Name || type.Resolve().Name == typeof(ManagedNetworkList<>).Name || type.Resolve().Name == typeof(ManagedNetworkArray<>).Name)
11651165
{
11661166
var genericInstanceType = (GenericInstanceType)type;
11671167
var wrappedType = genericInstanceType.GenericArguments[0];
Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Unity.Netcode
5+
{
6+
/// <summary>
7+
/// Event based <see cref="NetworkVariableBase"/> container for syncing arrays
8+
/// </summary>
9+
/// <typeparam name="T">The type for the array</typeparam>
10+
public class ManagedNetworkArray<T> : NetworkVariableBase where T : INetworkSerializable, IEquatable<T>
11+
{
12+
private readonly T[] m_array;
13+
private readonly List<NetworkArrayEvent<T>> m_DirtyEvents = new List<NetworkArrayEvent<T>>();
14+
15+
/// <summary>
16+
/// Delegate type for array changed event
17+
/// </summary>
18+
/// <param name="changeEvent">Struct containing information about the change event</param>
19+
public delegate void OnArrayChangedDelegate(NetworkArrayEvent<T> changeEvent);
20+
21+
/// <summary>
22+
/// The callback to be invoked when the array gets changed
23+
/// </summary>
24+
public event OnArrayChangedDelegate OnArrayChanged;
25+
26+
/// <inheritdoc/>
27+
/// <param name="values"></param>
28+
/// <param name="readPerm"></param>
29+
/// <param name="writePerm"></param>
30+
public ManagedNetworkArray(int size,
31+
IEnumerable<T> values = default,
32+
NetworkVariableReadPermission readPerm = DefaultReadPerm,
33+
NetworkVariableWritePermission writePerm = DefaultWritePerm)
34+
: base(readPerm, writePerm)
35+
{
36+
if (size < 0) {
37+
throw new ArgumentOutOfRangeException("size", size, "size should be greater than 0");
38+
}
39+
m_array = new T[size];
40+
41+
// allow null IEnumerable<T> to mean "no values"
42+
if (values != null) {
43+
IEnumerator<T> enumerator = values.GetEnumerator();
44+
for (int i = 0; i < size && enumerator.MoveNext(); i++) {
45+
m_array[i] = enumerator.Current;
46+
}
47+
}
48+
}
49+
50+
/// <inheritdoc />
51+
public override void ResetDirty()
52+
{
53+
base.ResetDirty();
54+
if (m_DirtyEvents.Count > 0) {
55+
m_DirtyEvents.Clear();
56+
}
57+
}
58+
59+
/// <inheritdoc />
60+
public override bool IsDirty()
61+
{
62+
// we call the base class to allow the SetDirty() mechanism to work
63+
return base.IsDirty() || m_DirtyEvents.Count > 0;
64+
}
65+
66+
internal void MarkNetworkObjectDirty()
67+
{
68+
if (m_NetworkBehaviour == null) {
69+
return;
70+
}
71+
72+
m_NetworkBehaviour.NetworkManager.BehaviourUpdater.AddForUpdate(m_NetworkBehaviour.NetworkObject);
73+
}
74+
75+
/// <inheritdoc />
76+
public override void WriteDelta(FastBufferWriter writer)
77+
{
78+
79+
if (base.IsDirty()) {
80+
writer.WriteValueSafe((ushort)1);
81+
writer.WriteValueSafe(NetworkArrayEvent<T>.EventType.Full);
82+
WriteField(writer);
83+
84+
return;
85+
}
86+
87+
writer.WriteValueSafe((ushort)m_DirtyEvents.Count);
88+
for (int i = 0; i < m_DirtyEvents.Count; i++) {
89+
NetworkArrayEvent<T> element = m_DirtyEvents[i];
90+
writer.WriteValueSafe(element.Type);
91+
switch (element.Type) {
92+
case NetworkArrayEvent<T>.EventType.Value: {
93+
writer.WriteValueSafe(element.Index);
94+
NetworkVariableSerialization<T>.Write(writer, ref element.Value);
95+
}
96+
break;
97+
case NetworkArrayEvent<T>.EventType.Clear: {
98+
//Nothing has to be written
99+
}
100+
break;
101+
}
102+
}
103+
}
104+
105+
/// <inheritdoc />
106+
public override void WriteField(FastBufferWriter writer)
107+
{
108+
writer.WriteValueSafe((ushort)m_array.Length);
109+
for (int i = 0; i < m_array.Length; i++) {
110+
T element = m_array[i];
111+
NetworkVariableSerialization<T>.Write(writer, ref element);
112+
m_array[i] = element;
113+
}
114+
}
115+
116+
/// <inheritdoc />
117+
public override void ReadField(FastBufferReader reader)
118+
{
119+
reader.ReadValueSafe(out ushort count);
120+
for (int i = 0; i < count; i++) {
121+
T value = default;
122+
NetworkVariableSerialization<T>.Read(reader, ref value);
123+
m_array[i] = value;
124+
}
125+
}
126+
127+
/// <inheritdoc />
128+
public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)
129+
{
130+
reader.ReadValueSafe(out ushort deltaCount);
131+
for (int i = 0; i < deltaCount; i++) {
132+
reader.ReadValueSafe(out NetworkArrayEvent<T>.EventType eventType);
133+
switch (eventType) {
134+
case NetworkArrayEvent<T>.EventType.Value: {
135+
reader.ReadValueSafe(out int index);
136+
T value = default;
137+
NetworkVariableSerialization<T>.Read(reader, ref value);
138+
if (index >= m_array.Length) {
139+
throw new Exception("Shouldn't be here, index is higher than array length");
140+
}
141+
142+
T previousValue = m_array[index];
143+
m_array[index] = value;
144+
145+
OnArrayChanged?.Invoke(new NetworkArrayEvent<T> {
146+
Type = eventType,
147+
Index = index,
148+
Value = value,
149+
PreviousValue = previousValue
150+
});
151+
152+
if (keepDirtyDelta) {
153+
m_DirtyEvents.Add(new NetworkArrayEvent<T>() {
154+
Type = eventType,
155+
Index = index,
156+
Value = value,
157+
PreviousValue = previousValue
158+
});
159+
MarkNetworkObjectDirty();
160+
}
161+
}
162+
break;
163+
case NetworkArrayEvent<T>.EventType.Clear: {
164+
//Read nothing
165+
ClearArray();
166+
167+
OnArrayChanged?.Invoke(new NetworkArrayEvent<T> {
168+
Type = eventType,
169+
});
170+
171+
if (keepDirtyDelta) {
172+
m_DirtyEvents.Add(new NetworkArrayEvent<T>() {
173+
Type = eventType
174+
});
175+
MarkNetworkObjectDirty();
176+
}
177+
}
178+
break;
179+
case NetworkArrayEvent<T>.EventType.Full: {
180+
ReadField(reader);
181+
ResetDirty();
182+
}
183+
break;
184+
}
185+
}
186+
}
187+
188+
/// <inheritdoc />
189+
public void Clear()
190+
{
191+
// check write permissions
192+
if (m_NetworkBehaviour && !CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId)) {
193+
throw new InvalidOperationException($"Client is not allowed to write to this ManagedNetworkArray");
194+
}
195+
196+
ClearArray();
197+
198+
var listEvent = new NetworkArrayEvent<T>() {
199+
Type = NetworkArrayEvent<T>.EventType.Clear
200+
};
201+
202+
HandleAddListEvent(listEvent);
203+
}
204+
205+
/// <inheritdoc />
206+
public int Length => m_array.Length;
207+
208+
/// <inheritdoc />
209+
public bool Contains(T item)
210+
{
211+
return IndexOf(item) != -1;
212+
}
213+
214+
/// <inheritdoc />
215+
public int IndexOf(T item)
216+
{
217+
return Array.IndexOf(m_array, item);
218+
}
219+
220+
/// <inheritdoc />
221+
public IEnumerator<T> GetEnumerator()
222+
{
223+
return ((IEnumerable<T>)m_array).GetEnumerator();
224+
}
225+
226+
/// <inheritdoc />
227+
public T this[int index]
228+
{
229+
get => m_array[index];
230+
set {
231+
// check write permissions
232+
if (m_NetworkBehaviour && !CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId)) {
233+
throw new InvalidOperationException("Client is not allowed to write to this ManagedNetworkArray");
234+
}
235+
236+
T previousValue = m_array[index];
237+
m_array[index] = value;
238+
239+
var listEvent = new NetworkArrayEvent<T>() {
240+
Type = NetworkArrayEvent<T>.EventType.Value,
241+
Index = index,
242+
Value = value,
243+
PreviousValue = previousValue
244+
};
245+
246+
HandleAddListEvent(listEvent);
247+
}
248+
}
249+
250+
private void HandleAddListEvent(NetworkArrayEvent<T> listEvent)
251+
{
252+
m_DirtyEvents.Add(listEvent);
253+
MarkNetworkObjectDirty();
254+
OnArrayChanged?.Invoke(listEvent);
255+
}
256+
257+
private void ClearArray()
258+
{
259+
for (int j = 0; j < m_array.Length; j++) {
260+
m_array[j] = default;
261+
}
262+
}
263+
264+
/// <summary>
265+
/// This is actually unused left-over from a previous interface
266+
/// </summary>
267+
public int LastModifiedTick =>
268+
// todo: implement proper network tick for NetworkList
269+
NetworkTickSystem.NoTick;
270+
271+
}
272+
273+
/// <summary>
274+
/// Struct containing event information about changes to a NetworkArray.
275+
/// </summary>
276+
/// <typeparam name="T">The type for the list that the event is about</typeparam>
277+
public struct NetworkArrayEvent<T>
278+
{
279+
/// <summary>
280+
/// Enum representing the different operations available for triggering an event.
281+
/// </summary>
282+
public enum EventType : byte
283+
{
284+
/// <summary>
285+
/// Change value at an index. Old value or new value can be null.
286+
/// </summary>
287+
Value,
288+
289+
/// <summary>
290+
/// Clear
291+
/// </summary>
292+
Clear,
293+
294+
/// <summary>
295+
/// Full array refresh
296+
/// </summary>
297+
Full
298+
}
299+
300+
/// <summary>
301+
/// Enum representing the operation made to the list.
302+
/// </summary>
303+
public EventType Type;
304+
305+
/// <summary>
306+
/// The value changed, added or removed if available.
307+
/// </summary>
308+
public T Value;
309+
310+
/// <summary>
311+
/// The previous value when "Value" has changed, if available.
312+
/// </summary>
313+
public T PreviousValue;
314+
315+
/// <summary>
316+
/// the index changed, added or removed if available
317+
/// </summary>
318+
public int Index;
319+
}
320+
}

com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/ManagedNetworkArray.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)