-
Notifications
You must be signed in to change notification settings - Fork 8
Mod Creation_C# Programming_Networking_R2API.NetworkingAPI
Add a [BepInDependency
attribute to your BaseUnityPlugin
.
// some usings...
namespace MyNamespace.Something
{
// some other attributes...
[BepInDependency(NetworkingAPI.PluginGUID)]
public class MyModPlugin : BaseUnityPlugin
{
// some code...
}
// some other classes...
}
Somewhere in your mod, you add a using R2API.Networking.Interfaces;
in the file where you will implement the INetMessage
interface. You make an empty class in the same file and implement the interface.
using R2API.Networking.Interfaces;
// other code
public class SyncSomething : INetMessage
{
}
If you use Visual Studio, you can just hover over INetMessage
, then click show potential fixes. After that, click Implement Interface
.
The class should now look like this.
public class SyncSomething : INetMessage
{
public void Deserialize(NetworkReader reader)
{
throw new System.NotImplementedException();
}
public void OnReceived()
{
throw new System.NotImplementedException();
}
public void Serialize(NetworkWriter writer)
{
throw new System.NotImplementedException();
}
}
Now you must know what values must sync. In this example, we will play a sound on a player character for the clients. That means I need the CharacterBody
of the player character. You need to send the NetworkInstanceId
of the CharacterBody.gameObject
. For the sake of demonstration, let's sync a bunch of useless stuff.
Now we know that we will sync the Vector3 position
, the useless int number
and the NetworkInstanceId netId
of the CharacterBody.gameObject
. Declare them in the class that implemented the interface.
public class SyncSomething : INetMessage
{
NetworkInstanceId netId;
Vector3 position;
int number;
// the methods...
}
Then you implement Serialize
. Serialize
is the method that will write the variables into the network coming from the caller of the machine. It will be used to send to the target clients or server. Order of writing them is important!
public class SyncSomething : INetMessage
{
// other code
public void Serialize(NetworkWriter writer)
{
writer.Write(netId);
writer.Write(position);
writer.Write(number);
}
}
Then you implement Deserialize
. Deserialize
is the method that handles how to read the data that was received on clients. The order of reading should be the same as the order of how it was written.
public class SyncSomething : INetMessage
{
// variable declarations
public void Deserialize(NetworkReader reader)
{
netId = reader.ReadNetworkId();
position = reader.ReadVector3();
number = reader.ReadInt32();
}
// other code
}
The class should now look like this.
public class SyncSomething : INetMessage
{
NetworkInstanceId netId;
Vector3 position;
int number;
public void Deserialize(NetworkReader reader)
{
netId = reader.ReadNetworkId();
position = reader.ReadVector3();
number = reader.ReadInt32();
}
public void OnReceived()
{
throw new System.NotImplementedException();
}
public void Serialize(NetworkWriter writer)
{
writer.Write(netId);
writer.Write(position);
writer.Write(number);
}
}
You put the logic in OnReceived
. The receiver will fire this method after deserializing. You can use the declared variables like normal. Here is a sample code.
public class SyncSomething : INetMessage
{
NetworkInstanceId netId;
Vector3 position;
int number;
public void Deserialize(NetworkReader reader)
{
netId = reader.ReadNetworkId();
position = reader.ReadVector3();
number = reader.ReadInt32();
}
public void OnReceived()
{
if (NetworkServer.active)
{
MyModPlugin.myLogger.LogMessage("SyncSomething: Host ran this. Skip.");
return;
}
Chat.AddMessage($"Client received SyncSomething. Position received is {position}. Number received is {number}.");
GameObject bodyObject = Util.FindNetworkObject(ownerBodyId);
if (!bodyObject)
{
MyModPlugin.myLogger.LogWarning("SyncSomething: bodyObject is null.");
return;
}
Util.PlaySound("somevanillasoundstring", bodyObject);
}
public void Serialize(NetworkWriter writer)
{
writer.Write(netId);
writer.Write(position);
writer.Write(number);
}
}
}
The constructors are also required for registering your Network Message and for actually using it. You need to make two: one with no arguments, and one with all the arguments based on the declared variables.
public class SyncSomething : INetMessage
{
NetworkInstanceId netId;
Vector3 position;
int number;
public SyncSomething()
{
}
public SyncSomething(NetworkInstanceId netId, Vector3 position, int num)
{
this.netId = netId;
this.position = position;
number = num;
}
// other methods
}
And with that, you should have a working INetMessage
class.
You need to register your INetMessage
first before actually using it. You can do this in your mod plugin's Awake
.
// some usings...
namespace MyNamespace.Something
{
// some attributes
public class MyModPlugin : BaseUnityPlugin
{
private void Awake()
{
// some code...
NetworkingAPI.RegisterMessageType<SyncSomething>();
// more code...
}
// other methods...
}
// some other classes...
}
Let's imagine we have a method where only the server fires the code.
// Somewhere...
public void PlaySoundInServer(CharacterBody body)
{
Util.PlaySound("somesoundstring", body.gameObject);
NetworkIdentity identity = body.gameObject.GetComponent<NetworkIdentity>();
if (!identity)
{
MyModPlugin.myLogger.LogWarning("PlaySoundInServer: The body did not have a NetworkIdentity component!");
return;
}
new SyncSomething(identity.netId, body.gameObject.transform.position, 9000).Send(NetworkDestination.Clients);
}
You managed to sync that random sound... or whatever! Check the console for errors if it fails to sync in-game. Logs will extremely help you.