using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
public class SyncListString : SyncList<string>
protected override void SerializeItem(NetworkWriter writer, string item) => writer.WriteString(item);
protected override string DeserializeItem(NetworkReader reader) => reader.ReadString();
public class SyncListFloat : SyncList<float>
protected override void SerializeItem(NetworkWriter writer, float item) => writer.WriteSingle(item);
protected override float DeserializeItem(NetworkReader reader) => reader.ReadSingle();
public class SyncListInt : SyncList<int>
protected override void SerializeItem(NetworkWriter writer, int item) => writer.WritePackedInt32(item);
protected override int DeserializeItem(NetworkReader reader) => reader.ReadPackedInt32();
public class SyncListUInt : SyncList<uint>
protected override void SerializeItem(NetworkWriter writer, uint item) => writer.WritePackedUInt32(item);
protected override uint DeserializeItem(NetworkReader reader) => reader.ReadPackedUInt32();
public class SyncListBool : SyncList<bool>
protected override void SerializeItem(NetworkWriter writer, bool item) => writer.WriteBoolean(item);
protected override bool DeserializeItem(NetworkReader reader) => reader.ReadBoolean();
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class SyncList<T> : IList<T>, IReadOnlyList<T>, SyncObject
public delegate void SyncListChanged(Operation op, int itemIndex, T oldItem, T newItem);
readonly IList<T> objects;
readonly IEqualityComparer<T> comparer;
public int Count => objects.Count;
public bool IsReadOnly { get; private set; }
public event SyncListChanged Callback;
public enum Operation : byte
internal Operation operation;
readonly List<Change> changes = new List<Change>();
protected virtual void SerializeItem(NetworkWriter writer, T item) { }
protected virtual T DeserializeItem(NetworkReader reader) => default;
protected SyncList(IEqualityComparer<T> comparer = null)
this.comparer = comparer ?? EqualityComparer<T>.Default;
protected SyncList(IList<T> objects, IEqualityComparer<T> comparer = null)
this.comparer = comparer ?? EqualityComparer<T>.Default;
public bool IsDirty => changes.Count > 0;
public void Flush() => changes.Clear();
void AddOperation(Operation op, int itemIndex, T oldItem, T newItem)
throw new InvalidOperationException("Synclists can only be modified at the server");
Change change = new Change
Callback?.Invoke(op, itemIndex, oldItem, newItem);
public void OnSerializeAll(NetworkWriter writer)
writer.WritePackedUInt32((uint)objects.Count);
for (int i = 0; i < objects.Count; i++)
SerializeItem(writer, obj);
writer.WritePackedUInt32((uint)changes.Count);
public void OnSerializeDelta(NetworkWriter writer)
writer.WritePackedUInt32((uint)changes.Count);
for (int i = 0; i < changes.Count; i++)
Change change = changes[i];
writer.WriteByte((byte)change.operation);
switch (change.operation)
SerializeItem(writer, change.item);
case Operation.OP_REMOVEAT:
writer.WritePackedUInt32((uint)change.index);
case Operation.OP_INSERT:
writer.WritePackedUInt32((uint)change.index);
SerializeItem(writer, change.item);
public void OnDeserializeAll(NetworkReader reader)
int count = (int)reader.ReadPackedUInt32();
for (int i = 0; i < count; i++)
T obj = DeserializeItem(reader);
changesAhead = (int)reader.ReadPackedUInt32();
public void OnDeserializeDelta(NetworkReader reader)
int changesCount = (int)reader.ReadPackedUInt32();
for (int i = 0; i < changesCount; i++)
Operation operation = (Operation)reader.ReadByte();
bool apply = changesAhead == 0;
newItem = DeserializeItem(reader);
case Operation.OP_INSERT:
index = (int)reader.ReadPackedUInt32();
newItem = DeserializeItem(reader);
objects.Insert(index, newItem);
case Operation.OP_REMOVEAT:
index = (int)reader.ReadPackedUInt32();
oldItem = objects[index];
index = (int)reader.ReadPackedUInt32();
newItem = DeserializeItem(reader);
oldItem = objects[index];
objects[index] = newItem;
Callback?.Invoke(operation, index, oldItem, newItem);
AddOperation(Operation.OP_ADD, objects.Count - 1, default, item);
AddOperation(Operation.OP_CLEAR, 0, default, default);
public bool Contains(T item) => IndexOf(item) >= 0;
public void CopyTo(T[] array, int index) => objects.CopyTo(array, index);
public int IndexOf(T item)
for (int i = 0; i < objects.Count; ++i)
if (comparer.Equals(item, objects[i]))
public int FindIndex(Predicate<T> match)
for (int i = 0; i < objects.Count; ++i)
public void Insert(int index, T item)
objects.Insert(index, item);
AddOperation(Operation.OP_INSERT, index, default, item);
public bool Remove(T item)
int index = IndexOf(item);
bool result = index >= 0;
public void RemoveAt(int index)
T oldItem = objects[index];
AddOperation(Operation.OP_REMOVEAT, index, oldItem, default);
if (!comparer.Equals(objects[i], value))
AddOperation(Operation.OP_SET, i, oldItem, value);
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
public struct Enumerator : IEnumerator<T>
readonly SyncList<T> list;
public T Current { get; private set; }
public Enumerator(SyncList<T> list)
if (++index >= list.Count)
public void Reset() => index = -1;
object IEnumerator.Current => Current;
public void Dispose() { }