

A BepInEx plugin utilizing HarmonyX to patch methods labeled with [ServerRpc] or [ClientRpc], inside a specified type that derives from NetworkBehaviour, to run the same format of checks NGO patches in during compile-time.
NetworkBehaviour added to a pre-existing NetworkObject not working?
NetworkBehaviour::Constructor to call a custom method to synchronize with the parent NetworkObject (something that registering a new NetworkObject w/ NetworkPrefab, and .Spawn()ing avoids). To prevent confusion this is now a manual process but is still possible.Runtime is what sets this apart, as it does it's "code insertion" at runtime.INetworkSerializable which usually is fine; However if you want a simple struct or class and writing a whole INetworkSerializable implementation method just for a few variables is too much, then you can mark the object with a [System.Serializable] attribute and RNV will handle serializing it over the network as well as your normal INetworkSerializable parameters.Reference Runtime Netcode RPC Validator by installing the NuGet package from the terminal (in your projects directory):
dotnet add package NicholaScott.BepInEx.RuntimeNetcodeRPCValidator
Add a BepInDependency attribute to your BaseUnityPlugin.
[BepInDependency(RuntimeNetcodeRPCValidator.MyPluginInfo.PLUGIN_GUID, RuntimeNetcodeRPCValidator.MyPluginInfo.PLUGIN_VERSION)]
Instantiate NetcodeValidator: Create and maintain a reference to an instance of NetcodeValidator and call NetcodeValidator.PatchAll(). If, and only if, you wish to revert any patches applied you can call Dispose(), or UnpatchSelf() if you want to keep the instance for re-patching.
Define and Use RPCs: Ensure your Remote Procedure Calls on your NetworkBehaviours have the correct attribute and end their name with ServerRpc/ClientRpc.
For more robust examples check the Github Repo of the UnitTester plugin, which is used during development to verify codebase.
// Example of using NetcodeValidator
namespace SomePlugin {
[BepInPlugin("My.Plugin.Guid", "My Plugin Name", "0.1.1")]
[BepInDependency(RuntimeNetcodeRPCValidator.MyPluginInfo.PLUGIN_GUID, RuntimeNetcodeRPCValidator.MyPluginInfo.PLUGIN_VERSION)]
public class MyPlugin : BaseUnityPlugin {
private NetcodeValidator netcodeValidator;
private void Awake()
{
netcodeValidator = new NetcodeValidator("My.Plugin.Guid");
netcodeValidator.PatchAll();
netcodeValidator.BindToPreExistingObjectByBehaviour<PluginNetworkingInstance, Terminal>();
}
}
}
// Example of using Server or Client RPCs. Naming conventions require the method to end with the corresponding attribute name.
namespace SomePlugin {
public class PluginNetworkingInstance : NetworkBehaviour {
[ServerRpc]
public void SendUsDataServerRpc() {
// Log the received name
Debug.Log(name);
// Tell all clients what the sender told us
TellAllOtherClientsClientRpc(NetworkBehaviourExtensions.LastSenderId, name);
}
[ClientRpc]
public void TellAllOtherClientsClientRpc(ulong senderId, string name) {
Debug.Log(StartOfRound.Instance.allPlayerScripts.First(playerController => playerController.actualClientId == senderId).playerUsername + " is now " + name);
}
[ClientRpc]
public void RunClientRpc() {
// Send to the server what our preferred name is, f.e.
SendPreferredNameServerRpc("Nicki");
}
private void Awake()
{
if (!IsHost) // Any clients should ask for sync of something :shrug:
StartCoroutine(WaitForSomeTime());
}
private IEnumerator WaitForSomeTime()
{
// We need to wait because sending an RPC before a NetworkObject is spawned results in errors.
yield return new WaitUntil(() => NetworkObject.IsSpawned);
// Tell all clients to run this method.
SendUsDataServerRpc();
}
}
}
Utilize the NetworkBehaviourExtensions.LastSenderId property to retrieve the ID of the last RPC sender. This will always be NetworkManager.ServerClientId on the clients.
So you don't wanna make a prefab eh? Don't feel like registering it with the network? Afraid of what might come? Fear no more, as you can bind your NetworkBehaviour to a pre-existing (native) NetworkBehaviour utilizing a method anytime before NetworkManager is initialized. Generally this would be in your Plugins Awake, right after you create and patch with your NetcodeValidator. See the Examples above for usage and below for a detailed signature.
public void NetcodeValidator::BindToPreExistingObjectByBehaviour<TCustomBehaviour, TNativeBehaviour>()
We welcome contributions! If you would like to help improve the RNV, please submit pull requests, and report bugs or suggestions in the issues section of this repository.
Discord: @Day