跳到主要内容
版本:0.1.0

自定义 RPC

WukongMP SDK 的核心功能之一是支持任意远程过程调用 (RPC) 函数。这些函数可以携带任意有效负载,并支持多种 中继模式

RPC 处理程序允许你定义自己的事件,这些事件将发送给连接到同一服务器的其他玩家。

声明一个 RPC 处理程序类

为了向你的模组添加自定义 RPC 过程,你必须定义一个 partial 类,它继承自 RpcClassBase

最简 RPC 类定义
public partial class MyRpc(IRpcClient client, IRelaySerializer serializer) : RpcClassBase(client, serializer)
{
// 在此处编写 RPC 处理器
}
important

要注册任何 RPC 处理程序,类 必须被添加到 DI 容器中,在你的模组的 Initialize 方法中。请查阅 ModBase 的文档。

定义 RPC 处理程序

为了向你的模组添加一个新的 RPC 处理程序,请添加一个带有 RpcEvent 属性的方法。

方法名必须以“On...”开头——在同一个类中会自动为发送 RPC 生成相应的“Send...”方法。

最简 RPC 处理器
public partial class MyRpc(IRpcClient client, IRelaySerializer serializer) : RpcClassBase(client, serializer)
{
[RpcEvent(RelayMode.AreaOfInterestOthers)]
private void OnMyCustomCall()
{
// 收到此消息时执行的操作
}

// 由 SDK 生成
private void SendMyCustomCall() { ... }
}

该属性需要一个类型为 RelayMode 的参数,该参数指示消息到达服务器时将如何传播给玩家。

当前支持以下模式:

中继模式描述
AreaOfInterestOthers消息已发送给同一等级的所有其他玩家
AreaOfInterestAll消息将发送给同一等级的所有玩家,包括发送者本人。
GlobalOthers消息已发送给服务器上的所有其他玩家
GlobalAll消息已发送给服务器上的所有玩家,包括发送者

正在发送数据

RPC 处理程序支持在参数中传递数据,这些数据要么是:

一个 RPC 处理程序可以按任意数量、任意顺序声明这些参数。生成的“Send...”方法将具有相同的参数(不包括注入的参数)。

基本数据类型

我们支持在 C# 运行时定义的所有原始数据类型:boolcharsbytebyteshortushortintuintlongulongfloatdouble,以及 string

传递基本数据
public partial class MyRpc(IRpcClient client, IRelaySerializer serializer) : RpcClassBase(client, serializer)
{
[RpcEvent(RelayMode.AreaOfInterestOthers)]
private void OnMyCustomCall(int number, string text)
{
// 收到此消息时执行的操作
}

// 由 SDK 生成
private void SendMyCustomCall(int number, string text) { ... }
}

复杂数据类型

您可以在 RPC 参数中使用复杂的数据结构。这些数据结构必须实现 INetSerializable 接口。

载荷结构的字段可以是任何可序列化类型,包括其他复杂类型,前提是它们也实现了 INetSerializable

复杂 RPC 参数示例
public partial class MyRpc(IRpcClient client, IRelaySerializer serializer) : RpcClassBase(client, serializer)
{
[RpcEvent(RelayMode.AreaOfInterestOthers)]
private void OnPlayerBuffed(BuffData payload)
{
// 收到此消息时执行的操作
}

// 由 SDK 生成
private void SendPlayerBuffed(BuffData payload) { ... }
}

SDK 提供了 [DeriveINetSerializable] 属性,这使得 SDK 在大多数情况下能够自动生成序列化代码。为了实现这一点,结构体必须声明为 partial

生成序列化代码
[DeriveINetSerializable]
public partial struct BuffData(int buffId, float duration) : INetSerializable
{
public int BuffId = buffId;
public float Duration = duration;
}

如果你想对通过网络发送的数据拥有完全的控制权,也可以手动实现该接口。

我们将 LiteNetLib 库作为我们网络栈的基础。你可以参考它的 documentation 以了解如何使用 NetDataWriterNetDataReader

手写序列化代码
public struct BuffAddData(int buffId, float duration) : INetSerializable
{
public int BuffId = buffId;
public float Duration = duration;

public void Serialize(NetDataWriter writer)
{
writer.Put(BuffId);
writer.Put(Duration);
}

public void Deserialize(NetDataReader reader)
{
BuffId = reader.GetInt();
Duration = reader.GetFloat();
}
}

特殊参数

目前只能使用一个特殊参数。我们可能会在未来版本的 SDK 中扩展此功能。

名称类型描述
__senderPlayerId发送该 RPC 的玩家的标识符

此参数可与其他参数一起使用,但在生成的“Send...”方法中不可见。

使用注入的参数
public partial class MyRpc(IRpcClient client, IRelaySerializer serializer) : RpcClassBase(client, serializer)
{
[RpcEvent(RelayMode.AreaOfInterestOthers)]
private void OnComplexEvent(int number, PlayerId __sender, BuffData buffData)
{
// 示例:处理发送者昵称
if (WukongApi.Sync.TryGetPlayerInfoById(__sender, out var playerName, out _))
{
Logging.LogInfo("Received data from {Sender}", playerName);
}
}

// 由 SDK 生成
private void SendComplexEvent(int number, BuffData buffData) { ... }
}