This document is about: FUSION 2
SWITCH TO

INetworkStruct

Overview

INetworkStruct defines a struct which can be used by Fusion for Networked Properties and RPC methods.

A user-defined INetworkStruct must:

  • be a struct type
  • implement the INetworkStruct interface
  • be blittable

These structs can also be nested as fields inside other INetworkStructs.

Blittable Requirement

A INetworkStruct must be blittable, which means it has an absolute memory footprint in both managed and unmanaged contexts. As such, field members may NOT be:

  • any ref types
  • a string or char (use NetworkString instead)
  • a bool (use NetworkBool or an int instead).

NOTE: Bools will compile and may work for most use cases, but they are not guaranteed to work across platforms - so we recommend NetworkBool in their place.

Learn more about .Net blittable types

Allowed Field Types

Field members directly affect the memory footprint and alignment of a struct, and therefore MUST be blittable types. Property members which are not auto-implementing and method members do not affect the memory layout, and therefore are not restricted to these types.

Safe field types include:

  • Blittable primitives
    • byte, sbyte
    • short, int, long
    • ushort, uint, ulong
    • float, double
  • Blittable Unity Struct Types
    • Vector2, Vector3, Vector4
    • Quaternion
    • Matrix4x4
    • Vector2Int, Vector3Int
    • BoundingSphere
    • Bounds
    • Rect
    • BoundsInt
    • RectInt
    • Color, Color32
  • Enums
  • System Blittable types (such as System.Guid)
  • User Defined Blittable Structs
  • User Defined INetworkStructs
  • Fusion Defined INetworkStructs
    • NetworkString<>
    • NetworkBool
    • Ptr
    • Angle
    • BitSet64, BitSet128, BitSet192, BitSet256
    • PlayerRefSet
    • NetworkId
    • NetworkButtons
    • NetworkRNG
    • NetworkObjectGuid
    • NetworkPrefabRef
    • NetworkObjectHeader
    • NetworkPrefabId
    • PlayerRef
    • SceneRef
    • TickTimer
    • IFixedStorage (_2, _4, _8, _16, _32, _64, _128, _256, _512)
  • Network Collections
  • Fixed-Sized Buffers (unsafe: such as fixed int MyArray[32])

Usage

INetworkStruct as Networked Properties

Note that structs are value types. For convenience Networked Properties can use the ref keyword. This allows for direct modification of the properties members without needing to work with copies.

C#

using Fusion;
using UnityEngine;

public class NetworkStructSampleCode : NetworkBehaviour
{
  public struct NetworkStructExample : INetworkStruct
  {
    public int IntField;
  }

  // For convenience, declared Networked INetworkStructs can use the ref keyword,
  // which allows direct modification of members without needing to work with copies.
  [Networked]
  public ref NetworkStructExample NetworkedStructRef => ref MakeRef<NetworkStructExample>();

  // You can also declare Networked structs normally,
  // but be aware the getter will return a copy and not a reference.
  [Networked]
  public NetworkStructExample NetworkedStruct { get; set; }

  public override void Spawned()
  {
    NetworkedStruct.IntField = 5;
    Debug.Log(NetworkedStruct.IntField); // prints default value (0) and not 5.

    NetworkedStructRef.IntField = 5;
    Debug.Log(NetworkedStructRef.IntField); // prints 5
  }
}

Simple Blittable Value Types

Non-float blittable primitives can just be declared as basic fields. No extra coding or attributes are required. For float types, or Unity structs which are float based (such as Vector3) refer to the Float Types section.

C#

using Fusion;
using UnityEngine;


