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 # Game protocol protobuf generate file
protocol/proto protocol/proto
protocol/proto_hk4e/proto
gate/client_proto/proto gate/client_proto/proto
gate/client_proto/client_proto_gen.go gate/client_proto/client_proto_gen.go

View File

@@ -1,21 +1,65 @@
CUR_DIR=$(shell pwd) 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 .PHONY: build
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 .PHONY: dev_tool
dev_tool: dev_tool:
# 安装natsrpc生成工具
go install github.com/golang/protobuf/protoc-gen-go@v1.5.2 go install github.com/golang/protobuf/protoc-gen-go@v1.5.2
go install github.com/byebyebruce/natsrpc/cmd/protoc-gen-natsrpc@develop go install github.com/byebyebruce/natsrpc/cmd/protoc-gen-natsrpc@develop
test: # 生成natsrpc协议代码
go test ./...
.PHONY: gen_natsrpc .PHONY: gen_natsrpc
gen_natsrpc: gen_natsrpc:
# 生成natsrpc协议代码
protoc \ protoc \
--proto_path=gs/api \ --proto_path=gs/api \
--go_out=paths=source_relative:gs/api \ --go_out=paths=source_relative:gs/api \
@@ -27,9 +71,9 @@ gen_natsrpc:
--natsrpc_out=paths=source_relative:node/api \ --natsrpc_out=paths=source_relative:node/api \
node/api/*.proto node/api/*.proto
# 生成客户端协议代码
.PHONY: gen_proto .PHONY: gen_proto
gen_proto: gen_proto:
# 生成客户端协议代码
cd protocol/proto_hk4e && \ cd protocol/proto_hk4e && \
rm -rf ./proto && mkdir -p proto && \ rm -rf ./proto && mkdir -p proto && \
protoc --proto_path=./ --go_out=paths=source_relative:./proto ./*.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 && \ mv ./proto/server_only/* ./proto/ && rm -rf ./proto/server_only && \
rm -rf ../proto && mkdir -p ../proto && mv ./proto/* ../proto/ && rm -rf ./proto && \ rm -rf ../proto && mkdir -p ../proto && mv ./proto/* ../proto/ && rm -rf ./proto && \
cd ../../ 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
hk4e game server #### hk4e game server
## 开发快速上手 ## 编译和运行环境
* Go >= 1.18 * Go >= 1.18
* Protoc >= 3.21
* Protoc Gen Go >= 1.28
> 1. 首次需要安装工具 `make dev_tool` > 1. 首次需要安装工具 `make dev_tool`
> 2. 生成协议 `make gen_natsrpc && make gen_proto` > 2. 生成协议 `make gen_natsrpc && make gen_proto`
> 3. 生成配置表 `make gen_csv`
## 快速运行 ## 快速运行
@@ -17,15 +20,15 @@ hk4e game server
* nats-server * nats-server
* redis * redis
#### 启动顺序 #### 服务器组件
> 1. 启动节点服务器(仅单节点 有状态) `cd cmd/node && go run .` * node 节点服务器 (仅单节点 有状态)
> 2. 启动http登录服务器(可多节点 无状态) `cd cmd/dispatch && go run .` * dispatch 登录服务器 (可多节点 无状态)
> 3. 启动网关服务器(可多节点 有状态) `cd cmd/gate && go run .` * gate 网关服务器 (可多节点 有状态)
> 4. 启动战斗服务器(可多节点 有状态 非必要) `cd cmd/fight && go run .` * fight 战斗服务器 (可多节点 有状态 非必要)
> 5. 启动寻路服务器(可多节点 无状态 非必要) `cd cmd/pathfinding && go run .` * pathfinding 寻路服务器 (可多节点 无状态 非必要)
> 6. 启动游戏服务器(可多节点 有状态) `cd cmd/gs && go run .` * gs 游戏服务器 (可多节点 有状态)
> 7. 启动游戏管理服务器(仅单节点 无状态) `cd cmd/gm && go run .` * gm 游戏管理服务器 (仅单节点 无状态)
#### 其它 #### 其它
@@ -34,5 +37,3 @@ hk4e game server
```shell ```shell
GOLANG_PROTOBUF_REGISTRATION_CONFLICT=ignore GOLANG_PROTOBUF_REGISTRATION_CONFLICT=ignore
``` ```
* 运行gdconf/game_data_config_test.go文件中的TestGenGdCsv方法 生成服务器配置表

View File

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

View File

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

View File

@@ -40,7 +40,6 @@ type Redis struct {
type Hk4e struct { type Hk4e struct {
KcpPort int32 `toml:"kcp_port"` // 该地址只用来注册到节点服务器 并非网关本地监听地址 本地监听为0.0.0.0 KcpPort int32 `toml:"kcp_port"` // 该地址只用来注册到节点服务器 并非网关本地监听地址 本地监听为0.0.0.0
KcpAddr string `toml:"kcp_addr"` KcpAddr string `toml:"kcp_addr"`
ResourcePath string `toml:"resource_path"`
GameDataConfigPath string `toml:"game_data_config_path"` GameDataConfigPath string `toml:"game_data_config_path"`
GachaHistoryServer string `toml:"gacha_history_server"` GachaHistoryServer string `toml:"gacha_history_server"`
ClientProtoProxyEnable bool `toml:"client_proto_proxy_enable"` 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 > 2. 将对应版本的proto协议文件复制到proto目录下并编译成pb.go
> 3. 将client_proto_gen_test.go的TestClientProtoGen方法添加运行配置 > 3. make gen_client_proto
> 4. 将运行配置输出目录和工作目录都设置为bin目录 > 4. 将client_cmd.csv放入gate和gs和fight服务器的运行目录
> 5. 运行并生成client_proto_gen.go > 5. 将gate和gs和fight服务器的配置文件中开启client_proto_proxy_enable客户端协议代理功能
> 6. 将client_cmd.csv放入gate和gs和fight服务器的运行目录下
> 7. 将gate和gs和fight服务器的配置文件中开启client_proto_proxy_enable客户端协议代理功能

View File

@@ -7,7 +7,7 @@ import (
) )
func TestClientProtoGen(t *testing.T) { func TestClientProtoGen(t *testing.T) {
dir, err := os.ReadDir("../proto") dir, err := os.ReadDir("./proto")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -39,7 +39,7 @@ func TestClientProtoGen(t *testing.T) {
fileData += "\t}\n" fileData += "\t}\n"
fileData += "}\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 { if err != nil {
panic(err) panic(err)
} }

View File

@@ -5,6 +5,7 @@ import (
"image" "image"
"image/color" "image/color"
"image/jpeg" "image/jpeg"
"log"
"os" "os"
"strings" "strings"
"testing" "testing"
@@ -16,9 +17,82 @@ import (
"github.com/hjson/hjson-go/v4" "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) { func TestInitGameDataConfig(t *testing.T) {
config.InitConfig("./application.toml") config.InitConfig("./bin/application.toml")
logger.InitLogger("InitGameDataConfig") logger.InitLogger("InitGameDataConfig")
logger.Info("start load conf") logger.Info("start load conf")
InitGameDataConfig() InitGameDataConfig()
@@ -56,11 +130,11 @@ func CheckJsonLoop(path string, errorJsonFileList *[]string, totalJsonFileCount
// 测试加载json配置 // 测试加载json配置
func TestCheckJsonValid(t *testing.T) { func TestCheckJsonValid(t *testing.T) {
config.InitConfig("./application.toml") config.InitConfig("./bin/application.toml")
logger.InitLogger("CheckJsonValid") logger.InitLogger("CheckJsonValid")
errorJsonFileList := make([]string, 0) errorJsonFileList := make([]string, 0)
totalJsonFileCount := 0 totalJsonFileCount := 0
CheckJsonLoop("../game_data_config/json", &errorJsonFileList, &totalJsonFileCount) CheckJsonLoop("./game_data_config/json", &errorJsonFileList, &totalJsonFileCount)
for _, v := range errorJsonFileList { for _, v := range errorJsonFileList {
logger.Info("%v", v) logger.Info("%v", v)
} }
@@ -68,86 +142,9 @@ func TestCheckJsonValid(t *testing.T) {
time.Sleep(time.Second) 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区块配置坐标范围可视化 // 场景lua区块配置坐标范围可视化
func TestSceneBlock(t *testing.T) { func TestSceneBlock(t *testing.T) {
config.InitConfig("./application.toml") config.InitConfig("./bin/application.toml")
logger.InitLogger("SceneBlock") logger.InitLogger("SceneBlock")
InitGameDataConfig() InitGameDataConfig()
scene, exist := CONF.SceneMap[3] scene, exist := CONF.SceneMap[3]
@@ -197,7 +194,7 @@ func TestSceneBlock(t *testing.T) {
rectColor = 0 rectColor = 0
} }
} }
file, err := os.Create("./block.jpg") file, err := os.Create("./bin/block.jpg")
if err != nil { if err != nil {
return return
} }