This document is about: FUSION 2
SWITCH TO

Network Collections

Overview

Fusion supplies struct-based collection types, which can be used for Networked Properties.

  • NetworkArray<T>
  • NetworkDictionary<K, V>
  • NetworkLinkedList<T>
  • NetworkString<Size>

Be aware, these collections are structs and not reference types. Some minor C# usage differences exist between these collections and C# collections (see Usage In INetworkStructs)

Allowed T/K/V Types are:

  • Primitives
    • byte, sbyte
    • Int16, Int32, Int64
    • UInt16, UInt32, UInt64
    • float
    • double
  • Unity struct types (defined in ILWeaver.cs)
    • Vector2, Vector3, Vector4
    • Quaternion
    • Matrix4x4
    • Vector2Int, Vector3Int
    • BoundingSphere
    • Bounds
    • Rect
    • BoundsInt
    • RectInt
    • Color, Color32
  • System.Guid
  • User Defined INetworkStructs
  • Fusion Defined INetworkStructs
    • NetworkString<IFixedStorage>
    • NetworkBool
    • Ptr
    • Angle
    • BitSet64, BitSet128, BitSet192, BitSet256
    • PlayerRefSet
    • NetworkId
    • NetworkButtons
    • NetworkRNG
    • NetworkObjectGuid
    • NetworkPrefabRef
    • NetworkObjectHeader
    • NetworkPrefabId
    • SceneRef
    • TickTimer
    • IFixedStorage (_2, _4, _8, _16, _32, _64, _128, _256, _512)

Declaration

NetworkArray, NetworkDictionary, and NetworkLinkedList must be defined as properties with the [Networked] attribute. The [Networked] attribute instructs the ILWeaver to generate property code which will connect these collections to backing memory in their associated NetworkRunner and Simultation instances.

The [Capacity] attribute should also be used to specify the maximum number of elements. Memory will be allocated for this many elements, regardless of usage - so select the smallest number that will still be enough to cover the greatest number of elements you anticipate. Elements do not generate network traffic unless they change value.

C#

// A basic Network Collection declaration
[Networked, Capacity(4)]
NetworkArray<int> NetArray => default;

Initialization

When NetworkArray NetworkDictionary, and NetworkLinkedList are declared as a Networked Property in a NetworkBehaviour, MakeInitializer() may be used to define initial values.

C#

[Networked]
[Capacity(4)] // Sets the fixed capacity of the collection
NetworkArray<int> NetArray { get; } 
  // Optional initialization
  = MakeInitializer(new int[] { 0, 1, 2, 3 });

[Networked, Capacity(4)]
NetworkLinkedList<int> NetLList { get; } 
  // Optional initialization
  = MakeInitializer(new int[] { 0, 1, 2, 3 });

[Networked, Capacity(2)]
NetworkDictionary<int, int> NetDict { get; } 
  // Optional initialization
  = MakeInitializer(new Dictionary<int, int> { { 0, 0 }, { 1, 1 } });

NetworkArray<T>

NetworkArray is a struct alternative to C# arrays, which can be used as a Networked Property in Fusion. While not completely identical to C# arrays, NetworkArray is an IEnumerable collection with methods of getting and setting values, as well as copying to and from other collections.

Primary methods are:

  • Get(int index)
  • Set(int, T)

C#

public class CollectionTestsArray : NetworkBehaviour 
{
  [Networked]
  [Capacity(4)] // Sets the fixed capacity of the collection
  NetworkArray<NetworkString<_32>> NetArray { get; } = 
    MakeInitializer(new NetworkString<_32>[] { "#0", "#1", "#2", "#3" });

  public override void FixedUpdateNetwork() 
  {
    NetArray.Set(0, "Zero");
    NetArray.Set(1, "One");
    NetArray.Set(2, "Two");

    // This is invalid with a NetworkDictionary property, use Set() instead.
    // NetArray[3] = "Three";

    NetArray.Set(0, NetArray[0].ToUpper());
    NetArray.Set(1, NetArray.Get(1).ToLower());
    NetArray.Set(2, default);

    for (int i = 0; i < NetArray.Length; ++i) 
    {
      Debug.Log($"{i}: '{NetArray[i]}''");
    }
  }
}

NetworkDictionary<K, V>

NetworkDictionary is a struct alternative to the C# Dictionary, which can be used as a Networked Property in Fusion. While not a complete IDictionary implementation, all of the major expected members are available and similar to Dictionary.

Primary methods and properties are:

  • Clear()
  • Add(K, V)
  • Remove(k)
  • ContainsKey(K)
  • ContainsValue(V)
  • Get(K)
  • Set(K, V)
  • this[K]
  • Capacity
  • Count

C#

public class NetworkDictionaryExample : NetworkBehaviour 
{  
  [Networked]
  [Capacity(4)] // Sets the fixed capacity of the collection
  [UnitySerializeField] // Show this private property in the inspector.
  private NetworkDictionary<int, NetworkString<_32>> NetDict => default;

