```
refactor(controller): 优化控制器初始化与命令解析逻辑 - 重构 getCmd 和 ParseCmdTag
This commit is contained in:
@@ -1,183 +1,183 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"blazing/logic/service/common"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"strings"
|
||||
|
||||
"bytes"
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/lunixbochs/struc"
|
||||
)
|
||||
|
||||
var Maincontroller = &Controller{} //注入service
|
||||
|
||||
// 分发cmd逻辑实现Controller
|
||||
type Controller struct {
|
||||
Port uint16
|
||||
RPCClient struct {
|
||||
Kick func(uint32) error
|
||||
|
||||
RegisterLogic func(uint16, uint16) error
|
||||
}
|
||||
}
|
||||
|
||||
func ParseCmd[T any](a T, data []byte) T {
|
||||
// := info.NewLoginSidInfo()
|
||||
struc.Unpack(bytes.NewBuffer(data), &a)
|
||||
return a
|
||||
//fmt.Println(pinfo)
|
||||
//login.OnData_1001(pinfo, player)
|
||||
//fmt.Println(data)
|
||||
}
|
||||
|
||||
func Init(isgame bool) { //默认初始化扫描
|
||||
|
||||
// 获取对象的反射值和类型
|
||||
value := reflect.ValueOf(Maincontroller)
|
||||
|
||||
// 获取类型
|
||||
typ := value.Type()
|
||||
|
||||
for i := 0; i < typ.NumMethod(); i++ {
|
||||
method := typ.Method(i)
|
||||
|
||||
methodValue := value.MethodByName(method.Name)
|
||||
//fmt.Println("找到注册方法", method.Name)
|
||||
methodValue.Type().NumIn()
|
||||
|
||||
for _, func_cmd := range getCmd(methodValue.Type().In(0)) {
|
||||
if func_cmd == 0 { //说明不是注册方法
|
||||
glog.Warning(context.Background(), "方法参数必须包含CMD参数", method.Name, "跳过注册")
|
||||
continue
|
||||
}
|
||||
|
||||
if !isgame && func_cmd > 1000 { //判断login服务器
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
if isgame && func_cmd < 1000 { //判断login服务器
|
||||
continue
|
||||
|
||||
}
|
||||
glog.Debug(context.Background(), "注册方法", func_cmd, method.Name)
|
||||
|
||||
_, ok := cool.CmdCache.LoadOrStore(func_cmd, cool.Cmd{
|
||||
Func: methodValue,
|
||||
Req: methodValue.Type().In(0).Elem(),
|
||||
// Res: ,
|
||||
}) //TODO 待实现对不同用户初始化方法以取消全局cmdcache
|
||||
|
||||
if ok { //方法已存在init
|
||||
glog.Error(context.Background(), "方法已存在init,不会初始化后面的方法", func_cmd)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var targetType = reflect.TypeOf(common.TomeeHeader{})
|
||||
|
||||
// 默认返回值(无匹配字段/解析失败时)
|
||||
const defaultCmdValue = 0
|
||||
|
||||
// getCmd 从结构体类型中提取绑定的cmd指令(递归查找嵌套结构体,支持值/指针类型的TomeeHeader)
|
||||
|
||||
// 参数 typ:待解析的结构体类型(支持多层指针)
|
||||
// 返回值:解析到的cmd切片,无匹配/解析失败时返回[defaultCmdValue]
|
||||
func getCmd(typ reflect.Type) []uint32 {
|
||||
// 初始化目标类型(仅第一次调用时执行)
|
||||
|
||||
// 1. 递归解引用所有指针类型(处理 *struct、**struct 等场景)
|
||||
for typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
|
||||
// 2. 非结构体类型直接返回默认值
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return []uint32{defaultCmdValue}
|
||||
}
|
||||
|
||||
// 3. 遍历结构体字段,查找TomeeHeader字段并解析cmd
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
// 4. 调用解析函数,判断是否为目标类型并解析cmd
|
||||
cmdSlice, err := ParseCmdTagWithStructField(field)
|
||||
if err == nil { // 解析成功,直接返回结果
|
||||
return cmdSlice
|
||||
}
|
||||
|
||||
// 5. 递归处理嵌套结构体(值/指针类型)
|
||||
nestedTyp := field.Type
|
||||
if nestedTyp.Kind() == reflect.Ptr {
|
||||
nestedTyp = nestedTyp.Elem()
|
||||
}
|
||||
if nestedTyp.Kind() == reflect.Struct {
|
||||
// 递归查找,找到有效cmd则立即返回
|
||||
if nestedCmd := getCmd(nestedTyp); len(nestedCmd) > 0 && nestedCmd[0] != defaultCmdValue {
|
||||
return nestedCmd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 未找到目标字段/所有解析失败,返回默认值
|
||||
return []uint32{defaultCmdValue}
|
||||
}
|
||||
|
||||
// ParseCmdTagWithStructField 校验字段是否为TomeeHeader(值/指针)并解析cmd标签
|
||||
// 参数 field:结构体字段元信息
|
||||
// 返回值:解析后的cmd切片,非目标类型/解析失败返回错误
|
||||
func ParseCmdTagWithStructField(field reflect.StructField) ([]uint32, error) {
|
||||
|
||||
// 判断字段类型是否为 TomeeHeader 或 *TomeeHeader
|
||||
var isTomeeHeader bool
|
||||
switch {
|
||||
case field.Type == targetType: // 值类型
|
||||
isTomeeHeader = true
|
||||
case field.Type.Kind() == reflect.Ptr && field.Type.Elem() == targetType: // 指针类型
|
||||
isTomeeHeader = true
|
||||
default:
|
||||
isTomeeHeader = false
|
||||
}
|
||||
|
||||
// 非目标类型返回错误
|
||||
if !isTomeeHeader {
|
||||
return nil, fmt.Errorf("field %s (type: %v) is not common.TomeeHeader or *common.TomeeHeader",
|
||||
field.Name, field.Type)
|
||||
}
|
||||
|
||||
// 提取cmd标签
|
||||
cmdStr := field.Tag.Get("cmd")
|
||||
if cmdStr == "" {
|
||||
return nil, fmt.Errorf("field %s cmd tag is empty", field.Name)
|
||||
}
|
||||
|
||||
// 高性能解析标签为uint32切片(替代gconv,减少第三方依赖且可控)
|
||||
parts := strings.Split(cmdStr, "|")
|
||||
result := make([]uint32, 0, len(parts))
|
||||
for idx, s := range parts {
|
||||
// 去除空白字符(兼容标签中意外的空格)
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return nil, fmt.Errorf("field %s cmd tag part %d is empty", field.Name, idx)
|
||||
}
|
||||
|
||||
// 手动解析uint32,比gconv更可控,避免隐式转换问题
|
||||
num, err := strconv.ParseUint(s, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("field %s cmd tag part %d parse error: %v (value: %s)",
|
||||
field.Name, idx, err, s)
|
||||
}
|
||||
result = append(result, uint32(num))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"blazing/logic/service/common"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"strings"
|
||||
|
||||
"bytes"
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/lunixbochs/struc"
|
||||
)
|
||||
|
||||
var Maincontroller = &Controller{} //注入service
|
||||
|
||||
// 分发cmd逻辑实现Controller
|
||||
type Controller struct {
|
||||
Port uint16
|
||||
RPCClient struct {
|
||||
Kick func(uint32) error
|
||||
|
||||
RegisterLogic func(uint16, uint16) error
|
||||
}
|
||||
}
|
||||
|
||||
func ParseCmd[T any](a T, data []byte) T {
|
||||
// := info.NewLoginSidInfo()
|
||||
struc.Unpack(bytes.NewBuffer(data), &a)
|
||||
return a
|
||||
//fmt.Println(pinfo)
|
||||
//login.OnData_1001(pinfo, player)
|
||||
//fmt.Println(data)
|
||||
}
|
||||
|
||||
func Init(isgame bool) { //默认初始化扫描
|
||||
|
||||
// 获取对象的反射值和类型
|
||||
value := reflect.ValueOf(Maincontroller)
|
||||
|
||||
// 获取类型
|
||||
typ := value.Type()
|
||||
|
||||
for i := 0; i < typ.NumMethod(); i++ {
|
||||
method := typ.Method(i)
|
||||
|
||||
methodValue := value.MethodByName(method.Name)
|
||||
//fmt.Println("找到注册方法", method.Name)
|
||||
methodValue.Type().NumIn()
|
||||
|
||||
for _, func_cmd := range getCmd(methodValue.Type().In(0)) {
|
||||
if func_cmd == 0 { //说明不是注册方法
|
||||
glog.Warning(context.Background(), "方法参数必须包含CMD参数", method.Name, "跳过注册")
|
||||
continue
|
||||
}
|
||||
|
||||
if !isgame && func_cmd > 1000 { //判断login服务器
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
if isgame && func_cmd < 1000 { //判断login服务器
|
||||
continue
|
||||
|
||||
}
|
||||
glog.Debug(context.Background(), "注册方法", func_cmd, method.Name)
|
||||
|
||||
_, ok := cool.CmdCache.LoadOrStore(func_cmd, cool.Cmd{
|
||||
Func: methodValue,
|
||||
Req: methodValue.Type().In(0).Elem(),
|
||||
// Res: ,
|
||||
}) //TODO 待实现对不同用户初始化方法以取消全局cmdcache
|
||||
|
||||
if ok { //方法已存在init
|
||||
glog.Error(context.Background(), "方法已存在init,不会初始化后面的方法", func_cmd)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var targetType = reflect.TypeOf(common.TomeeHeader{})
|
||||
|
||||
// 默认返回值(无匹配字段/解析失败时)
|
||||
const defaultCmdValue = 0
|
||||
|
||||
// getCmd 从结构体类型中提取绑定的cmd指令(递归查找嵌套结构体,支持值/指针类型的TomeeHeader)
|
||||
|
||||
// 参数 typ:待解析的结构体类型(支持多层指针)
|
||||
// 返回值:解析到的cmd切片,无匹配/解析失败时返回[defaultCmdValue]
|
||||
func getCmd(typ reflect.Type) []uint32 {
|
||||
// 初始化目标类型(仅第一次调用时执行)
|
||||
|
||||
// 1. 递归解引用所有指针类型(处理 *struct、**struct 等场景)
|
||||
for typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
|
||||
// 2. 非结构体类型直接返回默认值
|
||||
if typ.Kind() != reflect.Struct {
|
||||
return []uint32{defaultCmdValue}
|
||||
}
|
||||
|
||||
// 3. 遍历结构体字段,查找TomeeHeader字段并解析cmd
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
||||
// 4. 调用解析函数,判断是否为目标类型并解析cmd
|
||||
cmdSlice, err := ParseCmdTagWithStructField(field)
|
||||
if err == nil { // 解析成功,直接返回结果
|
||||
return cmdSlice
|
||||
}
|
||||
|
||||
// 5. 递归处理嵌套结构体(值/指针类型)
|
||||
nestedTyp := field.Type
|
||||
if nestedTyp.Kind() == reflect.Ptr {
|
||||
nestedTyp = nestedTyp.Elem()
|
||||
}
|
||||
if nestedTyp.Kind() == reflect.Struct {
|
||||
// 递归查找,找到有效cmd则立即返回
|
||||
if nestedCmd := getCmd(nestedTyp); len(nestedCmd) > 0 && nestedCmd[0] != defaultCmdValue {
|
||||
return nestedCmd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 未找到目标字段/所有解析失败,返回默认值
|
||||
return []uint32{defaultCmdValue}
|
||||
}
|
||||
|
||||
// ParseCmdTagWithStructField 校验字段是否为TomeeHeader(值/指针)并解析cmd标签
|
||||
// 参数 field:结构体字段元信息
|
||||
// 返回值:解析后的cmd切片,非目标类型/解析失败返回错误
|
||||
func ParseCmdTagWithStructField(field reflect.StructField) ([]uint32, error) {
|
||||
|
||||
// 判断字段类型是否为 TomeeHeader 或 *TomeeHeader
|
||||
var isTomeeHeader bool
|
||||
switch {
|
||||
case field.Type == targetType: // 值类型
|
||||
isTomeeHeader = true
|
||||
case field.Type.Kind() == reflect.Ptr && field.Type.Elem() == targetType: // 指针类型
|
||||
isTomeeHeader = true
|
||||
default:
|
||||
isTomeeHeader = false
|
||||
}
|
||||
|
||||
// 非目标类型返回错误
|
||||
if !isTomeeHeader {
|
||||
return nil, fmt.Errorf("field %s (type: %v) is not common.TomeeHeader or *common.TomeeHeader",
|
||||
field.Name, field.Type)
|
||||
}
|
||||
|
||||
// 提取cmd标签
|
||||
cmdStr := field.Tag.Get("cmd")
|
||||
if cmdStr == "" {
|
||||
return nil, fmt.Errorf("field %s cmd tag is empty", field.Name)
|
||||
}
|
||||
|
||||
// 高性能解析标签为uint32切片(替代gconv,减少第三方依赖且可控)
|
||||
parts := strings.Split(cmdStr, "|")
|
||||
result := make([]uint32, 0, len(parts))
|
||||
for idx, s := range parts {
|
||||
// 去除空白字符(兼容标签中意外的空格)
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return nil, fmt.Errorf("field %s cmd tag part %d is empty", field.Name, idx)
|
||||
}
|
||||
|
||||
// 手动解析uint32,比gconv更可控,避免隐式转换问题
|
||||
num, err := strconv.ParseUint(s, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("field %s cmd tag part %d parse error: %v (value: %s)",
|
||||
field.Name, idx, err, s)
|
||||
}
|
||||
result = append(result, uint32(num))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/rpc"
|
||||
"blazing/common/socket/errorcode"
|
||||
"blazing/logic/service/user"
|
||||
"blazing/modules/base/service"
|
||||
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"github.com/panjf2000/gnet/v2"
|
||||
)
|
||||
|
||||
var sg singleflight.Group
|
||||
|
||||
var ServerList []rpc.ServerInfo
|
||||
|
||||
func fetchData() (any, error) {
|
||||
ServerList = rpc.GetServerInfoList() //todo 待修改增加缓存
|
||||
return ServerList, nil
|
||||
}
|
||||
|
||||
// 处理命令: 105
|
||||
func (h *Controller) COMMEND_ONLINE(data *user.SidInfo, c gnet.Conn) (result *rpc.CommendSvrInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
|
||||
result = rpc.NewInInfo()
|
||||
|
||||
if service.NewBaseSysUserService().GetPerson(data.Head.UserID).Debug == 1 {
|
||||
result.IsVip = 1
|
||||
}
|
||||
v, _, _ := sg.Do("GetServerInfoList", fetchData)
|
||||
|
||||
result.ServerList = v.([]rpc.ServerInfo) //todo 待修改增加缓存
|
||||
|
||||
return
|
||||
|
||||
//return //TODO 这里待实现改成接口调用Ret方法
|
||||
}
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/rpc"
|
||||
"blazing/common/socket/errorcode"
|
||||
"blazing/logic/service/user"
|
||||
"blazing/modules/base/service"
|
||||
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"github.com/panjf2000/gnet/v2"
|
||||
)
|
||||
|
||||
var sg singleflight.Group
|
||||
|
||||
var ServerList []rpc.ServerInfo
|
||||
|
||||
func fetchData() (any, error) {
|
||||
ServerList = rpc.GetServerInfoList() //todo 待修改增加缓存
|
||||
return ServerList, nil
|
||||
}
|
||||
|
||||
// 处理命令: 105
|
||||
func (h *Controller) COMMEND_ONLINE(data *user.SidInfo, c gnet.Conn) (result *rpc.CommendSvrInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
|
||||
result = rpc.NewInInfo()
|
||||
|
||||
if service.NewBaseSysUserService().GetPerson(data.Head.UserID).Debug == 1 {
|
||||
result.IsVip = 1
|
||||
}
|
||||
v, _, _ := sg.Do("GetServerInfoList", fetchData)
|
||||
|
||||
result.ServerList = v.([]rpc.ServerInfo) //todo 待修改增加缓存
|
||||
|
||||
return
|
||||
|
||||
//return //TODO 这里待实现改成接口调用Ret方法
|
||||
}
|
||||
|
||||
@@ -1,78 +1,78 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/data/share"
|
||||
"blazing/cool"
|
||||
"fmt"
|
||||
|
||||
"blazing/common/socket/errorcode"
|
||||
|
||||
"blazing/logic/service/user"
|
||||
|
||||
"blazing/logic/service/player"
|
||||
"blazing/logic/service/space"
|
||||
"blazing/modules/base/service"
|
||||
blservice "blazing/modules/blazing/service"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/panjf2000/gnet/v2"
|
||||
)
|
||||
|
||||
func IsToday(t time.Time) bool {
|
||||
// 获取当前时间
|
||||
now := time.Now()
|
||||
|
||||
// 比较年、月、日是否相同
|
||||
return t.Year() == now.Year() &&
|
||||
t.Month() == now.Month() &&
|
||||
t.Day() == now.Day()
|
||||
}
|
||||
|
||||
// 处理命令: 1001
|
||||
func (h *Controller) Login(data *user.MAIN_LOGIN_IN, c gnet.Conn) (result *user.LoginMSInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
|
||||
tt := data.CheakSession()
|
||||
if !tt {
|
||||
|
||||
defer c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
cool.Loger.Info(context.TODO(), "准备踢人")
|
||||
err1 := h.RPCClient.Kick(data.Head.UserID) //先踢人
|
||||
if err1 != nil {
|
||||
fmt.Println("踢人失败", err)
|
||||
}
|
||||
//player.KickPlayer(data.Head.UserID)
|
||||
cool.Loger.Info(context.TODO(), "踢人请求完成,继续登录流程")
|
||||
|
||||
// <-time.After(time.Millisecond * 3000)
|
||||
share.ShareManager.SetUserOnline(data.Head.UserID, h.Port) //设置用户登录服务器
|
||||
t := player.GetPlayer(c, data.Head.UserID)
|
||||
if t == nil {
|
||||
cool.Loger.Error(context.Background(), "获取玩家失败", data.Head.UserID)
|
||||
|
||||
defer c.Close()
|
||||
return
|
||||
}
|
||||
t.Service = blservice.NewUserService(data.Head.UserID)
|
||||
t.User = service.NewBaseSysUserService()
|
||||
t.Info = t.Service.Info.Personself()
|
||||
|
||||
if t.Info == nil {
|
||||
defer c.Close()
|
||||
return
|
||||
}
|
||||
t.Info.UserID = data.Head.UserID
|
||||
t.Logintime = uint32(time.Now().Unix()) //保存时间戳
|
||||
|
||||
t.CompleteLogin() //通知客户端登录成功
|
||||
|
||||
result = user.NewOutInfo() //设置登录消息
|
||||
|
||||
result.PlayerInfo = *t.Info
|
||||
defer space.GetSpace(t.Info.MapID).EnterMap(t)
|
||||
|
||||
return result, 0
|
||||
|
||||
}
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/data/share"
|
||||
"blazing/cool"
|
||||
"fmt"
|
||||
|
||||
"blazing/common/socket/errorcode"
|
||||
|
||||
"blazing/logic/service/user"
|
||||
|
||||
"blazing/logic/service/player"
|
||||
"blazing/logic/service/space"
|
||||
"blazing/modules/base/service"
|
||||
blservice "blazing/modules/blazing/service"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/panjf2000/gnet/v2"
|
||||
)
|
||||
|
||||
func IsToday(t time.Time) bool {
|
||||
// 获取当前时间
|
||||
now := time.Now()
|
||||
|
||||
// 比较年、月、日是否相同
|
||||
return t.Year() == now.Year() &&
|
||||
t.Month() == now.Month() &&
|
||||
t.Day() == now.Day()
|
||||
}
|
||||
|
||||
// 处理命令: 1001
|
||||
func (h *Controller) Login(data *user.MAIN_LOGIN_IN, c gnet.Conn) (result *user.LoginMSInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
|
||||
tt := data.CheakSession()
|
||||
if !tt {
|
||||
|
||||
defer c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
cool.Loger.Info(context.TODO(), "准备踢人")
|
||||
err1 := h.RPCClient.Kick(data.Head.UserID) //先踢人
|
||||
if err1 != nil {
|
||||
fmt.Println("踢人失败", err)
|
||||
}
|
||||
//player.KickPlayer(data.Head.UserID)
|
||||
cool.Loger.Info(context.TODO(), "踢人请求完成,继续登录流程")
|
||||
|
||||
// <-time.After(time.Millisecond * 3000)
|
||||
share.ShareManager.SetUserOnline(data.Head.UserID, h.Port) //设置用户登录服务器
|
||||
t := player.GetPlayer(c, data.Head.UserID)
|
||||
if t == nil {
|
||||
cool.Loger.Error(context.Background(), "获取玩家失败", data.Head.UserID)
|
||||
|
||||
defer c.Close()
|
||||
return
|
||||
}
|
||||
t.Service = blservice.NewUserService(data.Head.UserID)
|
||||
t.User = service.NewBaseSysUserService()
|
||||
t.Info = t.Service.Info.Personself()
|
||||
|
||||
if t.Info == nil {
|
||||
defer c.Close()
|
||||
return
|
||||
}
|
||||
t.Info.UserID = data.Head.UserID
|
||||
t.Logintime = uint32(time.Now().Unix()) //保存时间戳
|
||||
|
||||
t.CompleteLogin() //通知客户端登录成功
|
||||
|
||||
result = user.NewOutInfo() //设置登录消息
|
||||
|
||||
result.PlayerInfo = *t.Info
|
||||
defer space.GetSpace(t.Info.MapID).EnterMap(t)
|
||||
|
||||
return result, 0
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ func (h *Controller) MapLeave(data *maps.LeaveMapInboundInfo, c *player.Player)
|
||||
func (h *Controller) MapList(data *maps.ListMapPlayerInboundInfo, c *player.Player) (result *info.ListMapPlayerOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
|
||||
atomic.StoreUint32(&c.Canmon, 2)
|
||||
|
||||
|
||||
result = &info.ListMapPlayerOutboundInfo{
|
||||
Player: c.GetSpace().GetInfo(c),
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/socket/errorcode"
|
||||
|
||||
"blazing/logic/service/player"
|
||||
"blazing/logic/service/systemtime"
|
||||
)
|
||||
|
||||
func (h Controller) SystemTimeInfo(data *systemtime.InInfo, c *player.Player) (result *systemtime.OutInfo, err errorcode.ErrorCode) {
|
||||
|
||||
return systemtime.NewOutInfo(), 0
|
||||
}
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/socket/errorcode"
|
||||
|
||||
"blazing/logic/service/player"
|
||||
"blazing/logic/service/systemtime"
|
||||
)
|
||||
|
||||
func (h Controller) SystemTimeInfo(data *systemtime.InInfo, c *player.Player) (result *systemtime.OutInfo, err errorcode.ErrorCode) {
|
||||
|
||||
return systemtime.NewOutInfo(), 0
|
||||
}
|
||||
|
||||
166
logic/main.go
166
logic/main.go
@@ -1,83 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
|
||||
"blazing/logic/service/fight"
|
||||
"blazing/logic/service/player"
|
||||
|
||||
"blazing/cool"
|
||||
|
||||
//"blazing/o/service"
|
||||
"blazing/modules/base/service"
|
||||
blservice "blazing/modules/blazing/service"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
|
||||
func PprofWeb() {
|
||||
runtime.SetMutexProfileFraction(1) // (非必需)开启对锁调用的跟踪
|
||||
runtime.SetBlockProfileRate(1) // (非必需)开启对阻塞操作的跟踪
|
||||
err := http.ListenAndServe(":9909", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
func signalHandlerForMain(sig os.Signal) {
|
||||
fight.Fightpool.ReleaseTimeout(0)
|
||||
|
||||
player.Mainplayer.Range(func(key uint32, value *player.Player) bool {
|
||||
value.Save()
|
||||
|
||||
return true
|
||||
})
|
||||
fmt.Println("MainProcess is shutting down due to signal:", sig.String())
|
||||
}
|
||||
|
||||
func main() {
|
||||
//loadAccounts()
|
||||
// if cool.IsRedisMode {
|
||||
// go cool.ListenFunc(gctx.New())
|
||||
// }
|
||||
// 解析命令行参数
|
||||
cool.Config.PortBL = gcmd.GetOpt("port", "1").Uint16()
|
||||
go Start(cool.Config.PortBL) //注入service
|
||||
if cool.Config.PortBL == 1 || cool.Config.PortBL == 2 { //只分析1服务器的
|
||||
go PprofWeb()
|
||||
}
|
||||
|
||||
fmt.Println("Process start, pid:", os.Getpid())
|
||||
|
||||
gproc.AddSigHandlerShutdown(
|
||||
|
||||
signalHandlerForMain,
|
||||
)
|
||||
|
||||
gproc.Listen()
|
||||
}
|
||||
|
||||
func loadAccounts() {
|
||||
t1, _ := os.Getwd()
|
||||
data, err := os.ReadFile(t1 + "/b.csv")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
t := service.NewBaseSysUserService().GetEamil(line)
|
||||
|
||||
blservice.NewUserService(uint32(t.ID)).Info.Reg(t.Username, 0)
|
||||
}
|
||||
//fmt.Printf("加载 %d 个账号\n", len(accounts))
|
||||
}
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
|
||||
"blazing/logic/service/fight"
|
||||
"blazing/logic/service/player"
|
||||
|
||||
"blazing/cool"
|
||||
|
||||
//"blazing/o/service"
|
||||
"blazing/modules/base/service"
|
||||
blservice "blazing/modules/blazing/service"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
|
||||
func PprofWeb() {
|
||||
runtime.SetMutexProfileFraction(1) // (非必需)开启对锁调用的跟踪
|
||||
runtime.SetBlockProfileRate(1) // (非必需)开启对阻塞操作的跟踪
|
||||
err := http.ListenAndServe(":9909", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
func signalHandlerForMain(sig os.Signal) {
|
||||
fight.Fightpool.ReleaseTimeout(0)
|
||||
|
||||
player.Mainplayer.Range(func(key uint32, value *player.Player) bool {
|
||||
value.Save()
|
||||
|
||||
return true
|
||||
})
|
||||
fmt.Println("MainProcess is shutting down due to signal:", sig.String())
|
||||
}
|
||||
|
||||
func main() {
|
||||
//loadAccounts()
|
||||
// if cool.IsRedisMode {
|
||||
// go cool.ListenFunc(gctx.New())
|
||||
// }
|
||||
// 解析命令行参数
|
||||
cool.Config.PortBL = gcmd.GetOpt("port", "1").Uint16()
|
||||
go Start(cool.Config.PortBL) //注入service
|
||||
if cool.Config.PortBL == 1 || cool.Config.PortBL == 2 { //只分析1服务器的
|
||||
go PprofWeb()
|
||||
}
|
||||
|
||||
fmt.Println("Process start, pid:", os.Getpid())
|
||||
|
||||
gproc.AddSigHandlerShutdown(
|
||||
|
||||
signalHandlerForMain,
|
||||
)
|
||||
|
||||
gproc.Listen()
|
||||
}
|
||||
|
||||
func loadAccounts() {
|
||||
t1, _ := os.Getwd()
|
||||
data, err := os.ReadFile(t1 + "/b.csv")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
t := service.NewBaseSysUserService().GetEamil(line)
|
||||
|
||||
blservice.NewUserService(uint32(t.ID)).Info.Reg(t.Username, 0)
|
||||
}
|
||||
//fmt.Printf("加载 %d 个账号\n", len(accounts))
|
||||
}
|
||||
|
||||
154
logic/server.go
154
logic/server.go
@@ -1,77 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/common/rpc"
|
||||
"blazing/common/socket"
|
||||
|
||||
"blazing/cool"
|
||||
"blazing/logic/controller"
|
||||
|
||||
blservice "blazing/modules/blazing/service"
|
||||
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
const (
|
||||
minRandomPort = 10000
|
||||
maxRandomPort = 60000
|
||||
maxPortRetryCount = 5
|
||||
)
|
||||
|
||||
var candidatePorts = cool.Config.GamePort
|
||||
|
||||
// determinePort 确定服务器使用的端口
|
||||
func determinePort() (int, error) {
|
||||
|
||||
// 尝试从指定端口列表中找可用端口,最多尝试maxPortRetryCount轮
|
||||
for i := 0; i < maxPortRetryCount; i++ {
|
||||
// 遍历指定的端口列表
|
||||
for _, port := range candidatePorts {
|
||||
if isPortAvailable(port) {
|
||||
return int(port), nil
|
||||
}
|
||||
log.Printf("Port %d is not available, checking next...", port)
|
||||
}
|
||||
log.Printf("All candidate ports are in use, retrying round %d...", i+1)
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("failed to find available port after %d rounds of checking", maxPortRetryCount)
|
||||
}
|
||||
|
||||
// isPortAvailable 检查端口是否可用
|
||||
func isPortAvailable(port uint64) bool {
|
||||
address := fmt.Sprintf(":%d", port)
|
||||
listener, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer listener.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果id是0,那就是login server
|
||||
func Start(serverid uint16) {
|
||||
// 确定端口
|
||||
port, err := determinePort()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to determine port: %v", err)
|
||||
}
|
||||
ser := socket.NewServer(
|
||||
socket.WithCORS(),
|
||||
socket.WithPort(port),
|
||||
)
|
||||
// go func() {
|
||||
t := rpc.StartClient(serverid, uint16(port), ser)
|
||||
|
||||
controller.Maincontroller.RPCClient = *t //将RPC赋值Start
|
||||
controller.Maincontroller.Port = uint16(port) //赋值服务器ID
|
||||
controller.Init(true)
|
||||
xmlres.Initfile()
|
||||
blservice.NewLoginServiceService().SetServerID(serverid, gconv.Uint16(port))
|
||||
ser.Boot()
|
||||
}
|
||||
package main
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/common/rpc"
|
||||
"blazing/common/socket"
|
||||
|
||||
"blazing/cool"
|
||||
"blazing/logic/controller"
|
||||
|
||||
blservice "blazing/modules/blazing/service"
|
||||
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
const (
|
||||
minRandomPort = 10000
|
||||
maxRandomPort = 60000
|
||||
maxPortRetryCount = 5
|
||||
)
|
||||
|
||||
var candidatePorts = cool.Config.GamePort
|
||||
|
||||
// determinePort 确定服务器使用的端口
|
||||
func determinePort() (int, error) {
|
||||
|
||||
// 尝试从指定端口列表中找可用端口,最多尝试maxPortRetryCount轮
|
||||
for i := 0; i < maxPortRetryCount; i++ {
|
||||
// 遍历指定的端口列表
|
||||
for _, port := range candidatePorts {
|
||||
if isPortAvailable(port) {
|
||||
return int(port), nil
|
||||
}
|
||||
log.Printf("Port %d is not available, checking next...", port)
|
||||
}
|
||||
log.Printf("All candidate ports are in use, retrying round %d...", i+1)
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("failed to find available port after %d rounds of checking", maxPortRetryCount)
|
||||
}
|
||||
|
||||
// isPortAvailable 检查端口是否可用
|
||||
func isPortAvailable(port uint64) bool {
|
||||
address := fmt.Sprintf(":%d", port)
|
||||
listener, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer listener.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
// 如果id是0,那就是login server
|
||||
func Start(serverid uint16) {
|
||||
// 确定端口
|
||||
port, err := determinePort()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to determine port: %v", err)
|
||||
}
|
||||
ser := socket.NewServer(
|
||||
socket.WithCORS(),
|
||||
socket.WithPort(port),
|
||||
)
|
||||
// go func() {
|
||||
t := rpc.StartClient(serverid, uint16(port), ser)
|
||||
|
||||
controller.Maincontroller.RPCClient = *t //将RPC赋值Start
|
||||
controller.Maincontroller.Port = uint16(port) //赋值服务器ID
|
||||
controller.Init(true)
|
||||
xmlres.Initfile()
|
||||
blservice.NewLoginServiceService().SetServerID(serverid, gconv.Uint16(port))
|
||||
ser.Boot()
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (f *FightC) ChangePet(c common.PlayerI, id uint32) {
|
||||
|
||||
selfinput.CurrentPet, ret.Reason = selfinput.GetPet(id)
|
||||
c.SendPackCmd(2407, &ret.Reason)
|
||||
|
||||
|
||||
f.actionChan <- ret
|
||||
}
|
||||
|
||||
|
||||
@@ -29,4 +29,4 @@ func (e *EffectNode) Action_end() bool {
|
||||
func (e *EffectNode) HookAction() bool {
|
||||
// panic("not implemented") // TODO: Implement
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"blazing/common/data/share"
|
||||
"blazing/logic/service/common"
|
||||
|
||||
"blazing/modules/blazing/model"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
)
|
||||
|
||||
// LoginSidInfo 登录携带的凭证结构体
|
||||
type MAIN_LOGIN_IN struct { //这里直接使用组合来实现将传入的原始头部数据和结构体参数序列化
|
||||
Head common.TomeeHeader `cmd:"1001" struc:"skip"` //玩家登录
|
||||
|
||||
Sid []byte `struc:"[16]byte"` // 登录会话ID,固定长度16字节
|
||||
|
||||
}
|
||||
|
||||
func (l *MAIN_LOGIN_IN) CheakSession() bool {
|
||||
// tt, _ := cool.CacheManager.Keys(context.Background())
|
||||
//g.Dump(tt)
|
||||
t1 := hex.EncodeToString(l.Sid)
|
||||
|
||||
t, err := share.ShareManager.GetSession(t1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
share.ShareManager.DeleteSession(t1)
|
||||
glog.Debug(context.Background(), "后端获取", t1, t, err)
|
||||
return t == l.Head.UserID
|
||||
}
|
||||
|
||||
type LoginMSInfo struct {
|
||||
model.PlayerInfo
|
||||
}
|
||||
|
||||
func NewOutInfo() *LoginMSInfo {
|
||||
|
||||
l := &LoginMSInfo{
|
||||
PlayerInfo: model.NewPlayerInfo(),
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
package user
|
||||
|
||||
import (
|
||||
"blazing/common/data/share"
|
||||
"blazing/logic/service/common"
|
||||
|
||||
"blazing/modules/blazing/model"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
)
|
||||
|
||||
// LoginSidInfo 登录携带的凭证结构体
|
||||
type MAIN_LOGIN_IN struct { //这里直接使用组合来实现将传入的原始头部数据和结构体参数序列化
|
||||
Head common.TomeeHeader `cmd:"1001" struc:"skip"` //玩家登录
|
||||
|
||||
Sid []byte `struc:"[16]byte"` // 登录会话ID,固定长度16字节
|
||||
|
||||
}
|
||||
|
||||
func (l *MAIN_LOGIN_IN) CheakSession() bool {
|
||||
// tt, _ := cool.CacheManager.Keys(context.Background())
|
||||
//g.Dump(tt)
|
||||
t1 := hex.EncodeToString(l.Sid)
|
||||
|
||||
t, err := share.ShareManager.GetSession(t1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
share.ShareManager.DeleteSession(t1)
|
||||
glog.Debug(context.Background(), "后端获取", t1, t, err)
|
||||
return t == l.Head.UserID
|
||||
}
|
||||
|
||||
type LoginMSInfo struct {
|
||||
model.PlayerInfo
|
||||
}
|
||||
|
||||
func NewOutInfo() *LoginMSInfo {
|
||||
|
||||
l := &LoginMSInfo{
|
||||
PlayerInfo: model.NewPlayerInfo(),
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user