public class NetworkStructSampleCode : NetworkBehaviour
{
  public struct NetworkStructExample : INetworkStruct
  {
    // Non-float primitive structs and Enum types, can be used normally as a field.
    // Except bools, which are non-blittable and cannot be used as fields.
    public SnapAxis   EnumField;
    public int        IntField;
    public Color32    Color32Field;
    public Vector2Int VectorIntField;
  }

Float Types

Float types (primitives and common Unity float-based types which wrap floats) can be used as fields. However, when used as a field no compression handling will be generated by the Fusion ILWeaver.

In order to automatically compress these float types, use the [Networked] attribute on a property with a default implementation. Fusion’s ILWeaver will replace the implementation with compression handling.

C#

using Fusion;
using UnityEngine;
public class NetworkStructSampleCode : NetworkBehaviour
{
  public struct NetworkStructExample : INetworkStruct
  {
  // Recognized float-based Networked Properties will implement Fusion's compression.
  // Such as Float, Double, Vector2, Vector3, Color, Matrix etc.
  // Note: an auto-implemented property is allowed here.
  [Networked]
  public Vector3 CompressedVector { get; set; }

  [Networked]
  public float   CompressedFloat { get; set; }

  // Float types declared as fields will be uncompressed and will replicate bit for bit.
  // Typically you want compression, so this handling should not be used in most cases.
  public Vector3 UncompressedVector;
  public float   UncompressedFloat;
}

Bools

Bools are non-blittable, therefore the bool type cannot be safely used for fields. Fusion provides a NetworkBool type which can be used interchangeably with bool, which requires less code to implement than a bool property, and is blittable.

C#

using Fusion;
using UnityEngine;

public class NetworkStructSampleCode : NetworkBehaviour
{
  public struct NetworkStructExample : INetworkStruct
  {
    // The preferred way to network a boolean is to use the NetworkBool type.
    public NetworkBool NetworkBool;

    // [Networked] is required for the primitive bool in order to instruct
    // the ILWeaver to create the required backing int and implementation.
    // Note that we do NOT declare this as an auto-implemented property,
    // but instead with an empty getter/setter.
    [Networked]
    public bool PrimitiveBool { get => default; set { } }

 }

The booltype may be used for properties as long as they are NOT auto-implementing.

Strings

String and Char types are non-blittable, so the string type may not be used safely for fields, but may be used for non-auto-implementing properties.

Fusion provides a NetworkString<_size> type which can be used interchangeably with string, which produces fewer allocations, is more performant, and requires less code to implement than a string property.

C#

using Fusion;
using UnityEngine;

public class NetworkStructSampleCode : NetworkBehaviour
{
  public struct NetworkStructExample : INetworkStruct
  {
    // The easiest and cleanest way to use a string in an INetworkStruct
    // is to use the Fusion NetworkString<> type as a field.
    // _16 here indicates that we are allocating a capacity of 16 characters.
    public NetworkString<_16> Name;

    // Optionally a regular string can be used as a Property (not a field)
    [Networked]       // Notifies the ILWeaver to extend this property
    [Capacity(16)]    // allocates memory for 16 characters
    [UnityMultiline]  // Optional attribute to force multi-line in inspector.
    public string StringProperty { get => default; set { } }
  }

  // For convenience, declared Networked INetworkStructs can use the ref keyword,
  // which allows direct modification of members without needing to deal with copies.
  [Networked]
  public ref NetworkStructExample NetworkedStructRef => ref MakeRef<NetworkStructExample>();

  public override void Spawned()
  {
    NetworkedStructRef.Name = "John Conner";
  }
}

Network Collections

For details on using Network Collections, refer to the Network Collections page.

C#

using Fusion;
using UnityEngine;

public class NetworkStructSampleCode : NetworkBehaviour
{
  public struct  NetworkStructExample : INetworkStruct
  {
    // Network Collections must be NOT be declared as auto-implemented properties,
    // and with only a default getter (the IL Weaver will replace the getter).
    [Networked, Capacity(16)]
    public NetworkDictionary<int, NetworkString<_4>> DictOfStrings => default;
  }

  // For convenience, declared Networked INetworkStructs can use the ref keyword,
  // which allows direct modification of members without needing to work with copies.
  [Networked]
  public ref NetworkStructExample NetworkedStructRef => ref MakeRef<NetworkStructExample>();

