代码拉取完成,页面将自动刷新
同步操作将从 暖阳/gogame 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
go实现的游戏逻辑框架
cmd: 命令行
game 游戏逻辑模块
manage
configmanange 配置管理 【g.GC】 采用LuBan生成json和sturt 在此基础上2次封装
modulemanange 功能模块管理 【g.M】
modelmanange 数据层管理 【g.DATA】
module(存放所有的功能模块)
user:用户模块
api 接口基类
api_login 用户登录
api_loginout 用户登出
fun:模块公共逻辑方法
hero:同上
route:所有模块接口注册
common:游戏逻辑常用方法封装
g:游戏全局使用封装
globalplayer:玩家全局数据
gameconfig:游戏配置相关
crossserver:跨服配置处理
serverconfig:服务器配置处理
gameservice:游戏服务
gateway(网关服务,维护客户端链接)
app:网关服务载体
handle:用户网关实例
rpcmanage:管理rpcXClient
sender: 玩家数据推送
worker(游戏逻辑服务)
app:游戏逻辑服务载体
handle: 接口分发,注射
service:基类service
servicemanage:服务管理
log:存在游戏日志,以小时切割文件
worker:
2022-7-14-15.log
gateway:
2022-7-14-15.log
json:自定义json
crosstimer.json 跨服定时器配置
timer.json 本服定时器配置
errormsg.json 异常拦截语言包
samejson:策划json
hero.json 英雄表
item.json 道具表
logger:日志模块
server:网络服务
go_rpc: 自实现rpc
rpcx:基于rpcx的2次封装
client
client
selector 自定义worker选择器
server 服务端
server
serverplugin 中间件
network:基础服务
tcp:tcp
ws:websocket
lib: 第三方库封装,自定义工具
database:数据库相关
convert:类型转换
emitter:event事件管理
json:json编解码
file:文件操作
protobuf:protobuf操作
random:随机相关处理
time:时间相关处理
sys:通用方法
pb: protobuf存储
pb_python pb.python存储
proto 定义所有protobuf文件
模块名
模块名_api.proto 所有接口的proto
模块名_mongo.proto mongodb表对应的proto
pb.go都存储在当前目录
test:测试模块
debug:各种调试都可以
python python相关逻辑
yace 压测脚本
config: 服务配置文件
main: 服务启动入口
pb2go.py: protobuf转go和python
json2go.py: json转go
// ws请求pb
message Request {
string ModuleName = 1; // 功能模块名 例如:user、hero
string ApiName = 2; //接口名 例如: Login、LoginOut
google.protobuf.Any Data = 3; // 请求参数,接口分发时动态解析
string Sec = 4; // 接口秘钥
}
// 接口请求pb
message ApiRequest {
string ModuleName = 1; // 功能模块名 例如:user、hero
string ApiName = 2; //接口名 例如: Login、LoginOut
string Api = 3; // 拼接名 例如:user.Login
google.protobuf.Any Data = 4; // 请求参数,接口分发时动态解析
string Ip = 5; // rpcXClient地址
string Uid = 6; // 玩家uid
string GatewayUrl = 7; // gateway地址
string WorkerUrl = 8; // 请求的worker地址
}
// 接口响应pb
message ApiResponse {
// ResponseApi不为“”则为正常pb响应,取Data,否则就是字符串响应,取StringMsg
int64 S = 1; // 状态码 不为1表示请求失败
string ErrorMsg = 2; // 错误信息
string ResponseApi = 3; // 响应的api 例如:user.Login
google.protobuf.Any Data = 4; // pb响应
string StringMsg = 5; // 字符串响应
string Uid = 6; // 玩家uid
repeated Response FirstResponse = 7; // 需要提前返回的消息
int64 IsCheck = 8; // 接口检测标识 接口必须要进程接口检测,并将该字段置未1,否则调用不通过
}
// ws响应pb
message Response {
// ResponseApi不为“”则为正常pb响应,取Data,否则就是字符串响应,取StringMsg
int64 S = 1; // 状态码 不为1表示请求失败
string ErrorMsg = 2; // 错误信息
string ResponseApi = 3; // 响应的api 例如:user.Login
google.protobuf.Any Data = 5; // pb响应
string StringMsg = 6; // 字符串响应
}
func (h *Api) LoginCheck(ctx context.Context, args *pb.LoginArgs, res *rpcx.Response) {
res.IsCheck = 1
res.S = 1
_sid := args.Sid
// 区服id错误
if !g.Config.HasSid(_sid) {
res.S = -1
res.ErrorMsg = "区服id错误"
return
}
}
// Login 登陆
func (h *Api) Login(ctx context.Context, request *pb.ApiRequest, args *pb.LoginArgs, res *rpcx.Response) {
h.LoginCheck(ctx, args, res)
if res.S != 1 {
return
}
type ModuleUser struct {
*base.ModuleBase
Api
}
var User *ModuleUser
func init() {
User = &ModuleUser{
ModuleBase: base.NewModuleBase(),
}
// 手动定义接口 手动定义的接口args是any类型
User.Api2Func = worker.Api2FuncInfo{
// key:接口名,需要驼峰,和反射调用保持一致 value: [接口func,接口args]
"GetInfo": worker.FmtFuncInfo(User.GetInfo, new(pb.UserGetInfoArgs)),
}
_moduleName := "user"
// 路由注册 未定义Api2Func默认采用反射筛选符合条件的方法注册和调用
worker.Register(_moduleName, User)
// 模块注册
module.Manage.User = User
// 定时器注册 测试
_name2Func := timer.Api2Func{
// key就是定时器文件名 values定时器方法名
"timer_hello": TimerHello,
}
timer.Register(_moduleName, _name2Func)
}
package module
import (
"fmt"
_ "gogame/game/module/hero"
_ "gogame/game/module/user"
)
func InitRoute() {
fmt.Println()
}
// 获取所有英雄列表
_heroList = g.M.Hero.GetHeroList("uid")
// 获取英雄数据
_heroInfo = g.M.Hero.GetHeroList("oid")
// 创建玩家
g.M.User.CreateUser("wnp001", "127.0.0.1", 0)
// 获取玩家数据
g.M.User.GetUserInfo("uid")
功能用json----config_Hero.go
package config
import cfg "gogame/game/manage/config/structs"
type Hero struct {
StructHandle
*cfg.Game_hero
NewFunc func(_buf []map[string]any) (*cfg.Game_hero, error) // 数据生成方法
// 预处理字段定义区域---start
// 预处理字段定义区域---end
}
func init() {
config := &Hero{
StructHandle: NewStructHandle("game_Hero"),
NewFunc: cfg.NewGame_hero,
}
Manage.configs.Hero = config
RegisterConfigInterface(config.Name, config)
}
// Load 加载配置
func (self *Hero) Load(jsonData any) error {
data, err := self.NewFunc(jsonData.([]map[string]any))
if err != nil {
return err
}
self.Game_hero = data
self.PreConfig()
return nil
}
// PreConfig 配置预处理
func (self *Hero) PreConfig() {
}
// ------------------------ 以上文件内容自动生成,请勿随便修改! ------------------------
// ------------------------ 下方、定义配置diy方法 ------------------------
自定义json----config_ErrorMsg.go
package config
type ErrorMsg struct {
*JsonHandle
// 预处理字段定义区域---start
// 预处理字段定义区域---end
}
func init() {
config := &ErrorMsg{
JsonHandle: NewJsonHandle("ErrorMsg"),
}
Manage.configs.ErrorMsg = config
RegisterConfigInterface(config.Name, config)
}
// Load 加载配置
func (self *ErrorMsg) Load(jsonData any) error {
err := self.JsonHandle.Load(jsonData)
if err != nil {
return err
}
self.PreConfig()
return nil
}
// PreConfig 配置预处理
func (self *ErrorMsg) PreConfig() {
}
// ------------------------ 以上文件内容自动生成,请勿随便修改! ------------------------
// ------------------------ 下方、定义配置diy方法 ------------------------
// 获取所有英雄配置
g.GC.Hero().GetDataList()
// 获取单个英雄配置
g.GC.Hero().Get(101)
// 获取star大于3星的所有英雄
g.GC.Hero().GetListByStar(3)
// 获取所有英雄配置
g.GC.Item().GetDataList()
// 获取单个英雄配置
g.GC.Item().Get(101)
// 获取color大于3星的所有英雄
g.GC.Item().GetListByColor(3)
每隔5秒执行一次: */5 * * * * *
每隔1分钟执行一次: 0 */1 * * * *
每天23点执行一次: 0 0 23 * * *
每月1号凌晨1点执行一次: 0 0 1 1 * *
在26分、29分、33分执行一次: 0 26,29,33 * * * *
每天的0点、13点、18点、21点都执行一次: 0 0 0,13,18,21 * * *
{
"timerstr": "*/2 * * * * *", // cron格式定义
"folder": "user", // 所属模块
"api": "hello", // 接口名,注册时候func对应的key
"args": [], // 接口参数
"desc": "这是测试用的" // 定时器描述
}
【2022-07-19 11:03:12】 heroinit route success
【2022-07-19 11:03:12】 userinit route success
【2022-07-19 11:03:12】 **********************************************************************************************
【2022-07-19 11:03:12】 addTimer: user.hello timerStr: */2 * * * * * desc: 这是测试用的
【2022-07-19 11:03:12】 **********************************************************************************************
【2022-07-19 17:05:38】 heroinit route success
【2022-07-19 17:05:38】 userinit route success
【2022-07-19 17:05:38】 **********************************************************************************************
【2022-07-19 17:05:38】 定时器加载ing...
【2022-07-19 17:05:38】 addTimer: user.timer_hello timerStr: */2 * * * * * desc: 这是测试用的
【2022-07-19 17:05:38】 **********************************************************************************************
【2022-07-19 17:05:40】 user timer test....hello world
【2022-07-19 17:05:40】 定时器【user.timer_hello】执行成功!!
【2022-07-19 17:05:42】 user timer test....hello world
【2022-07-19 17:05:42】 定时器【user.timer_hello】执行成功!!
【2022-07-19 17:07:35】 heroinit route success
【2022-07-19 17:07:35】 userinit route success
【2022-07-19 17:07:35】 **********************************************************************************************
【2022-07-19 17:07:35】 定时器加载ing...
【2022-07-19 17:07:35】 addTimer: user.timer_hello timerStr: */2 * * * * * desc: 这是测试用的
【2022-07-19 17:07:35】 **********************************************************************************************
【2022-07-19 17:07:36】 user timer test....hello world
【2022-07-19 17:07:36】 定时器【user.timer_hello】执行失败!! msg-->测试,不允许执行
【2022-07-19 17:29:19】 heroinit route success
【2022-07-19 17:29:19】 userinit route success
【2022-07-19 17:29:19】 **********************************************************************************************
【2022-07-19 17:29:19】 定时器加载ing...
【2022-07-19 17:29:19】 addTimer: user.timer_hello timerStr: */2 * * * * * desc: 这是测试用的
【2022-07-19 17:29:19】 **********************************************************************************************
【2022/07/19 17:29:20.014】 ERROR 【timer/job.go:27】 Timer.Run error!!
api:【user.timer_hello】 args: []
err--->: 【runtime error: index out of range [0] with length 0】
goroutine 15 [running]:
runtime/debug.Stack()
C:/Users/mayn/go1.18/go1.18/src/runtime/debug/stack.go:24 +0x65
gogame/lib/timer.(*Job).Run.func1()
E:/wnp/go_project/gogame/lib/timer/job.go:25 +0x77
panic({0x1c1d160, 0xc0002f2dc8})
C:/Users/mayn/go1.18/go1.18/src/runtime/panic.go:838 +0x207
gogame/game/module/user.TimerHello({0x26ff0b0?, 0x0?, 0x0?})
E:/wnp/go_project/gogame/game/module/user/timer_hello.go:6 +0xa6
gogame/lib/timer.(*Job).Run(0xc00031a510)
E:/wnp/go_project/gogame/lib/timer/job.go:30 +0x82
github.com/robfig/cron/v3.(*Cron).startJob.func1()
C:/Users/mayn/go/pkg/mod/github.com/robfig/cron/v3@v3.0.1/cron.go:312 +0x6a
created by github.com/robfig/cron/v3.(*Cron).startJob
C:/Users/mayn/go/pkg/mod/github.com/robfig/cron/v3@v3.0.1/cron.go:310 +0xad
例:
1000个玩家产生了1万条数据,正常会与mongo产生1万次io
model类会整合这1万条数据,insertMany入库到mongo的model_log表,只进行1次io
消费服务会监听mongo的model_log表,有数据就会即时消费掉这些数据
例如:
userinfo表的唯一key是uid,一个玩家只有一条数据
hero表的唯一key是_id,一个玩家有多个英雄
```go
type UserInfo struct {
*ModelBase
}
func init() {
model := &UserInfo{
ModelBase: NewModelBase("userinfo", "uid", SetExpireTime(time.Hour*6)),
}
Manage.models.UserInfo = model
}
// 这里的单独使用 g.GC会导致循环引用
var (
GC = config.Manage
M = module.Manage
C = lib.Common
)
type models struct {
UserInfo *UserInfo
Hero *Hero
}
// GetInfo 获取英雄数据 外部调用就是g.DATA.Hero.GetInfo(oid)
func (self *Hero) GetInfo(id string) *pb.ModelHero {
heroInfo := new(pb.ModelHero)
self.Get(id, heroInfo)
return heroInfo
}
// GetList 获取玩家英雄列表 外部调用就是g.DATA.Hero.GetList(uid)
func (self *Hero) GetList(uid string) []*pb.ModelHero {
var heroList []*pb.ModelHero
self.ModelBase.GetList(uid, &heroList)
// 测试,没有英雄数据增加几个
if len(heroList) == 0 {
_hid2Hero := self.AddHero(uid, 11001, 11002, 11003)
for _, h := range _hid2Hero {
heroList = append(heroList, h)
}
}
return heroList
}
// 修英雄的等级修改为666级
_setData := map[string]any{
"lv": 666
}
g.DATA.Hero.Set("62df6524ae0dd951b1daaafc", _setData)
g.DATA.Hero.Remove("62df6524ae0dd951b1daaafc")
g.DATA.Hero.InsertOne("62df6524ae0dd951b1daaafc")
例如:
// AddHero 增加一个英雄
func (self *Hero) AddHero(uid string, hid int32) *pb.ModelHero {
_heroInfo := M.Hero.GetDefHeroInfo(uid, hid)
// 英雄不存在
if _heroInfo == nil {
return nil
}
_heroInfo.Id = C.GetObjectId()
self.InsertOne(uid, _heroInfo)
return _heroInfo
}
g.DATA.Hero.InsertMany([]any)
例如:
_heroList := []any{
g.M.Hero.GetDefHeroInfo(uid, 11001),
g.M.Hero.GetDefHeroInfo(uid, 11001),
g.M.Hero.GetDefHeroInfo(uid, 11001),
}
g.DATA.Hero.InsertMany(uid, _heroList)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。