https://github.com/stalomeow/protolinker
Protobuf 消息分组 + 自动编号,支持 C# 和 Go
https://github.com/stalomeow/protolinker
csharp go golang protobuf protocol-buffers unity
Last synced: 6 months ago
JSON representation
Protobuf 消息分组 + 自动编号,支持 C# 和 Go
- Host: GitHub
- URL: https://github.com/stalomeow/protolinker
- Owner: stalomeow
- License: mit
- Created: 2024-02-04T11:20:33.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-05-26T13:39:21.000Z (about 2 years ago)
- Last Synced: 2025-05-23T00:36:09.711Z (about 1 year ago)
- Topics: csharp, go, golang, protobuf, protocol-buffers, unity
- Language: Go
- Homepage:
- Size: 24.4 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# protolinker
Protobuf 消息分组 + 自动编号,支持 C# 和 Go。
该插件会额外生成一个 Message 汇总文件,方便根据 id 创建 Message 对象。
## 安装
```
go install github.com/stalomeow/protolinker/...@latest
```
## 配置
TOML 配置文件,可以起名为 `link.toml`。
``` 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"`。`=` 左右不要有空格。
``` protobuf
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#
``` make
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
``` make
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 文件夹](examples) 中有生成的样例代码。
### C#
``` csharp
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(data, msgSize);
IMessage msg = parser.ParseFrom(span);
// 处理消息
switch (msgId)
{
case Examples.MessageA.MsgId:
// ...
break;
case Examples.MessageB.MsgId:
// ...
break;
// ...
}
```
``` csharp
// INetMessage 是插件生成的接口
// 它派生自 IMessage,包含了 MsgId 和 MsgName 信息
INetMessage msg = new Examples.MessageA();
// 消息长度
int msgSize = msg.CalculateSize();
// 消息 id
ushort msgId = msg.MsgId;
// 写入消息
msg.WriteTo(span);
```
### Go
``` 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:
// ...
// ...
}
```
``` go
// NetMessage 是插件生成的接口,同 C#
msg := new(examples.MessageA).(examples.NetMessage)
// 消息 id
msgId := msg.MsgId()
```