  public override void Spawned()
  {
    NetworkedStructRef.DictOfStrings.Set(1, "One");
    NetworkedStructRef.DictOfStrings.Set(4, "Four");

    Debug.Log($"Values Set: " +
              $"1:{NetworkedStructRef.DictOfStrings.Get(1)} " +
              $"4:{NetworkedStructRef.DictOfStrings[4]}");
  }
}

Fusion Object and ID Types

While an INetworkStruct cannot resolve Fusion objects by their Id (it is unaware of the associated NetworkRunner), it can still store those Ids. Finding references must be done in the NetworkBehaviour, as the NetworkRunner instance is required to resolve these lookups.

C#

using Fusion;
using UnityEngine;

public class NetworkStructSampleCode : NetworkBehaviour
{
  public struct NetworkStructExample : INetworkStruct
  {
    // Fusion Object and Ref ID types are backed by integers,
    // so they are blittable and can be used as basic fields.
    public NetworkId          NetworkIdField;
    public NetworkBehaviourId NetworkBehaviourIdField;
    public PlayerRef          PlayerRefField;
  }

  [Networked]
  public ref NetworkStructExample NetworkedStructRef => ref MakeRef<NetworkStructExample>();


  public override void Spawned()
  {
    // Capture this NetworkBehaviour's Id.
    NetworkedStructRef.NetworkBehaviourIdField = this;
  }

  public override unsafe void FixedUpdateNetwork()
  {
    // Look up the NetworkObject on the Runner using the networked ID.
    Runner.TryFindBehaviour(NetworkedStructRef.NetworkBehaviourIdField, out var nb);
    Debug.LogWarning($"NBID: {(nb == null ? "Null NB" : nb.Id.ToString())}");
  }
}

INetworkStruct Nesting

Since structs which implement INetworkStruct are blittable, they can be used as fields inside other INetworkStruct definitions.

C#

using Fusion;
using UnityEngine;

public class NetworkStructSampleCode : NetworkBehaviour
{
  public struct InnerStruct : INetworkStruct
  {
    public int IntField;
  }

  public struct OuterStruct: INetworkStruct
  {
    public InnerStruct Inner;
  }

  [Networked]
  public ref OuterStruct Outer => ref MakeRef<OuterStruct>();
}

OnChanged with Nested INetworkStructs

The OnChanged callback for nested INetworkStructs detects changes in any change the the entire struct, and individual values cannot be monitored for changes. If you need a callback for individual fields/properties, keep that variable in the NetworkBehaviour as its own Networked Property.

C#

using Fusion;
using UnityEngine;

public class NetworkStructSampleCode : NetworkBehaviour
{

  public struct NetworkStructExample : INetworkStruct
  {
    public NetworkBool NetworkBool;
  }

  [Networked(OnChanged = nameof(OnNetworkStructChanged))]
  public ref NetworkStructExample NetworkedStructRef => ref MakeRef<NetworkStructExample>();

  // Note that OnChanged monitors any changes to the ENTIRE INetworkStruct,
  // so this callback will be triggered by ANY changes to the struct.
  public static void OnNetworkStructChanged(Changed<NetworkStructSampleCode> changed)
  {
    Debug.LogWarning($"Bool changed {changed.Behaviour.NetworkedStructRef.NetworkBool}");
  }

  public override unsafe void FixedUpdateNetwork()
  {
    // Toggle the nested bool value every tick, to trigger the OnChanged callback.
    NetworkedStructRef.PrimitiveBool = !NetworkedStructRef.NetworkBool;
  }
}

Fixed-Size Buffers (Unsafe)

Unsafe fixed-size buffers are valid, as they have a strictly defined memory footprint and alignment.

C#

public struct NetworkStructExample : INetworkStruct
{
  // Fixed is allowed, and can be used for advanced custom unsafe handling.
  public unsafe fixed byte FixedArray[32];
}
Back to top