  public override void FixedUpdateNetwork() 
  {
    NetDict.Clear();
    NetDict.Add(0, "Zero");
    NetDict.Add(2, "Two");
    NetDict.Add(5, "Five");
    
    // Setting values with the indexer are not valid, this is one of the cases
    // where the struct based NetworkDictionary differs in usage from Dictionary.
    // NetDict[0] = "Foo"; // this is not valid.

    if (NetDict.ContainsKey(0))
    {
      NetDict.Set(0, NetDict[0].ToUpper());
    }

    if (NetDict.TryGet(5, out var value))
    {
      NetDict.Set(5, value.ToLower());
    }

    NetDict.Remove(2);

    foreach(var kvp in NetDict)
    {
      Debug.Log($"{NetDict.Count}/{NetDict.Capacity} Key:{kvp.Key} Value:{kvp.Value}");
    }
  }
}

NetworkLinkedList<T>

NetworkLinkedList is a struct alternative to NetworkArray which allows a subset of the capacity to be used. Added items are added to open slots in the backing data. When items are removed, the backing array data does not shift other elements in memory. The collection instead stores data about the element order as part of the struct, and the enumerator and indexer will return elements in the order in which they were added, rather than the order in which they reside in the backing array.

C#

public class NetworkLinkedListExample : NetworkBehaviour 
{
  [Networked]
  [Capacity(4)] // Sets the fixed capacity of the collection
  [UnitySerializeField] // Show this private property in the inspector.
  private NetworkLinkedList<NetworkString<_32>> NetList { get; } 
    = MakeInitializer(new NetworkString<_32>[] { "Zero", "One", "Two", "Four"});

  public override void Spawned() 
  {
    // Remove the second entry, leaving one open capacity.
    NetList.Remove("One");

    // Find an entry by value
    NetList.Set(NetList.IndexOf("Two"), "TWO");

    // Add a new entry. In memory it backfills the now open memory position.
    NetList.Add("Five");

    // The indexed order however remains in sequence,
    // so only the changed memory position is dirty and networked.
    Debug.Log($"List {NetList.Count}/{NetList.Capacity}" + 
      $"0:'{NetList[0]}' 1:'{NetList[1]}' 2:'{NetList[2]} 3:'{NetList[3]}'");
  }
}

NetworkString<Size>

NetworkString is a fixed size collection for string data. Size can be any of the predefined IFixedStorage types, which are named _X, where X is the size of storage struct _32 for example, is backed by a uint[32] and can store a string up to 32 characters in length.

C#

public class NetworkStringExample : NetworkBehaviour 
{
  [Networked]
  public NetworkString<_16> NetString { get; set; }

  public override void FixedUpdateNetwork() 
  {
    if (Runner.IsServer) {
      NetString = System.IO.Path.GetRandomFileName();
    }
  }
}

Usage in INetworkStructs

When modifying values of a Networked Property which is an INetworkStruct, be sure to either:

  • Work with a copy of the INetworkStruct and apply the altered copy to the Networked Property,
  • Or, define the Networked Property as a ref. When the Network Property is declared as a ref, there is no need for copying values, as the reference can be directly modified.

Networked Properties of type INetworkStruct can be initialized (see code below for example).

Using these networked collections in INetworkStructs as Networked Property:

C#

public class NetworkDictionaryTest : NetworkBehaviour 
{
  [System.Serializable]
  struct NetworkStruct : INetworkStruct 
  {
    [Networked, Capacity(16)]
    public NetworkDictionary<int, int> NestedDict => default;

    // NetworkString is a normal struct, so it doesn't require any Fusion attributes
    public NetworkString<_16> NestedString;

    // Define default initialization as a property if needed.
    public static NetworkStruct Defaults 
    {
      get 
      {
        var result = new NetworkStruct();
        result.NestedDict.Add(0, 0);
        result.NestedString = "Initialized";
        return result;
      }
    }
  }

  // Property declared normally as a value type
  [Networked]
  [UnitySerializeField]
  private NetworkStruct NestedStruct { get; set; } = NetworkStruct.Defaults;

  public void ModifyValues() 
  {
    // NestedStruct is a value type, so modifications need to be performed on a copy,
    // and the modified result must be applied back to the property.
    var copy = NestedStruct;
    copy.NestedDict.Add(copy.NestedDict.Count, default);
    copy.NestedString = System.IO.Path.GetRandomFileName();
    NestedStruct = copy;
  }
}

Using these networked collections in INetworkStruct as a referenced Networked Property:

C#

public class NetworkDictionaryTest : NetworkBehaviour 
{
  [System.Serializable]
  struct NetworkStruct : INetworkStruct 
  {
    [Networked, Capacity(16)]
    public NetworkDictionary<int, int> NestedDict => default;

    // NetworkString is a normal struct, so it doesn't require any Fusion attributes
    public NetworkString<_16> NestedString;

    public static NetworkStruct Defaults 
    {
      get 
      {
        var result = new NetworkStruct();
        result.NestedDict.Add(0, 0);
        result.NestedString = "Initialized";
        return result;
      }
    }
  }

  // Property declared as ref type, allowing direct modification of values
  [Networked]
  [UnitySerializeField]
  private ref NetworkStruct NestedStruct => ref MakeRef(NetworkStruct.Defaults);

  public void ModifyValues() 
  {
    // NestedStruct was declared as a ref above, so modifications 
    // may be made directly to the reference, without need for copies.
    NestedStruct.NestedDict.Add(NestedStruct.NestedDict.Count, default);
    NestedStruct.NestedString = System.IO.Path.GetRandomFileName();
  }
}
Back to top