Coming from Netick 1 (Unity)
This is a guide to help you migrate from Netick 1 to Netick 2, for Unity users. It shows you what has changed in Netick 2 and it also shows you many of the new features that Netick 2 brings to your toolset.
First of all, please make a back-up copy of your project. Then carefully read each section of this article. If you need help, please feel free to join our discord.
Importing Netick 2
Assuming you have already downloaded Netick 2 package, delete the root folder of Netick 1 from your project, which is located at Assets/Netick. After that, simply unpack/copy Netick 2 into your project. It is recommended to do this in your operating system's File Explorer instead of Unity Project Panel.
Project Settings
Go to Project Settings -> Player -> Other Settings and change these settings to be as follows:
- Allow 'unsafe' code:
true - Api compatibility level:
.NET Standard 2.1
API Naming Changes:
| Netick 1 | Netick 2 |
|---|---|
| NetworkSandbox.GetRpcCaller | NetworkSandbox.CurrentRpcCaller |
| NetworkSandbox.RpcSource | NetworkSandbox.CurrentRpcSource |
| NetworkEventsListner | NetworkEventsListener |
| NetworkBehaviour.ApplyToBehaviour | NetworkBehaviour.GameEngineIntoNetcode |
| NetworkBehaviour.ApplyToComponent | NetworkBehaviour.NetcodeIntoGameEngine |
| NetHit | LagCompHit |
Game Starter
Now the transport is specified when starting Netick and not using NetickConfig. A field has been added to GameStarter for that.
Network Events Listener
A parameter for disconnection reason (TransportDisconnectReason) has been added to OnClientDisconnected:
public override void OnClientDisconnected(NetworkSandbox sandbox, NetworkConnection client)
{
}
Network Behaviour
Add using Netick.Unity to every script that you have which inherits from NetworkBehaviour.
using Netick;
using Netick.Unity;
public class MyScript : NetworkBehaviour
{
...
}
Network Input
Network inputs are now structs instead of classes, which makes it easy to sync them as network properties if needed.
public class MyInput : NetworkInput
{
public bool ShootInput;
public float MoveDirX, MoveDirY;
}
Because they are now value types instead of reference types, this means the previous method of populating them won't work anymore. Instead, you have to use another call to update the input.
public override void OnInput(NetworkSandbox sandbox)
{
var input = sandbox.GetInput<BombermanInput>();
input.Movement = GetMovementDir();
input.PlantBomb |= Input.GetKeyDown(KeyCode.Space);
}
OnChanged
Now OnChanged methods must have a parameter of OnChangedData type which can be used to retrieve the previous property value:
[Networked]
public int Health { get; set; }
[OnChanged(nameof(Health ))]
private void OnHealthChanged(int previous)
{
// Something that happens when the Health property changes
}
It also now supports retrieving previous array values:
[Networked(size: 32)]
public NetworkArray<int> ArrayExample = new NetworkArray<int>(32);
[OnChanged(nameof(IntArray))]
private void OnArrayExampleChanged(OnChangedData onChangedData)
{
// getting the changed element value directly
var changedPreviousElementValue = onChangedData.GetArrayPreviousElementValue<int>();
// or just getting the index
var changedPreviousElementIndex = onChangedData.GetArrayChangedElementIndex();
// or maybe getting the previous value of another index we want
var someRandomPreviousElementValue = onChangedData.GetArrayPreviousElementValue<int>(13);
}
Behavioral Change
[OnChanged] methods now will be called for all non-default initialization values - property definition assignments and inspector values. And this happens for the first time when the object is first created, before NetworkStart is called. So if you try to access a class instance variable inside the [OnChanged] method which is initialized inside NetworkStart, it can cause a null reference exception - because NetworkStart is invoked after [OnChanged] method, not before. To fix this, transfer all class instance variables initialization into NetworkAwake (which is called before the first [OnChanged] ever).
Network Arrays
Network arrays syntax has changed a little bit. They are now field members instead of property members.
Warning
Regarding network arrays for Netick 2: size of [Networked(size: 32)] must be the same as the value that is passed to the array constructor new NetworkArray<int>(32)
As you can see, it's now possible to have initialization values for network arrays.
Network Array Struct
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.
// Network Struct Array 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 normal NetworkArray 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 Structs
Now all structs are networked by default, so you don't need to add [Networked] to them or even implement custom equality. You no longer have a limit size for a single struct too. You can also now have nested structs. So this works as expected:
public struct MyNestedStruct
{
public int Int1;
public bool Bool1;
public float Float1;
public double Double1;
}
public struct MyStruct
{
public MyNestedStruct MyNestedStruct;
public NetworkArrayStruct8<int> StructArray;
public int Int1;
public bool Bool1;
public float Float1;
public double Double1;
}
[Networked]
public MyStruct MyStructProperty {get; set;}
Input Source
Now, to change the input source of an object you do that directly using the InputSource property setter:
// assigning an input source to network object:
Object.PermitInput(myNewInputSource);
// removing the input source from the object:
Object.RevokeInput();
Callbacks of NetworkBehaviour, OnInputPermitted and OnInputRevoked, have been removed and replaced by one single callback:
public override void OnInputPermitted()
{
// called on the InputSource machine when InputSource is now equal to this machine.
}
public override void OnInputRevoked()
{
// called on the InputSource machine when this machine is no longer the InputSource.
}
Rpcs
At this moment in time, string is not supported as a parameter to Rpcs. Instead, fixed-size structs can be used:
Now, you can have static Rpcs on NetworkBehaviour classes which can be pretty useful.
[Rpc]
public static void MyStaticRpc(NetickEngine engine, int someRpcPara)
{
var sandbox = engine.UserObject as NetworkSandbox;
}
// this is how you would call the rpc:
MyStaticRpc(Sandbox.Engine, 56);
Note that they must have a NetickEngine as the first parameter.
Lag Compensation
LagCompensation component class has been removed.
Interpolation
Interpolator is now an non-generic struct.
To find an Interpolator, now you simply use the name of the property instead of using an Id.
[Networked][Smooth(6)]
public MyType SomeProperty {get; set;}
public override void NetworkStart()
{
var interpolator = FindInterpolator<MyType>(6);
}
Also, now [Smooth] takes a parameter to specify if it should give auto-interpolated values inside NetworkRender or not, by specifying a true or false value for auto parameter of [Smooth].
Accessing Interpolation Data
To get interpolation data, now instead of using To, From, and Alpha fields of Interpolator, you use GetInterpolationData method of Interpolator struct:
bool didGetData = interpolator.GetInterpolationData<int>(InterpolationMode.Auto, out var from, out var to, out float alpha);
It also now supports getting interpolation data for network arrays:
int myIndex = 4;
bool didGetData = interpolator.GetInterpolationData<int>(InterpolationMode.Auto, myIndex, out var from, out var to, out float alpha);
Replication
Netick 2 introduces a new replication method called Pessimistic Replication (in contrast to Optimistic Replication, which was the only replication method in Netick 1), which ensures that the client always receives the full state together, not partial, but always the full state. In addition, this new replication method uses delta encoding to highly reduce the bandwidth required. This replication method eliminates the burden of having to account for the potential bugs caused by not always having the entire changed state together using Optimistic Replication.
As of now, this is the default and only replication method. But the old Optimistic Replication will come back later in the future. Pessimistic Replication as of now works with AoI by disabling delta encoding, but this will change in the future. When that happens, Pessimistic Replication will be better than Optimistic Replication for almost every single situation. This is why it has not been a priority to make Optimistic Replication present in Netick 2 from the start.
Network Transport
Note
If you are not a transport or a transport wrapper developer, you can ignore this section.
In Netick 1, your network transport main script was inheriting from NetworkTransport, which by itself was inheriting from ScriptableObject. But now that's not possible anymore, since ScriptableObject is a Unity class.
Now, NetworkTransport does not inherit from ScriptableObject, which means you no longer can have assets on your project representing a transport like in Netick 1.
To solve this, a wrapper class has been added NetworkTransportProvider, which inherits from ScriptableObject and wraps the network transport:
[CreateAssetMenu(fileName = "LiteNetLibTransportProvider", menuName = "Netick/Transport/LiteNetLibTransportProvider", order = 1)]
public class LiteNetLibTransportProvider : NetworkTransportProvider
{
public override NetworkTransport MakeTransportInstance() => new LiteNetLibTransport();
}
MakeTransportInstance is called by Netick to create an instance of the transport.
Netick now only receives data in the form of BitBuffer. BitBuffer.SetFrom is used to set a pointer to the data which BitBuffer will use. Take a look at the new LiteNetLib transport to understand how it all works.
public unsafe void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod)
{
if (_clients.TryGetValue(peer, out var c))
{
var len = reader.AvailableBytes;
reader.GetBytes(_bytes, 0, reader.AvailableBytes);
fixed(byte* ptr = _bytes)
{
_buffer.SetFrom(ptr, len, _bytes.Length);
NetworkPeer.Receive(c, _buffer);
}
}
}