Global Messaging
DISCLAIMER
This is not a tutorial on how to use Unity's Netcode for GameObjects RPCs and Network Variables. This is only meant to be used to understand how to implement custom networking into the game.
There are several different methods available to you for messaging between the host and any connected clients in a global fashion. You can use an API, use Netcode for GameObjects' (NGO) Remote Procedure Calls, or use NGO's Network Messages.
NOTE
This article only takes a look at how these methods are used in a static or singleton nature, where you may want to set game settings, log changes on all clients, or set off events that impact the entire game/lobby.
For information on networking for instanced objects, like enemys, items, or traps, look at the Object Behaviour article.
APIs
INFO
For specific information on the available APIs, you can look at the networking APIs section of this wiki.
This is an easy method of messaging between the host and clients. The available APIs already implement NGO in the context of the game, and thus only need to be implemented in the context of your mod.
These APIs generally are able to work outside of the context of a NetworkBehaviour class, unlike that of an RPC. This allows you to access it anywhere in your code, but may cause additional runtime errors if using while not connected to a lobby/game instance.
NGO's Remote Procedure Calls (RPCs)
RPCs are the standard and recommended solution. They give you more control than APIs likely will, and they are significantly easier to use than Custom Messages.
However, they only exist in the context of NetworkBehaviours, and you must use a Netcode Patcher in order to use them at runtime.
Installing UnityNetcodePatcher
- Add the following code to your
Plugin::Awakemethod:
private void Awake()
{
var types = Assembly.GetExecutingAssembly().GetTypes();
foreach (var type in types)
{
var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (var method in methods)
{
var attributes = method.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), false);
if (attributes.Length > 0)
{
method.Invoke(null, null);
}
}
}
}- Install the tool with:
$ dotnet tool install -g Evaisa.NetcodePatcher.Cli- Add the following code to your
.csproj:
<Target Name="NetcodePatch" AfterTargets="PostBuildEvent">
<Exec Command="netcode-patch -uv 2022.3.62 -nv 1.12.0 -tv 1.0.0 "$(TargetPath)" @(ReferencePathWithRefAssemblies->'"%(Identity)"', ' ')"/>
</Target>Editing the .csproj
To modify the .csproj file, there are a few different methods possible. The first option is to open the file in a text editor - such as Notepad++. You can also modify the file in your IDE, which can be opened by either pressing F4 when the project is selected in the solution explorer, or by right-clicking the project in the solution explorer and selecting Edit Project.
Updating UnityNetcodePatcher from before v73
If you previously used Unity Netcode Patcher or have it installed, before patching for v73 and later, ensure you have the latest version of UNP.
The maintainer of that project recommends uninstalling and reinstalling the package globally to ensure there's not conflicting files and is up-to-date:
$ dotnet tool uninstall -g Evaisa.NetcodePatcher.Cli
$ dotnet tool install -g Evaisa.NetcodePatcher.CliAdditionally, ensure you are using the CLI/post-build event correctly with the new parameters:
$ netcode-patch -uv 2022.3.62 -nv 1.12.0 -tv 1.0.0 ...<Target Name="NetcodePatch" AfterTargets="PostBuildEvent">
<Exec Command="netcode-patch -uv 2022.3.62 -nv 1.12.0 -tv 1.0.0 "$(TargetPath)" @(ReferencePathWithRefAssemblies->'"%(Identity)"', ' ')"/>
</Target>As RPCs can only exist in the context of a NetworkBehaviour, much of what you will have to do is equivalent to the article on Custom Object Behaviours. As such, this article will only go into the specifics for global messaging. For information on Network Behaviours and what you can use in them, look at the Custom Object Behaviour article or the NGO Docs.
StaticNetcodeLib
An alternative to NGO's instanced RPCs are the RPCs provided by StaticNetcodeLib. These RPCs are functionally the same as NGO's RPCs, except that they are static in nature, and can be used outside of NetworkBehaviours. Thus, they do not need to be attached to a game object that is instantiated.
For more information, look at the wiki article for StaticNetcodeLib.
Singleton
Although it depends on your use case, you will likely want to use a "singleton" game object for your network messages. This is due to the fact that you only want one instance sending/receiving messages between clients - to prevent methods unintentionally running multiple times.
In your class that extends NetworkBehaviour, you can use a simple Singleton/Instance pattern, like so:
internal class MyNetworkBehaviour : NetworkBehaviour
{
internal MyNetworkBehaviour Instance { get; private set; }
public override void OnNetworkSpawn()
{
if (Instance != null)
{
Destroy(this);
return;
}
Instance = this;
}
}Attaching to a Network Prefab
There are a few ways of going about this. The easiest way, without a lib, is to use asset bundles from Unity as described here in the Custom Object Behaviours article. The other way is to do so programmatically.
There are two options for this, to do so through an API - LethalLib provides a CreateNetworkPrefab method - or to do it manually. Doing it manually requires that you publicize the Unity.Netcode.Runtime assembly or use reflection.
WARNING
You can only register Network Prefabs when both the NetworkManager exists and the player is not in a lobby.
LethalLib will handle this for you as long as you register/create your Network Prefab in your mod's Awake method.
LethalLib
To make a network prefab, you can do the following method in your mod's Awake method:
myNetworkPrefab = NetworkPrefabs.CreateNetworkPrefab("MyNetworkPrefab");Once you have the prefab, you just have to add your NetworkBehaviour component to the prefab. Doing so is as simple as the following:
myNetworkPrefab.AddComponent<MyNetworkBehaviour>();Manually
To manually create a network prefab programmatically, you must do so after the NetworkManager is instantiated (postfix GameNetworkManager::Start) and deal with a few issues. To do so, you can do the following:
INFO
- Make a new GameObject
- Set it's
HideFlagstoHideAndDontSave - Parent said object to a disabled "container" that has the
HideAndDontSaveHideFlag - Attach a NetworkObject component
- Attach the Custom Network Behaviour component
- Change the
GlobalObjectIdHashto a new uint GUID - Register the GameObject as a Network Prefab
To shorten this article, only step 6 will be discussed due to it's complexity and undocumented nature.
You must change the GlobalObjectIdHash as it is 0 by default, and leaving it as so will cause conflicts with any mod that unknowingly has its object id set to 0.
A simple - though not infallible - way of doing this is by hashing your mod's guid & object name into a uint. This can be done like so:
var objectIdHash = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes($"{ExampleMod.Info.Metadata.GUID}.MyNetworkPrefab"));
myNetworkPrefab.GetComponent<NetworkObject>()!.GlobalObjectIdHash = BitConverter.ToUInt32(objectIdHash);DANGER
Using this method requires publicizing Unity.Netcode.Runtime.dll! The GlobalObjectIdHash is an internal field and thus can only be modified either through reflection or the publicized assembly, the latter of which is much easier to do.
NGO's Custom Messages
The hardest method of messaging between the host and clients is using NGO's Custom Messages. They do have their advantages, however.
- They support vanilla compatibility as they do not have to register custom network objects, unlike RPCs.
- They do not require the use of Eva's Unity Netcode Patcher.
- You can also utilize Network Messages to send longer pieces of information as they have the
NetworkDelivery.ReliableFragmentedSequencedoption.
For examples on usage, take a look at NGO's Custom Messages docs. Linked is the named messages heading/section, which is recommended over unnamed messages as using named messages is easier and will help against conflicting with other mods' messaging systems.
This article will only go into the basic concepts necessary for usage, not the actual usage.
Serialization
To transmit data, you have to manually use the FastBufferReader and FastBufferWriter to serialize the data you want to transmit. You can use the pre-built serialization types Unity provides; however, custom classes, structs, and records will likely need custom serialization.
Custom serialization requires that you manually write extensions for FastBufferReader and FastBufferWriter to serialize the data in the class, struct, or record.
For more information, you can read this article.