makefile编译客户端协议、生成服务器配置表 && docker镜像构建

This commit is contained in:
flswld
2023-01-19 23:05:10 +08:00
parent 2983c16272
commit cda96a81df
16 changed files with 217 additions and 114 deletions

1
.gitignore vendored
View File

@@ -23,7 +23,6 @@ bin
# Game protocol protobuf generate file
protocol/proto
protocol/proto_hk4e/proto
gate/client_proto/proto
gate/client_proto/client_proto_gen.go

View File

@@ -1,21 +1,65 @@
CUR_DIR=$(shell pwd)
VERSION=1.0.0
# 清理
.PHONY: clean
clean:
rm -rf ./bin
rm -rf ./protocol/proto
rm -rf ./gate/client_proto/client_proto_gen.go
rm -rf ./gdconf/game_data_config/csv/*.csv
# 构建服务器二进制文件
.PHONY: build
build:
mkdir -p bin/ && CGO_ENABLED=0 go build -ldflags "-X main.Version=$(VERSION)" -o ./bin/ ./cmd/...
mkdir -p bin && CGO_ENABLED=0 go build -ldflags "-X main.Version=$(VERSION)" -o ./bin/ ./cmd/...
# 清理镜像
.PHONY: docker_clean
docker_clean:
rm -rf ./docker/node/bin/*
rm -rf ./docker/dispatch/bin/*
rm -rf ./docker/gate/bin/*
rm -rf ./docker/fight/bin/*
rm -rf ./docker/pathfinding/bin/*
rm -rf ./docker/gs/bin/*
rm -rf ./docker/gm/bin/*
docker rmi flswld/node:$(VERSION)
docker rmi flswld/dispatch:$(VERSION)
docker rmi flswld/gate:$(VERSION)
docker rmi flswld/fight:$(VERSION)
docker rmi flswld/pathfinding:$(VERSION)
docker rmi flswld/gs:$(VERSION)
docker rmi flswld/gm:$(VERSION)
# 构建镜像
.PHONY: docker_build
docker_build:
mkdir -p ./docker/node/bin && cp -rf ./bin/node ./cmd/node/* ./docker/node/bin/
mkdir -p ./docker/dispatch/bin && cp -rf ./bin/dispatch ./cmd/dispatch/* ./docker/dispatch/bin/
mkdir -p ./docker/gate/bin && cp -rf ./bin/gate ./cmd/gate/* ./docker/gate/bin/
mkdir -p ./docker/fight/bin && cp -rf ./bin/fight ./cmd/fight/* ./docker/fight/bin/
mkdir -p ./docker/pathfinding/bin && cp -rf ./bin/pathfinding ./cmd/pathfinding/* ./docker/pathfinding/bin/
mkdir -p ./docker/gs/bin && cp -rf ./bin/gs ./cmd/gs/* ./docker/gs/bin/
mkdir -p ./docker/gm/bin && cp -rf ./bin/gm ./cmd/gm/* ./docker/gm/bin/
docker build -t flswld/node:$(VERSION) ./docker/node
docker build -t flswld/dispatch:$(VERSION) ./docker/dispatch
docker build -t flswld/gate:$(VERSION) ./docker/gate
docker build -t flswld/fight:$(VERSION) ./docker/fight
docker build -t flswld/pathfinding:$(VERSION) ./docker/pathfinding
docker build -t flswld/gs:$(VERSION) ./docker/gs
docker build -t flswld/gm:$(VERSION) ./docker/gm
# 安装natsrpc生成工具
.PHONY: dev_tool
dev_tool:
# 安装natsrpc生成工具
go install github.com/golang/protobuf/protoc-gen-go@v1.5.2
go install github.com/byebyebruce/natsrpc/cmd/protoc-gen-natsrpc@develop
test:
go test ./...
# 生成natsrpc协议代码
.PHONY: gen_natsrpc
gen_natsrpc:
# 生成natsrpc协议代码
protoc \
--proto_path=gs/api \
--go_out=paths=source_relative:gs/api \
@@ -27,9 +71,9 @@ gen_natsrpc:
--natsrpc_out=paths=source_relative:node/api \
node/api/*.proto
# 生成客户端协议代码
.PHONY: gen_proto
gen_proto:
# 生成客户端协议代码
cd protocol/proto_hk4e && \
rm -rf ./proto && mkdir -p proto && \
protoc --proto_path=./ --go_out=paths=source_relative:./proto ./*.proto && \
@@ -41,3 +85,13 @@ gen_proto:
mv ./proto/server_only/* ./proto/ && rm -rf ./proto/server_only && \
rm -rf ../proto && mkdir -p ../proto && mv ./proto/* ../proto/ && rm -rf ./proto && \
cd ../../
# 生成服务器配置表
.PHONY: gen_csv
gen_csv:
cd gdconf && go test -v -run TestGenGdCsv .
# 生成客户端协议代理功能所需的代码
.PHONY: gen_client_proto
gen_client_proto:
cd gate/client_proto && go test -v -run TestClientProtoGen .

View File

@@ -1,13 +1,16 @@
# hk4e
hk4e game server
#### hk4e game server
## 开发快速上手
## 编译和运行环境
* Go >= 1.18
* Protoc >= 3.21
* Protoc Gen Go >= 1.28
> 1. 首次需要安装工具 `make dev_tool`
> 2. 生成协议 `make gen_natsrpc && make gen_proto`
> 3. 生成配置表 `make gen_csv`
## 快速运行
@@ -17,15 +20,15 @@ hk4e game server
* nats-server
* redis
#### 启动顺序
#### 服务器组件
> 1. 启动节点服务器(仅单节点 有状态) `cd cmd/node && go run .`
> 2. 启动http登录服务器(可多节点 无状态) `cd cmd/dispatch && go run .`
> 3. 启动网关服务器(可多节点 有状态) `cd cmd/gate && go run .`
> 4. 启动战斗服务器(可多节点 有状态 非必要) `cd cmd/fight && go run .`
> 5. 启动寻路服务器(可多节点 无状态 非必要) `cd cmd/pathfinding && go run .`
> 6. 启动游戏服务器(可多节点 有状态) `cd cmd/gs && go run .`
> 7. 启动游戏管理服务器(仅单节点 无状态) `cd cmd/gm && go run .`
* node 节点服务器 (仅单节点 有状态)
* dispatch 登录服务器 (可多节点 无状态)
* gate 网关服务器 (可多节点 有状态)
* fight 战斗服务器 (可多节点 有状态 非必要)
* pathfinding 寻路服务器 (可多节点 无状态 非必要)
* gs 游戏服务器 (可多节点 有状态)
* gm 游戏管理服务器 (仅单节点 无状态)
#### 其它
@@ -34,5 +37,3 @@ hk4e game server
```shell
GOLANG_PROTOBUF_REGISTRATION_CONFLICT=ignore
```
* 运行gdconf/game_data_config_test.go文件中的TestGenGdCsv方法 生成服务器配置表

View File

@@ -1,10 +1,10 @@
[hk4e]
kcp_addr = "127.0.0.1" # 该地址只用来注册到节点服务器 并非网关本地监听地址 本地监听为0.0.0.0
kcp_port = 22103
kcp_port = 22222
client_proto_proxy_enable = false
version = "320"
gate_tcp_mq_addr = "127.0.0.1"
gate_tcp_mq_port = 9999
gate_tcp_mq_port = 33333
[logger]
level = "DEBUG"

View File

@@ -1,6 +1,5 @@
[hk4e]
client_proto_proxy_enable = false
resource_path = "./GameDataConfigTable"
game_data_config_path = "./game_data_config"
gacha_history_server = "https://hk4e.flswld.com/api/v1"

View File

@@ -40,7 +40,6 @@ type Redis struct {
type Hk4e struct {
KcpPort int32 `toml:"kcp_port"` // 该地址只用来注册到节点服务器 并非网关本地监听地址 本地监听为0.0.0.0
KcpAddr string `toml:"kcp_addr"`
ResourcePath string `toml:"resource_path"`
GameDataConfigPath string `toml:"game_data_config_path"`
GachaHistoryServer string `toml:"gacha_history_server"`
ClientProtoProxyEnable bool `toml:"client_proto_proxy_enable"`

View File

@@ -0,0 +1,9 @@
FROM ubuntu:18.04
EXPOSE 8080/tcp
WORKDIR /dispatch
COPY ./bin/dispatch ./dispatch
RUN chmod +x ./dispatch
ENTRYPOINT ["./dispatch"]

7
docker/fight/Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
FROM ubuntu:18.04
WORKDIR /fight
COPY ./bin/fight ./fight
RUN chmod +x ./fight
ENTRYPOINT ["./fight"]

10
docker/gate/Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM ubuntu:18.04
EXPOSE 22222/udp
EXPOSE 33333/tcp
WORKDIR /gate
COPY ./bin/gate ./gate
RUN chmod +x ./gate
ENTRYPOINT ["./gate"]

9
docker/gm/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM ubuntu:18.04
EXPOSE 9001/tcp
WORKDIR /gm
COPY ./bin/gm ./gm
RUN chmod +x ./gm
ENTRYPOINT ["./gm"]

7
docker/gs/Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
FROM ubuntu:18.04
WORKDIR /gs
COPY ./bin/gs ./gs
RUN chmod +x ./gs
ENTRYPOINT ["./gs"]

7
docker/node/Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
FROM ubuntu:18.04
WORKDIR /node
COPY ./bin/node ./node
RUN chmod +x ./node
ENTRYPOINT ["./node"]

View File

@@ -0,0 +1,7 @@
FROM ubuntu:18.04
WORKDIR /pathfinding
COPY ./bin/pathfinding ./pathfinding
RUN chmod +x ./pathfinding
ENTRYPOINT ["./pathfinding"]

View File

@@ -6,10 +6,8 @@
## 使用方法
> 1. 在此目录下建立bin目录和proto目录
> 1. 在此目录下建立proto目录
> 2. 将对应版本的proto协议文件复制到proto目录下并编译成pb.go
> 3. 将client_proto_gen_test.go的TestClientProtoGen方法添加运行配置
> 4. 将运行配置输出目录和工作目录都设置为bin目录
> 5. 运行并生成client_proto_gen.go
> 6. 将client_cmd.csv放入gate和gs和fight服务器的运行目录下
> 7. 将gate和gs和fight服务器的配置文件中开启client_proto_proxy_enable客户端协议代理功能
> 3. make gen_client_proto
> 4. 将client_cmd.csv放入gate和gs和fight服务器的运行目录
> 5. 将gate和gs和fight服务器的配置文件中开启client_proto_proxy_enable客户端协议代理功能

View File

@@ -7,7 +7,7 @@ import (
)
func TestClientProtoGen(t *testing.T) {
dir, err := os.ReadDir("../proto")
dir, err := os.ReadDir("./proto")
if err != nil {
panic(err)
}
@@ -39,7 +39,7 @@ func TestClientProtoGen(t *testing.T) {
fileData += "\t}\n"
fileData += "}\n"
err = os.WriteFile("../client_proto_gen.go", []byte(fileData), 0644)
err = os.WriteFile("./client_proto_gen.go", []byte(fileData), 0644)
if err != nil {
panic(err)
}

View File

@@ -5,6 +5,7 @@ import (
"image"
"image/color"
"image/jpeg"
"log"
"os"
"strings"
"testing"
@@ -16,9 +17,82 @@ import (
"github.com/hjson/hjson-go/v4"
)
type TableField struct {
FieldName string `json:"field_name"`
FieldType string `json:"field_type"`
OriginName string `json:"origin_name"`
}
type TableStructMapping struct {
TableName string `json:"table_name"`
FieldList []*TableField `json:"field_list"`
}
// 生成最终服务器读取的配置表
func TestGenGdCsv(t *testing.T) {
tableStructMappingList := make([]*TableStructMapping, 0)
configFileData, err := os.ReadFile("./table_struct_mapping.json")
if err != nil {
log.Printf("open config file error: %v", err)
return
}
err = json.Unmarshal(configFileData, &tableStructMappingList)
if err != nil {
log.Printf("parse config file error: %v", err)
return
}
for _, tableStructMapping := range tableStructMappingList {
txtFileData, err := os.ReadFile("./game_data_config/txt/" + tableStructMapping.TableName + ".txt")
if err != nil {
log.Printf("read txt file error: %v", err)
continue
}
// 转换txt配置表格式为csv
originCsv := string(txtFileData)
originCsv = strings.ReplaceAll(originCsv, "\r\n", "\n")
originCsv = strings.ReplaceAll(originCsv, "\r", "\n")
originCsv = strings.ReplaceAll(originCsv, ",", "#")
originCsv = strings.ReplaceAll(originCsv, ";", "#")
originCsv = strings.ReplaceAll(originCsv, "\t", ",")
originCsvLineList := strings.Split(originCsv, "\n")
if len(originCsvLineList) == 0 {
log.Printf("origin csv file is empty")
continue
}
originCsvHeadList := strings.Split(originCsvLineList[0], ",")
if len(originCsvHeadList) == 0 {
log.Printf("origin csv file head is empty")
continue
}
fieldNameHead := ""
fieldTypeHead := ""
for index, originCsvHead := range originCsvHeadList {
for _, tableField := range tableStructMapping.FieldList {
if originCsvHead == tableField.OriginName {
// 字段名匹配成功
fieldNameHead += tableField.FieldName
fieldTypeHead += tableField.FieldType
}
}
if index < len(originCsvHeadList)-1 {
fieldNameHead += ","
fieldTypeHead += ","
}
}
fieldNameHead += "\n"
fieldTypeHead += "\n"
gdCsvFile := fieldNameHead + fieldTypeHead + originCsv
err = os.WriteFile("./game_data_config/csv/"+tableStructMapping.TableName+".csv", []byte(gdCsvFile), 0644)
if err != nil {
log.Printf("write gd csv file error: %v", err)
continue
}
}
}
// 测试初始化加载配置表
func TestInitGameDataConfig(t *testing.T) {
config.InitConfig("./application.toml")
config.InitConfig("./bin/application.toml")
logger.InitLogger("InitGameDataConfig")
logger.Info("start load conf")
InitGameDataConfig()
@@ -56,11 +130,11 @@ func CheckJsonLoop(path string, errorJsonFileList *[]string, totalJsonFileCount
// 测试加载json配置
func TestCheckJsonValid(t *testing.T) {
config.InitConfig("./application.toml")
config.InitConfig("./bin/application.toml")
logger.InitLogger("CheckJsonValid")
errorJsonFileList := make([]string, 0)
totalJsonFileCount := 0
CheckJsonLoop("../game_data_config/json", &errorJsonFileList, &totalJsonFileCount)
CheckJsonLoop("./game_data_config/json", &errorJsonFileList, &totalJsonFileCount)
for _, v := range errorJsonFileList {
logger.Info("%v", v)
}
@@ -68,86 +142,9 @@ func TestCheckJsonValid(t *testing.T) {
time.Sleep(time.Second)
}
type TableField struct {
FieldName string `json:"field_name"`
FieldType string `json:"field_type"`
OriginName string `json:"origin_name"`
}
type TableStructMapping struct {
TableName string `json:"table_name"`
FieldList []*TableField `json:"field_list"`
}
// 生成最终服务器读取的配置表
func TestGenGdCsv(t *testing.T) {
config.InitConfig("./application.toml")
logger.InitLogger("GenGdCsv")
tableStructMappingList := make([]*TableStructMapping, 0)
configFileData, err := os.ReadFile("../table_struct_mapping.json")
if err != nil {
logger.Error("open config file error: %v", err)
return
}
err = json.Unmarshal(configFileData, &tableStructMappingList)
if err != nil {
logger.Error("parse config file error: %v", err)
return
}
for _, tableStructMapping := range tableStructMappingList {
txtFileData, err := os.ReadFile("../game_data_config/txt/" + tableStructMapping.TableName + ".txt")
if err != nil {
logger.Error("read txt file error: %v", err)
continue
}
// 转换txt配置表格式为csv
originCsv := string(txtFileData)
originCsv = strings.ReplaceAll(originCsv, "\r\n", "\n")
originCsv = strings.ReplaceAll(originCsv, "\r", "\n")
originCsv = strings.ReplaceAll(originCsv, ",", "#")
originCsv = strings.ReplaceAll(originCsv, ";", "#")
originCsv = strings.ReplaceAll(originCsv, "\t", ",")
originCsvLineList := strings.Split(originCsv, "\n")
if len(originCsvLineList) == 0 {
logger.Error("origin csv file is empty")
continue
}
originCsvHeadList := strings.Split(originCsvLineList[0], ",")
if len(originCsvHeadList) == 0 {
logger.Error("origin csv file head is empty")
continue
}
fieldNameHead := ""
fieldTypeHead := ""
for index, originCsvHead := range originCsvHeadList {
for _, tableField := range tableStructMapping.FieldList {
if originCsvHead == tableField.OriginName {
// 字段名匹配成功
fieldNameHead += tableField.FieldName
fieldTypeHead += tableField.FieldType
}
}
if index < len(originCsvHeadList)-1 {
fieldNameHead += ","
fieldTypeHead += ","
}
}
fieldNameHead += "\n"
fieldTypeHead += "\n"
gdCsvFile := fieldNameHead + fieldTypeHead + originCsv
err = os.WriteFile("../game_data_config/csv/"+tableStructMapping.TableName+".csv", []byte(gdCsvFile), 0644)
if err != nil {
logger.Error("write gd csv file error: %v", err)
continue
}
}
logger.Info("gen gd csv finish")
time.Sleep(time.Second)
}
// 场景lua区块配置坐标范围可视化
func TestSceneBlock(t *testing.T) {
config.InitConfig("./application.toml")
config.InitConfig("./bin/application.toml")
logger.InitLogger("SceneBlock")
InitGameDataConfig()
scene, exist := CONF.SceneMap[3]
@@ -197,7 +194,7 @@ func TestSceneBlock(t *testing.T) {
rectColor = 0
}
}
file, err := os.Create("./block.jpg")
file, err := os.Create("./bin/block.jpg")
if err != nil {
return
}