protolinker

module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 5, 2024 License: MIT

README

protolinker

Protobuf 消息分组 + 自动编号,支持 C# 和 Go。

该插件会额外生成一个 Message 汇总文件,方便根据 id 创建 Message 对象。

安装

go install github.com/stalomeow/protolinker@latest

配置

TOML 配置文件,可以起名为 link.toml

# 配置 C# 的 Message 汇总文件
[out.csharp]
namespace = "Examples"
class = "NetMessageStore"
filename = "NetMessageStore.cs"
disposable_message = false # 是否让 Message 实现 IDisposable 接口

# 配置 Go 的 Message 汇总文件
[out.go]
import_path = "github.com/stalomeow/protolinker/examples"
package = "examples"

# Go 的文件名配置比较特殊,请参考下面文档中 output directory 相关内容
# https://protobuf.dev/reference/go/go-generated/#invocation
filename = "github.com/stalomeow/protolinker/examples/net_message.go"

# 第一组消息
[[groups]]
name = "group/one"
min = 100 # 该组消息的 uint16 id 的最小值(包含)
max = 300 # 该组消息的 uint16 id 的最大值(包含)

# 第二组消息
[[groups]]
name = "group/two"
min = 400
max = 900

# ...

分组

分组信息写在 Message 的 Leading Comments 里,独占一行,格式为 @group="GROUP_NAME"= 左右不要有空格。

syntax = "proto3";

option csharp_namespace = "Examples";
option go_package = "github.com/stalomeow/protolinker/examples";

// @group="group/one"
message MessageA {
    int32 i = 1;
}

// @group="group/one"
message MessageB {
    int32 i = 1;
}

// @group="group/two"
message MessageC {
    int32 i = 1;
}

// @group="group/two"
message MessageD {
    int32 i = 1;
}

生成

id 的分配顺序取决于:

  1. protoc 参数中 .proto 文件的顺序。
  2. .proto 文件中 Message 的定义顺序。

如果分开调用 protoc,先生成 C# 再生成 Go 的话,需要保证上面几个顺序一致。

C#
protoc ./*.proto -I=.        \
  --csharp_out=$(CSHARP_OUT) \
  --csharp_opt=$(CSHARP_OPT) \
  --cslink_out=$(CSHARP_OUT) \
  --cslink_opt=config=link.toml
  • --cslink_out:指定输出目录。

  • --cslink_opt

    • config:配置文件的路径。
    • base_namespace:同 --csharp_opt 里的 base_namespace
Go
protoc ./*.proto -I=.        \
  --go_out=$(GOLANG_OUT)     \
  --go_opt=$(GOLANG_OPT)     \
  --golink_out=$(GOLANG_OUT) \
  --golink_opt=$(GOLANG_OPT),config=link.toml
  • --golink_out:指定输出目录。

  • --golink_opt

    • config:配置文件的路径。
    • 所有 --go_opt 的参数。

使用

examples 文件夹 中有生成的样例代码。

C#
var store = new Examples.NetMessageStore();

// 从某处获得 msgId、msgSize 以及 data

// 过滤消息
if (msgId < store.MsgGroupMin_GroupOne || msgId > store.MsgGroupMax_GroupOne)
{
    return;
}

// 解析消息
MessageParser parser = store.GetMsgParserById(msgId);
var span = new ReadOnlySpan<byte>(data, msgSize);
IMessage msg = parser.ParseFrom(span);

// 处理消息
switch (msgId)
{
    case Examples.MessageA.MsgId:
        // ...
        break;

    case Examples.MessageB.MsgId:
        // ...
        break;

    // ...
}
// INetMessage 是插件生成的接口
// 它派生自 IMessage,包含了 MsgId 和 MsgName 信息
INetMessage msg = new Examples.MessageA();

// 消息长度
int msgSize = msg.CalculateSize();

// 消息 id
ushort msgId = msg.MsgId;

// 写入消息
msg.WriteTo(span);
Go
// 从某处获得 msgId、msgSize 以及 data

// 过滤消息
if msgId < examples.MsgGroupMin_GroupOne || msgId > examples.MsgGroupMax_GroupOne {
    return
}

// 解析消息
msg, err := examples.NewMsgById(msgId)
if err != nil {
    panic(err)
}
err = pb.Unmarshal(data[:msgSize], msg)
if err != nil {
    panic(err)
}

// 处理消息
switch msgId {
case examples.MessageA_MsgId:
    // ...

case examples.MessageB_MsgId:
    // ...

// ...
}
// NetMessage 是插件生成的接口,同 C#
msg := new(examples.MessageA).(examples.NetMessage)

// 消息 id
msgId := msg.MsgId()

Directories

Path Synopsis
cmd
examples
internal
app

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL