前言
之前使用BepInEx开发游戏模组时 发现使用自定义网络物体无法使用ClientRpc和ServerRpc函数 于是在网上查了很多资料终于找到了解决方法
原因
由于Unity网络库Netcode在编译时 会自动Patch代码 将ClientRpc/ServerRpc加上网络通信代码
没修补之前
using System;
using Unity.Netcode;
using UnityEngine;
namespace Test
{
// Token: 0x02000003 RID: 3
public class Test : NetworkBehaviour
{
// Token: 0x06000002 RID: 2 RVA: 0x00002050 File Offset: 0x00000250
[ClientRpc]
public void ServerToClient_ClientRpc(int parameter1, int parameter2)
{
Debug.Log("ClientRpc");
}
// Token: 0x06000003 RID: 3 RVA: 0x0000205C File Offset: 0x0000025C
[ServerRpc(RequireOwnership = false)]
public void ClientToServer_ServerRpc(int parameter1, int parameter2)
{
Debug.Log("ServerRpc");
}
}
}
修补之后
using System;
using Unity.Netcode;
using UnityEngine;
namespace Test
{
// Token: 0x02000003 RID: 3
public class Test : NetworkBehaviour
{
// Token: 0x06000003 RID: 3 RVA: 0x0000205C File Offset: 0x0000025C
[ClientRpc]
public void ServerToClient_ClientRpc(int parameter1, int parameter2)
{
NetworkManager networkManager = base.NetworkManager;
if (networkManager == null || !networkManager.IsListening)
{
return;
}
if (this.__rpc_exec_stage != 2 && (networkManager.IsServer || networkManager.IsHost))
{
ClientRpcParams clientRpcParams;
FastBufferWriter fastBufferWriter = base.__beginSendClientRpc(961691156U, clientRpcParams, 0);
BytePacker.WriteValueBitPacked(fastBufferWriter, parameter1);
BytePacker.WriteValueBitPacked(fastBufferWriter, parameter2);
base.__endSendClientRpc(ref fastBufferWriter, 961691156U, clientRpcParams, 0);
}
if (this.__rpc_exec_stage != 2 || (!networkManager.IsClient && !networkManager.IsHost))
{
return;
}
Debug.Log("ClientRpc");
}
// Token: 0x06000004 RID: 4 RVA: 0x00002148 File Offset: 0x00000348
[ServerRpc(RequireOwnership = false)]
public void ClientToServer_ServerRpc(int parameter1, int parameter2)
{
NetworkManager networkManager = base.NetworkManager;
if (networkManager == null || !networkManager.IsListening)
{
return;
}
if (this.__rpc_exec_stage != 1 && (networkManager.IsClient || networkManager.IsHost))
{
ServerRpcParams serverRpcParams;
FastBufferWriter fastBufferWriter = base.__beginSendServerRpc(1806026893U, serverRpcParams, 0);
BytePacker.WriteValueBitPacked(fastBufferWriter, parameter1);
BytePacker.WriteValueBitPacked(fastBufferWriter, parameter2);
base.__endSendServerRpc(ref fastBufferWriter, 1806026893U, serverRpcParams, 0);
}
if (this.__rpc_exec_stage != 1 || (!networkManager.IsServer && !networkManager.IsHost))
{
return;
}
Debug.Log("ServerRpc");
}
// Token: 0x06000006 RID: 6 RVA: 0x0000223C File Offset: 0x0000043C
protected override void __initializeVariables()
{
base.__initializeVariables();
}
// Token: 0x06000007 RID: 7 RVA: 0x00002252 File Offset: 0x00000452
[RuntimeInitializeOnLoadMethod]
internal static void InitializeRPCS_Test()
{
NetworkManager.__rpc_func_table.Add(961691156U, new NetworkManager.RpcReceiveHandler(Test.__rpc_handler_961691156));
NetworkManager.__rpc_func_table.Add(1806026893U, new NetworkManager.RpcReceiveHandler(Test.__rpc_handler_1806026893));
}
// Token: 0x06000008 RID: 8 RVA: 0x0000228C File Offset: 0x0000048C
private static void __rpc_handler_961691156(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams)
{
NetworkManager networkManager = target.NetworkManager;
if (networkManager == null || !networkManager.IsListening)
{
return;
}
int parameter;
ByteUnpacker.ReadValueBitPacked(reader, ref parameter);
int parameter2;
ByteUnpacker.ReadValueBitPacked(reader, ref parameter2);
target.__rpc_exec_stage = 2;
((Test)target).ServerToClient_ClientRpc(parameter, parameter2);
target.__rpc_exec_stage = 0;
}
// Token: 0x06000009 RID: 9 RVA: 0x00002300 File Offset: 0x00000500
private static void __rpc_handler_1806026893(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams)
{
NetworkManager networkManager = target.NetworkManager;
if (networkManager == null || !networkManager.IsListening)
{
return;
}
int parameter;
ByteUnpacker.ReadValueBitPacked(reader, ref parameter);
int parameter2;
ByteUnpacker.ReadValueBitPacked(reader, ref parameter2);
target.__rpc_exec_stage = 1;
((Test)target).ClientToServer_ServerRpc(parameter, parameter2);
target.__rpc_exec_stage = 0;
}
// Token: 0x0600000A RID: 10 RVA: 0x00002373 File Offset: 0x00000573
protected internal override string __getTypeName()
{
return "Test";
}
}
}
问题显而易见
解决方法
1, 使用 UnityNetcodePatcher (直接修补DLL)
使用之前需要先将调试信息改为嵌入式或可移植
在你模组的Awake函数中添加:
/* 用来加载NetworkBehaviour初始化函数 */
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);
}
}
}
如果你用的是 Visual studio 就打开dotnet终端输入 不是的话就去releases下载
dotnet tool install -g Evaisa.NetcodePatcher.Cli
netcode-patch [你生成的模组DLL文件位置] [依赖文件夹位置]
依赖文件夹用游戏的Managed文件夹就行了
然后没问题的话就会生成两个文件 带original结尾的就是源文件 不带original结尾的文件就是修补好的
2, 使用 RuntimeNetcodeRPCValidator (不需要修补DLL)
// 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>();
}
}
}
将 PluginNetworkingInstance 替换为你自定义的NetworkBehaviour
将 Terminal 替换为游戏存在的GameObject
PS: 这个是用于Lethal Company游戏的代码我也不知道其他游戏能不能用 :P