Networked State
Networked state is the data of the game that you want to replicate to players. In Netick, networked state is delta compressed, therefore only changes are replicated. If a field of a struct changes, only that field is replicated. If a counter increases, only the delta to the previous value is replicated. If your counter was at 32534536, and now it is at 32534537, it will be replicated as a delta of one. It applies to vectors and quaternions too. Thus, using as little bandwidth as possible.
Every networked variable can be predicted and interpolated too. Allowing you to create complex networked systems easily.
Network Properties
A network property is a C# property which is replicated across the network. For a property to be networked, the attribute [Networked] must be added to it.
Examples of networked properties:
[Networked]
public int Health { get; set; }
[Networked]
public float Speed { get; set; }
[Networked]
public Vector3 Velocity { get; set; }
[Networked]
public Angle Pitch { get; set; } // `Angle` is a helper struct that represents an angle in degrees, which automatically wraps to [0, 360) range. This type supports automatic interpolation.
[Networked]
public int Ammo { get; set; }
[Networked]
public NetworkBool IsAlive { get; set; }
[Networked]
public NetworkString32 Name { get; set;}
Warning
Reference types are not networkable.
Warning
If you are intending on building your game using IL2CPP, you must use NetworkBool instead of bool.
Note
Don't use types with sizes lower than 4 bytes such as byte or short, instead simply use int. Netick already compresses everything to only the required bits of the current value of a variable. So if an int variable current value is 5, it will only be serialized as a few bits, not anywhere near 4 bytes (the byte size of int).
Network Arrays
Network arrays are just like regular C# arrays, but their syntax is a bit different. They are defined using the NetworkArray<T> generic class.
Example of a network array:
[Networked(size: 5)]
public readonly NetworkArray<int> IntArrayExample = new NetworkArray<int>(5) { 55, 66, 77 };
Warning
size of [Networked(size: 32)] must be the same as the value that is passed to the array constructor new NetworkArray<int>(32).
Network Array Structs
Netick 2 introduces a new type of network array, network arrays that are completely value types - Network Array Structs. These are fixed-size struct arrays available only in 4 fixed sizes: 8, 16, 32, and 64.
Network Array Structs are pretty useful since they can used as members of another struct, or even nested inside other arrays. In addition, they can be sent as RPC parameters.
// Network Array Struct Examples
[Networked]
public NetworkArrayStruct8<int> IntFixedArray { get; set; } = new int[] {1 , 4 ,5}.ToNetworkStructArray8();
[Networked]
public NetworkArrayStruct8<NetworkArrayStruct8<int>> ArrayOfArrays { get; set; };
Note
Network Array Structs are treated as if they were simple struct types like int or float, so they must be defined as a property not as a field (like NetworkArray<T> that is non-fixed size).
Changing Elements of Network Array Struct
Because Network Array Structs are structs, the whole array will be replaced even when you change a single element. To avoid bugs, this should be how you change array elements:
IntFixedArray = IntFixedArray.Set(index, value);
// as you can see, we are reassigning the property with the new changed array which has the change.
Network Collections
In addition to NetworkArray<T>, Netick also has alternatives to C# collections that are fully synced, predicted, and interpolated.
NetworkLinkedList<T>NetworkDictionary<TKey,TValue>NetworkHashSet<T>NetworkUnorderedList<T>NetworkStack<T>NetworkQueue<T>
In terms of bandwidth usage, the most expensive collection is NetworkDictionary, while the cheapest is NetworkUnorderedList (excluding NetworkArray). The order from the most expensive to the cheapest is: NetworkDictionary > NetworkLinkedList > NetworkHashSet > NetworkQueue > NetworkStack > NetworkUnorderedList. However, note that this only relates to the bandwidth usage when adding/removing elements, as all collections (or any networked variable) use no bandwidth or CPU time when they're idle and not changing.
Usage examples:
[Networked(size: 16)]
public readonly NetworkDictionary<int, int> MyNetworkDictionary = new(16);
[Networked(size: 16)]
public readonly NetworkHashSet<int> MyNetworkHashSet = new(16);
[Networked(size: 16)]
public readonly NetworkLinkedList<int> MyNetworkLinkedList = new(16);
[Networked(size: 16)]
public readonly NetworkUnorderedList<int> MyNetworkUnorderedList = new(16);
[Networked(size: 16)]
public readonly NetworkStack<int> MyNetworkedStack = new(16);
[Networked(size: 16)]
public readonly NetworkQueue<int> MyNetworkedQueue = new(16);
Removing and adding elements is the same as with C# collections.
Note
The size that you pass to [Networked] and the constructor represents the fixed capacity of the collection. The collections don't support resizing as all network state sizes are set at compile time.
Network Structs
Netick can synchronize any struct that does not contain class-based arrays or references. Which includes all C# primitive types and Unity/Godot/Flax primitive types.
Example:
[Networked]
public struct MyNestedStruct
{
public int Int;
public NetworkBool Bool;
[Networked]
public float Float { get; set;}
[Networked]
public Vector3 Position { get; set;}
[Networked]
public Quaternion Rotation { get; set;}
[Networked]
public Color Color { get; set;}
public NetworkString8 Name;
}
[Networked]
public struct MyStruct
{
public MyNestedStruct MyNestedStruct;
public NetworkArrayStruct8<int> StructArray;
public int Int;
public NetworkBool Bool;
[Networked]
public float Float { get; set;}
}
[Networked]
public MyStruct MyStructProperty {get; set;}
Note
string is not supported as a type that can be used inside a struct. Use NetworkString instead.
Note
[Networked] attribute on structs is optional. However, when adding it to a struct, it allows float-based types (such as float or Vector3, which also have [Networked] on them) of a struct to have extra compression on them.
Networking References to NetworkObject and NetworkBehaviour
Since you can't directly synchronize class references, we provide two helper structs that are used to synchronize a reference to NetworkObject and NetworkBehaviour:
NetworkObjectRef
Usage Example:
public class PlayerController : NetworkBehaviour
{
[Networked]
public NetworkObjectRef MyPlayer { get; set;}
public override void NetworkStart()
{
// assigning the ref
MyPlayer = Object.GetRef();
}
public void ExampleOfUsingTheRef()
{
// getting the object from the ref
var netObj = MyPlayer.GetObject(Sandbox); // or TryGetObject
}
}
NetworkBehaviourRef
Usage Example:
public class PlayerController: NetworkBehaviour
{
[Networked]
public NetworkBehaviourRef<PlayerController> MyPlayer { get; set;}
public override void NetworkStart()
{
// assigning the ref
MyPlayer = this.GetRef<PlayerController>;
}
public void ExampleOfUsingTheRef()
{
// getting the behaviour from the ref
var playerController = MyPlayer.GetBehaviour(Sandbox); // or TryGetBehaviour
}
}
Replication Relevancy
You can choose to replicate a property only to the Input Source client of the object. This is done using the optional parameter relevancy of [Networked].
Example:
[Networked(relevancy: Relevancy.InputSource)]
public int Ammo {get; set;}
State Synchronization
Updates to the network state are atomic, it's not possible for a property to update in the client without other changed properties to update alongside it. If you change two (or more) properties in the server at the same time (or in two subsequent ticks), you are ensured to have both replicate together in the client. This makes it so that you don't have to worry about packet loss and possible race conditions that might occur due to some properties updates arriving while others arriving later. This simplifies how you program your game as you never have to worry about such things happening.
This also means that when you create an object in the server, assign some initial values to some network properties, when this object is created in the client, inside NetworkStart of that object you will have full initial state that you assigned in the server.