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
orchar
(useNetworkString
instead) - a
bool
(useNetworkBool
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
- NetworkArray<T> with a maximum
Length
set using the[Capacity]
attribute (defaults to 1) - NetworkDictionary<K, V> with a maximum
Count
set using the[Capacity]
- NetworkLinkedList<T> with a maximum
Count
set using the[Capacity]
attribute. - NetworkString<_size>
- NetworkArray<T> with a maximum
- 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 bool
type 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];
}