"feat(socket): 添加跨域请求处理并集成enum依赖,优化TCP连接数据注入"
This commit is contained in:
217
common/core/info/LoginUserInfo.go
Normal file
217
common/core/info/LoginUserInfo.go
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
package info
|
||||||
|
|
||||||
|
// Point 表示坐标结构
|
||||||
|
type Point struct {
|
||||||
|
X uint32
|
||||||
|
Y uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeamInfo 战队信息
|
||||||
|
type TeamInfo struct {
|
||||||
|
// 此处应包含TeamInfo的具体字段,原Java代码中未给出详细定义
|
||||||
|
// 请根据实际需求补充
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeamPKInfo 战队PK信息
|
||||||
|
type TeamPKInfo struct {
|
||||||
|
// 此处应包含TeamPKInfo的具体字段,原Java代码中未给出详细定义
|
||||||
|
// 请根据实际需求补充
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeopleItemInfo 人物物品信息
|
||||||
|
type PeopleItemInfo struct {
|
||||||
|
// 此处应包含PeopleItemInfo的具体字段,原Java代码中未给出详细定义
|
||||||
|
// 请根据实际需求补充
|
||||||
|
}
|
||||||
|
|
||||||
|
// PetInfo 精灵信息
|
||||||
|
type PetInfo struct {
|
||||||
|
// 此处应包含PetInfo的具体字段,原Java代码中未给出详细定义
|
||||||
|
// 请根据实际需求补充
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginUserInfo 登录用户信息结构体
|
||||||
|
type LoginUserInfo struct {
|
||||||
|
// 米米号 通过sid拿到
|
||||||
|
UserId uint64
|
||||||
|
// 注册时间(按秒的时间戳)
|
||||||
|
RegisterTime uint64
|
||||||
|
// 16字节昵称
|
||||||
|
Nick [16]byte `array_serialize:"fixed_length,16"`
|
||||||
|
// 暂时不明建议先给固定值0
|
||||||
|
Vip uint16 `ushort:"true"`
|
||||||
|
// 暂时不明建议先给固定值15
|
||||||
|
Viped uint16 `ushort:"true"`
|
||||||
|
// 暂时不明建议先给固定值0
|
||||||
|
DsFlag uint64
|
||||||
|
// 机器人人物颜色 00 rgb
|
||||||
|
Color uint64
|
||||||
|
// 暂时不明建议先给固定值0
|
||||||
|
Texture uint64
|
||||||
|
// 暂时不明建议先给固定值3000
|
||||||
|
Energy uint64 `default:"3000"`
|
||||||
|
// 赛尔豆
|
||||||
|
Coins uint64
|
||||||
|
// 暂时不明建议先给固定值0
|
||||||
|
FightBadge uint64
|
||||||
|
// 上线的地图id
|
||||||
|
MapID uint64
|
||||||
|
// 上线的坐标 2个uint
|
||||||
|
Pos Point `array_serialize:"fixed_length,8"`
|
||||||
|
// 已经消耗掉的时间(秒为单位)
|
||||||
|
TimeToday uint64
|
||||||
|
// 总电池限制(秒为单位)
|
||||||
|
TimeLimit uint64
|
||||||
|
// 暂时不明感觉是某种活动建议先给固定值0(只能0或1)
|
||||||
|
IsClothHalfDay byte
|
||||||
|
// 暂时不明感觉是某种活动建议先给固定值0(只能0或1)
|
||||||
|
IsRoomHalfDay byte
|
||||||
|
// 暂时不明感觉是某种活动建议先给固定值0(只能0或1)
|
||||||
|
IFortressHalfDay byte
|
||||||
|
// 暂时不明感觉是某种活动建议先给固定值0(只能0或1)
|
||||||
|
IsHQHalfDay byte
|
||||||
|
// 暂时不明建议先给固定值0
|
||||||
|
LoginCount uint64
|
||||||
|
// 邀请活动建议先给固定值0
|
||||||
|
Inviter uint64
|
||||||
|
// 邀请活动建议先给固定值0
|
||||||
|
NewInviteeCount uint64
|
||||||
|
// 超no等级建议固定8
|
||||||
|
VipLevel uint64 `default:"8"`
|
||||||
|
// 超no的vip值建议固定80000
|
||||||
|
VipValue uint64 `default:"80000"`
|
||||||
|
// 超no的外形等级建议固定1(暂定)
|
||||||
|
VipStage uint64 `default:"1"`
|
||||||
|
// nono是否自动充电 建议固定1
|
||||||
|
AutoCharge uint64 `default:"1"`
|
||||||
|
// 超no的结束时间建议尽可能大
|
||||||
|
VipEndTime uint64 `default:"4294967295"`
|
||||||
|
// 邀请活动建议先给固定值0
|
||||||
|
FreshManBonus uint64
|
||||||
|
// 超no芯片列表*(80字节)
|
||||||
|
NonoChipList [80]byte `array_serialize:"fixed_length,80"`
|
||||||
|
// 50字节,默认值为3
|
||||||
|
DailyResArr [50]byte `array_serialize:"fixed_length,50"`
|
||||||
|
// 教官id
|
||||||
|
TeacherID uint64
|
||||||
|
// 学员id
|
||||||
|
StudentID uint64
|
||||||
|
// 毕业人数
|
||||||
|
GraduationCount uint64
|
||||||
|
// 默认值为0
|
||||||
|
MaxPuniLv uint64 `default:"0"`
|
||||||
|
// 精灵的最高等级
|
||||||
|
PetMaxLevel uint64
|
||||||
|
// 所有的精灵的数量
|
||||||
|
AllPetNumber uint64
|
||||||
|
// 精灵王之战胜场
|
||||||
|
MonKingWin uint64
|
||||||
|
// 勇者之塔当前到达的层数
|
||||||
|
CurrentStage uint64
|
||||||
|
// 试炼之塔最大胜利的层数
|
||||||
|
MaxStage uint64
|
||||||
|
// 试炼之塔当前到达的层数
|
||||||
|
CurrentFreshStage uint64
|
||||||
|
// 试炼之塔最大胜利的层数
|
||||||
|
MaxFreshStage uint64
|
||||||
|
// 星际擂台连胜
|
||||||
|
MaxArenaWins uint64
|
||||||
|
// 未知默认0
|
||||||
|
TwoTimes uint64 `default:"0"`
|
||||||
|
// 未知默认0
|
||||||
|
ThreeTimes uint64 `default:"0"`
|
||||||
|
// 是否自动战斗(未知默认值0)
|
||||||
|
AutoFight uint64 `default:"0"`
|
||||||
|
// 自动战斗剩余的场次(未知默认值0)
|
||||||
|
AutoFightTime uint64 `default:"0"`
|
||||||
|
// 能量吸收仪剩余次数(未知待定默认值0)
|
||||||
|
EnergyTime uint64 `default:"0"`
|
||||||
|
// 学习力吸收仪剩余次数(未知待定默认值0)
|
||||||
|
LearnTimes uint64 `default:"0"`
|
||||||
|
// 未知默认0
|
||||||
|
MonBattleMedal uint64 `default:"0"`
|
||||||
|
// 未知默认0
|
||||||
|
RecordCount uint64 `default:"0"`
|
||||||
|
// 未知默认0
|
||||||
|
ObtainTm uint64 `default:"0"`
|
||||||
|
// 当前在孵化的元神珠id
|
||||||
|
SoulBeadItemID uint64
|
||||||
|
// 未知默认0
|
||||||
|
ExpireTm uint64 `default:"0"`
|
||||||
|
// 未知默认0
|
||||||
|
FuseTimes uint64 `default:"0"`
|
||||||
|
// 玩家有没有nono
|
||||||
|
HasNono uint64 `default:"1"`
|
||||||
|
// 玩家有没有超能nono
|
||||||
|
SuperNono uint64 `default:"1"`
|
||||||
|
// 默认值-1
|
||||||
|
NonoState uint64 `default:"4294967295"`
|
||||||
|
// nono的颜色
|
||||||
|
NonoColor uint64
|
||||||
|
// nono的名字 必须要补齐到16位
|
||||||
|
NonoNick [16]byte `array_serialize:"fixed_length,16"`
|
||||||
|
// 猜测为战队信息24字节
|
||||||
|
TeamInfo TeamInfo `array_serialize:"fixed_length,24"`
|
||||||
|
// 8字节
|
||||||
|
TeamPkInfo TeamPKInfo `array_serialize:"fixed_length,8"`
|
||||||
|
// 1字节 无内容
|
||||||
|
Reserved byte
|
||||||
|
// 默认值为0
|
||||||
|
Badge uint64 `default:"0"`
|
||||||
|
// 未知(27字节,默认值为3)
|
||||||
|
Reserved1 [27]byte `array_serialize:"fixed_length,27"`
|
||||||
|
// 任务状态数组(500字节,3为已经完成,建议默认值为3)
|
||||||
|
TaskList [500]byte `array_serialize:"fixed_length,500"`
|
||||||
|
// 精灵背包内的信息由于特性精灵的存在精灵背包不定长 如果有特性占199字节 如果没特性 一个精灵占175字节
|
||||||
|
PetList []PetInfo
|
||||||
|
// 穿戴装备 8字节
|
||||||
|
Clothes []PeopleItemInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLoginUserInfo 创建新的登录用户信息实例,设置默认值
|
||||||
|
func NewLoginUserInfo() *LoginUserInfo {
|
||||||
|
info := &LoginUserInfo{
|
||||||
|
Vip: 0,
|
||||||
|
Viped: 15,
|
||||||
|
Energy: 3000,
|
||||||
|
VipLevel: 8,
|
||||||
|
VipValue: 80000,
|
||||||
|
VipStage: 1,
|
||||||
|
AutoCharge: 1,
|
||||||
|
VipEndTime: uint64(^uint32(0)),
|
||||||
|
TwoTimes: 0,
|
||||||
|
ThreeTimes: 0,
|
||||||
|
AutoFight: 0,
|
||||||
|
AutoFightTime: 0,
|
||||||
|
EnergyTime: 0,
|
||||||
|
LearnTimes: 0,
|
||||||
|
MonBattleMedal: 0,
|
||||||
|
RecordCount: 0,
|
||||||
|
ObtainTm: 0,
|
||||||
|
ExpireTm: 0,
|
||||||
|
FuseTimes: 0,
|
||||||
|
HasNono: 1,
|
||||||
|
SuperNono: 1,
|
||||||
|
NonoState: uint64(^uint32(0)),
|
||||||
|
Badge: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化固定长度数组
|
||||||
|
for i := range info.NonoChipList {
|
||||||
|
info.NonoChipList[i] = 0
|
||||||
|
}
|
||||||
|
for i := range info.DailyResArr {
|
||||||
|
info.DailyResArr[i] = 3
|
||||||
|
}
|
||||||
|
for i := range info.Reserved1 {
|
||||||
|
info.Reserved1[i] = 3
|
||||||
|
}
|
||||||
|
for i := range info.TaskList {
|
||||||
|
info.TaskList[i] = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化nono昵称
|
||||||
|
copy(info.NonoNick[:], "nono")
|
||||||
|
|
||||||
|
return info
|
||||||
|
}
|
||||||
41
common/core/info/ServerInfo.go
Normal file
41
common/core/info/ServerInfo.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package info
|
||||||
|
|
||||||
|
// ServerInfo 服务器信息结构体
|
||||||
|
type ServerInfo struct {
|
||||||
|
// 连接ID, 即服务器序号
|
||||||
|
OnlineID uint32
|
||||||
|
// 当前服务器玩家在线数量, 供SWF显示
|
||||||
|
UserCnt uint32
|
||||||
|
// 服务器IP, 16字节UTF-8, 不足16补齐到16
|
||||||
|
IP []byte `v:"FIXED_LENGTH|length:16"`
|
||||||
|
// 端口
|
||||||
|
Port uint16
|
||||||
|
// 好友在线的个数
|
||||||
|
Friends uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerInfo 创建新的服务器信息实例
|
||||||
|
func NewServerInfo() *ServerInfo {
|
||||||
|
return &ServerInfo{
|
||||||
|
OnlineID: 0,
|
||||||
|
UserCnt: 0,
|
||||||
|
IP: []byte{},
|
||||||
|
Port: 0,
|
||||||
|
Friends: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // SetIP 设置IP地址并自动填充到16字节
|
||||||
|
// func (s *ServerInfo) SetIP(ip string) {
|
||||||
|
// copy(s.IP[:], ip)
|
||||||
|
// if len(ip) < 16 {
|
||||||
|
// for i := len(ip); i < 16; i++ {
|
||||||
|
// s.IP[i] = 0 // 用0填充剩余字节
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // GetIP 获取IP地址(去除填充的0)
|
||||||
|
// func (s *ServerInfo) GetIP() string {
|
||||||
|
// return strings.TrimRight(string(s.IP[:]), "\x00")
|
||||||
|
// }
|
||||||
@@ -7,6 +7,7 @@ require github.com/panjf2000/gnet v1.6.7
|
|||||||
require (
|
require (
|
||||||
github.com/panjf2000/ants/v2 v2.11.3 // indirect
|
github.com/panjf2000/ants/v2 v2.11.3 // indirect
|
||||||
github.com/panjf2000/gnet/v2 v2.5.0 // indirect
|
github.com/panjf2000/gnet/v2 v2.5.0 // indirect
|
||||||
|
github.com/tnnmigga/enum v1.0.2 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/tnnmigga/enum v1.0.2 h1:Yvchx0Esc01X5HiphW78sKzH/RXKttdFsfPO1ARiOa4=
|
||||||
|
github.com/tnnmigga/enum v1.0.2/go.mod h1:QaBFBwGJi/2GAM34b2pz6UL2NRtl2TRZ8lXp4vGwqhA=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
|||||||
@@ -1,360 +0,0 @@
|
|||||||
package socket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server 游戏服务器核心类,管理玩家、星球和游戏逻辑
|
|
||||||
type Server struct {
|
|
||||||
players map[int64]*entity.Player
|
|
||||||
planets map[int64]*planet.Planet
|
|
||||||
mutex sync.RWMutex
|
|
||||||
gameRepo repo.GameResourceRepo
|
|
||||||
serverRepo repo.ServerRepo
|
|
||||||
accountRepo repo.AccountRepo
|
|
||||||
playerInfoRepo repo.PlayerInfoRepo
|
|
||||||
playerItemRepo repo.PlayerItemInfoRepo
|
|
||||||
petRepo repo.PetRepo
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServer 创建新的游戏服务器实例
|
|
||||||
func NewServer(
|
|
||||||
gameRepo repo.GameResourceRepo,
|
|
||||||
serverRepo repo.ServerRepo,
|
|
||||||
accountRepo repo.AccountRepo,
|
|
||||||
playerInfoRepo repo.PlayerInfoRepo,
|
|
||||||
playerItemRepo repo.PlayerItemInfoRepo,
|
|
||||||
petRepo repo.PetRepo,
|
|
||||||
) *Server {
|
|
||||||
s := &Server{
|
|
||||||
players: make(map[int64]*entity.Player),
|
|
||||||
planets: make(map[int64]*planet.Planet),
|
|
||||||
gameRepo: gameRepo,
|
|
||||||
serverRepo: serverRepo,
|
|
||||||
accountRepo: accountRepo,
|
|
||||||
playerInfoRepo: playerInfoRepo,
|
|
||||||
playerItemRepo: playerItemRepo,
|
|
||||||
petRepo: petRepo,
|
|
||||||
}
|
|
||||||
s.initializePlanets()
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// initializePlanets 初始化所有星球
|
|
||||||
func (s *Server) initializePlanets() {
|
|
||||||
maps := s.gameRepo.GetAllMaps()
|
|
||||||
for _, config := range maps {
|
|
||||||
planet := s.generatePlanet(config)
|
|
||||||
s.planets[planet.GetId()] = planet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generatePlanet 根据地图配置生成星球
|
|
||||||
func (s *Server) generatePlanet(config *mapInfo.MapXmlModel) *planet.Planet {
|
|
||||||
entries := config.GetEntries()
|
|
||||||
positions := make(map[int64]structs.Point)
|
|
||||||
|
|
||||||
if len(entries) == 0 {
|
|
||||||
positions = make(map[int64]structs.Point)
|
|
||||||
} else {
|
|
||||||
for _, entry := range entries {
|
|
||||||
positions[entry.GetFromMap()] = s.generatePoint(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return planet.NewPlanet(
|
|
||||||
s,
|
|
||||||
int64(config.GetId()),
|
|
||||||
config.GetName(),
|
|
||||||
structs.Point{X: config.GetX(), Y: config.GetY()},
|
|
||||||
positions,
|
|
||||||
s.gameRepo.CanMapRefresh(config.GetId()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generatePoint 从入口配置生成点坐标
|
|
||||||
func (s *Server) generatePoint(xml *mapInfo.EntryXmlModel) structs.Point {
|
|
||||||
return structs.Point{X: xml.GetPosX(), Y: xml.GetPosY()}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPlayer 获取玩家信息
|
|
||||||
func (s *Server) GetPlayer(accountID int64) (*entity.Player, error) {
|
|
||||||
s.mutex.RLock()
|
|
||||||
defer s.mutex.RUnlock()
|
|
||||||
|
|
||||||
player, exists := s.players[accountID]
|
|
||||||
if !exists {
|
|
||||||
return nil, errors.New("玩家不存在")
|
|
||||||
}
|
|
||||||
return player, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPlanet 获取星球信息
|
|
||||||
func (s *Server) GetPlanet(planetID int64) (*planet.Planet, error) {
|
|
||||||
s.mutex.RLock()
|
|
||||||
defer s.mutex.RUnlock()
|
|
||||||
|
|
||||||
planet, exists := s.planets[planetID]
|
|
||||||
if !exists {
|
|
||||||
return nil, errors.New("星球不存在")
|
|
||||||
}
|
|
||||||
return planet, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDefaultPlanet 获取默认星球
|
|
||||||
func (s *Server) GetDefaultPlanet() (*planet.Planet, error) {
|
|
||||||
s.mutex.RLock()
|
|
||||||
defer s.mutex.RUnlock()
|
|
||||||
|
|
||||||
planet, exists := s.planets[1] // 假设1是默认星球ID
|
|
||||||
if !exists {
|
|
||||||
return nil, errors.New("未找到默认星球")
|
|
||||||
}
|
|
||||||
return planet, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PlayerEnter 玩家加入服务器
|
|
||||||
func (s *Server) PlayerEnter(player *entity.Player) {
|
|
||||||
s.mutex.Lock()
|
|
||||||
defer s.mutex.Unlock()
|
|
||||||
|
|
||||||
s.players[player.GetAccountID()] = player
|
|
||||||
}
|
|
||||||
|
|
||||||
// PlayerOffline 玩家离线处理
|
|
||||||
func (s *Server) PlayerOffline(player *entity.Player) {
|
|
||||||
s.mutex.Lock()
|
|
||||||
defer s.mutex.Unlock()
|
|
||||||
|
|
||||||
// 清理缓存会话数据
|
|
||||||
if err := s.accountRepo.RemoveSessionID(player.GetSessionID()); err != nil {
|
|
||||||
log.Printf("清除会话ID失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理登录绑定服务器
|
|
||||||
if err := s.serverRepo.CancelRecordAccount(player.GetAccountID()); err != nil {
|
|
||||||
log.Printf("取消账号绑定失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从玩家列表中移除
|
|
||||||
delete(s.players, player.GetAccountID())
|
|
||||||
}
|
|
||||||
|
|
||||||
// BroadcastMessage 广播消息给所有在线玩家
|
|
||||||
func (s *Server) BroadcastMessage(message *net.OutboundMessage) {
|
|
||||||
s.mutex.RLock()
|
|
||||||
defer s.mutex.RUnlock()
|
|
||||||
|
|
||||||
for _, player := range s.players {
|
|
||||||
if player.IsOnline() {
|
|
||||||
player.SendMessage(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BroadcastMessageWithFilter 按条件广播消息给在线玩家
|
|
||||||
func (s *Server) BroadcastMessageWithFilter(message *net.OutboundMessage, filter func(*entity.Player) bool) {
|
|
||||||
s.mutex.RLock()
|
|
||||||
defer s.mutex.RUnlock()
|
|
||||||
|
|
||||||
for _, player := range s.players {
|
|
||||||
if player.IsOnline() && filter(player) {
|
|
||||||
player.SendMessage(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GeneratePetEntity 生成宠物实体
|
|
||||||
func (s *Server) GeneratePetEntity(
|
|
||||||
playerID int64,
|
|
||||||
petTypeID int,
|
|
||||||
individualValue int16,
|
|
||||||
nature int,
|
|
||||||
abilityTypeEnum int,
|
|
||||||
isShiny bool,
|
|
||||||
level int,
|
|
||||||
) (*pet.PetEntity, error) {
|
|
||||||
if level < 1 || level > 100 {
|
|
||||||
return nil, fmt.Errorf("精灵等级必须在1到100之间, level: %d", level)
|
|
||||||
}
|
|
||||||
|
|
||||||
petInfo, err := s.gameRepo.GetMonsterByID(petTypeID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("无效的精灵ID, pet_id: %d, 错误: %v", petTypeID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
firstSkillInfo, err := s.gameRepo.GetPetFirstSkillID(petTypeID, level)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("精灵没有初始技能, pet_id: %d, 错误: %v", petTypeID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
zero := int16(0)
|
|
||||||
|
|
||||||
pet := pet.NewPetEntityBuilder()
|
|
||||||
.WithAsset(petInfo)
|
|
||||||
.WithLevelToExp(s.gameRepo.GetLevelToExp())
|
|
||||||
.WithPlayerID(playerID)
|
|
||||||
.WithCapturePlayerID(playerID)
|
|
||||||
.WithCaptureTime(time.Now().Unix())
|
|
||||||
.WithCaptureMap(0) // 假设0是默认地图ID
|
|
||||||
.WithCaptureRect(0) // 假设0是默认区域
|
|
||||||
.WithCaptureLevel(level)
|
|
||||||
.WithIndividualValue(individualValue)
|
|
||||||
.WithNature(nature)
|
|
||||||
.WithAbilityTypeEnum(abilityTypeEnum)
|
|
||||||
.WithIsShiny(isShiny)
|
|
||||||
.WithLevel(level)
|
|
||||||
.WithCurrentExp(0)
|
|
||||||
.WithEvHp(zero)
|
|
||||||
.WithEvAttack(zero)
|
|
||||||
.WithEvDefense(zero)
|
|
||||||
.WithEvSpecialAttack(zero)
|
|
||||||
.WithEvSpecialDefense(zero)
|
|
||||||
.WithEvSpeed(zero)
|
|
||||||
.WithSkill1ID(firstSkillInfo[0].GetId())
|
|
||||||
.WithSkill1Pp(firstSkillInfo[0].GetMaxPp())
|
|
||||||
.WithSkill2ID(firstSkillInfo[1].GetId())
|
|
||||||
.WithSkill2Pp(firstSkillInfo[1].GetMaxPp())
|
|
||||||
.WithSkill3ID(firstSkillInfo[2].GetId())
|
|
||||||
.WithSkill3Pp(firstSkillInfo[2].GetMaxPp())
|
|
||||||
.WithSkill4ID(firstSkillInfo[3].GetId())
|
|
||||||
.WithSkill4Pp(firstSkillInfo[3].GetMaxPp())
|
|
||||||
.WithIndividualGuarantee(0)
|
|
||||||
.WithNatureGuarantee(0)
|
|
||||||
.Build()
|
|
||||||
|
|
||||||
if err := s.CalculatePetPanel(pet); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pet, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CalculatePetPanel 计算宠物面板属性
|
|
||||||
func (s *Server) CalculatePetPanel(petEntity *pet.PetEntity) error {
|
|
||||||
natureInfo, err := s.gameRepo.GetNatureInfoByID(petEntity.GetNature())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("无效的性格ID, nature: %d, 错误: %v", petEntity.GetNature(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hp := util.CalculatePetHPPanelSize(
|
|
||||||
petEntity.GetAsset().GetHp(),
|
|
||||||
petEntity.GetIndividualValue(),
|
|
||||||
petEntity.GetLevel(),
|
|
||||||
petEntity.GetEvHp(),
|
|
||||||
)
|
|
||||||
|
|
||||||
attack := util.CalculatePetPanelSize(
|
|
||||||
petEntity.GetAsset().GetAtk(),
|
|
||||||
petEntity.GetIndividualValue(),
|
|
||||||
petEntity.GetLevel(),
|
|
||||||
petEntity.GetEvHp(),
|
|
||||||
natureInfo.GetAttackCorrect(),
|
|
||||||
)
|
|
||||||
|
|
||||||
defense := util.CalculatePetPanelSize(
|
|
||||||
petEntity.GetAsset().GetDef(),
|
|
||||||
petEntity.GetIndividualValue(),
|
|
||||||
petEntity.GetLevel(),
|
|
||||||
petEntity.GetEvHp(),
|
|
||||||
natureInfo.GetDefenseCorrect(),
|
|
||||||
)
|
|
||||||
|
|
||||||
specialAttack := util.CalculatePetPanelSize(
|
|
||||||
petEntity.GetAsset().GetSpAtk(),
|
|
||||||
petEntity.GetIndividualValue(),
|
|
||||||
petEntity.GetLevel(),
|
|
||||||
petEntity.GetEvHp(),
|
|
||||||
natureInfo.GetSaCorrect(),
|
|
||||||
)
|
|
||||||
|
|
||||||
specialDefense := util.CalculatePetPanelSize(
|
|
||||||
petEntity.GetAsset().GetSpDef(),
|
|
||||||
petEntity.GetIndividualValue(),
|
|
||||||
petEntity.GetLevel(),
|
|
||||||
petEntity.GetEvHp(),
|
|
||||||
natureInfo.GetSdCorrect(),
|
|
||||||
)
|
|
||||||
|
|
||||||
speed := util.CalculatePetPanelSize(
|
|
||||||
petEntity.GetAsset().GetSpd(),
|
|
||||||
petEntity.GetIndividualValue(),
|
|
||||||
petEntity.GetLevel(),
|
|
||||||
petEntity.GetEvHp(),
|
|
||||||
natureInfo.GetSpeedCorrect(),
|
|
||||||
)
|
|
||||||
|
|
||||||
petEntity.SetMaxHp(hp)
|
|
||||||
petEntity.SetCurrentHp(hp)
|
|
||||||
petEntity.SetAttack(attack)
|
|
||||||
petEntity.SetDefense(defense)
|
|
||||||
petEntity.SetSpecialAttack(specialAttack)
|
|
||||||
petEntity.SetSpecialDefense(specialDefense)
|
|
||||||
petEntity.SetSpeed(speed)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SavePlayer 保存玩家信息到数据库
|
|
||||||
func (s *Server) SavePlayer(player *entity.Player) (bool, error) {
|
|
||||||
ctx := context.Background()
|
|
||||||
tx, err := s.playerInfoRepo.BeginTransaction(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("开始事务失败: %v", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
log.Printf("保存玩家时发生panic: %v", r)
|
|
||||||
} else if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
} else {
|
|
||||||
tx.Commit()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
playerID := player.GetGameID()
|
|
||||||
pets := player.GetPetBag().GetUsedPets()
|
|
||||||
items := player.GetItemBag().GetItems()
|
|
||||||
|
|
||||||
// 保存玩家信息
|
|
||||||
if err := s.playerInfoRepo.Save(ctx, tx, player); err != nil {
|
|
||||||
return false, fmt.Errorf("保存玩家信息失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存精灵信息
|
|
||||||
if err := s.petRepo.SaveAll(ctx, tx, playerID, pets); err != nil {
|
|
||||||
return false, fmt.Errorf("保存精灵信息失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存玩家背包信息
|
|
||||||
if err := s.playerItemRepo.SaveAll(ctx, tx, playerID, items); err != nil {
|
|
||||||
return false, fmt.Errorf("保存背包信息失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy 销毁服务器,清理资源
|
|
||||||
func (s *Server) Destroy() error {
|
|
||||||
// 保存所有玩家数据
|
|
||||||
s.mutex.RLock()
|
|
||||||
for _, player := range s.players {
|
|
||||||
go func(p *entity.Player) {
|
|
||||||
if _, err := s.SavePlayer(p); err != nil {
|
|
||||||
log.Printf("保存玩家 %d 数据失败: %v", p.GetAccountID(), err)
|
|
||||||
}
|
|
||||||
}(player)
|
|
||||||
}
|
|
||||||
s.mutex.RUnlock()
|
|
||||||
|
|
||||||
log.Println("Destroying server ...")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
10
common/serialize/ArraySerialize.go
Normal file
10
common/serialize/ArraySerialize.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package serialize
|
||||||
|
|
||||||
|
type ArraySerialize struct {
|
||||||
|
OrderId int64 `v:"order-exist"`
|
||||||
|
ProductName string
|
||||||
|
Amount int64
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
var paddedContent byte=0//长度不足时填充的内容, 默认填充0
|
||||||
|
|
||||||
20
common/serialize/cheak.go
Normal file
20
common/serialize/cheak.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package serialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/util/gvalid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
OrderId int64 `v:"order-exist"`
|
||||||
|
ProductName string `v:"order-exist"`
|
||||||
|
Amount int64
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func RuleOrderExist(ctx context.Context, in gvalid.RuleFuncInput) error {
|
||||||
|
fmt.Println(in)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
22
common/serialize/cheak_test.go
Normal file
22
common/serialize/cheak_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package serialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_main(t *testing.T) {
|
||||||
|
var (
|
||||||
|
ctx = gctx.New()
|
||||||
|
req = &Request{
|
||||||
|
OrderId: 65535,
|
||||||
|
ProductName: "HikingShoe",
|
||||||
|
Amount: 10000,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
err := g.Validator().RuleFunc("order-exist", RuleOrderExist).Data(req).Run(ctx)
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
236
common/serialize/ser.go
Normal file
236
common/serialize/ser.go
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
package serialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
"github.com/gogf/gf/v2/util/gvalid"
|
||||||
|
"github.com/tnnmigga/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
var lengthtype = enum.New[struct {
|
||||||
|
LENGTH_FIRST int
|
||||||
|
FIXED_LENGTH int
|
||||||
|
}]()
|
||||||
|
|
||||||
|
// PacketSerializer 定义序列化函数类型,将数据转换为字节切片
|
||||||
|
type PacketSerializer[T any] func(data T) ([]byte, error)
|
||||||
|
|
||||||
|
// PacketDeserializer 定义反序列化函数类型,将字节切片转换为数据
|
||||||
|
type PacketDeserializer[T any] func(data []byte) (T, error)
|
||||||
|
|
||||||
|
// PacketHandler 封装序列化和反序列化处理函数
|
||||||
|
type PacketHandler[T any] struct {
|
||||||
|
Serialize PacketSerializer[T] // 序列化函数
|
||||||
|
Deserialize PacketDeserializer[T] // 反序列化函数
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func serializebase[T any](field reflect.StructField, buf *bytes.Buffer, writedata any) error {
|
||||||
|
|
||||||
|
|
||||||
|
if field.Type.Kind() == reflect.Slice { //|| field.Type.Kind() == reflect.Array}
|
||||||
|
|
||||||
|
datatype := make(chan int, 1)
|
||||||
|
FIXED_LENGTH := func(ctx context.Context, in gvalid.RuleFuncInput) error {
|
||||||
|
if in.Field == field.Name { //判断相同
|
||||||
|
datatype <- lengthtype.FIXED_LENGTH
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
LENGTH_FIRST := func(ctx context.Context, in gvalid.RuleFuncInput) error {
|
||||||
|
datatype <- lengthtype.FIXED_LENGTH
|
||||||
|
// fmt.Println(in)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
length := func(ctx context.Context, in gvalid.RuleFuncInput) error {
|
||||||
|
tem := <-datatype
|
||||||
|
close(datatype)
|
||||||
|
writelen := 0
|
||||||
|
if parts := strings.Split(in.Rule, ":"); len(parts) > 1 {
|
||||||
|
writelen = gconv.Int(strings.TrimSpace(parts[1]))
|
||||||
|
|
||||||
|
}
|
||||||
|
switch tem {
|
||||||
|
case lengthtype.FIXED_LENGTH: //in.value
|
||||||
|
tempslice := gconv.SliceAny(writedata)
|
||||||
|
temp := make([]byte, writelen-len(tempslice))
|
||||||
|
|
||||||
|
for i := 0; i < len(tempslice); i++ {
|
||||||
|
tempdata := tempslice[i]
|
||||||
|
fmt.Println(i)
|
||||||
|
serializebase[T]( field, buf, tempdata) //todo递归序列化
|
||||||
|
// copy(temp, date1)
|
||||||
|
// if err := binary.Write(buf, binary.BigEndian, temp); err != nil {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(buf, binary.BigEndian, temp); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case lengthtype.LENGTH_FIRST:
|
||||||
|
// temp := make([]byte, writelen)
|
||||||
|
// date1 := []byte(writedata.(string))
|
||||||
|
// copy(temp, date1)
|
||||||
|
// if err := binary.Write(buf, binary.BigEndian, temp); err != nil {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rules := make(map[string]gvalid.RuleFunc, 3)
|
||||||
|
rules["LENGTH_FIRST"] = LENGTH_FIRST
|
||||||
|
rules["FIXED_LENGTH"] = FIXED_LENGTH
|
||||||
|
rules["length"] = length
|
||||||
|
g.Validator().RuleFuncMap(rules).Data(writedata).Run(gctx.New())
|
||||||
|
// serializeslice[T](field, buf, writedata)
|
||||||
|
} else {
|
||||||
|
if err := binary.Write(buf, binary.BigEndian, writedata); err != nil {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultPacketSerializer 默认序列化实现,使用大端序写入数据
|
||||||
|
func DefaultPacketSerializer[T any]() PacketSerializer[T] {
|
||||||
|
return func(data T) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
// 使用大端序写入数据
|
||||||
|
// 1. 使用reflect获取结构体类型
|
||||||
|
typ := reflect.TypeOf(data)
|
||||||
|
fmt.Println("结构体类型名称:", typ.Name())
|
||||||
|
fmt.Println("字段数量:", typ.NumField())
|
||||||
|
|
||||||
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
field := typ.Field(i)
|
||||||
|
fmt.Printf("字段名: %s, 类型: %s",
|
||||||
|
field.Name, field.Type)
|
||||||
|
fmt.Println("字段值:", reflect.ValueOf(data).Field(i).Interface())
|
||||||
|
|
||||||
|
writedata := reflect.ValueOf(data).Field(i).Interface()
|
||||||
|
fmt.Println(field.Type.Kind())
|
||||||
|
|
||||||
|
serializebase[T]( field, &buf, writedata)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultPacketDeserializer 默认反序列化实现,使用大端序读取数据
|
||||||
|
func DefaultPacketDeserializer[T any]() PacketDeserializer[T] {
|
||||||
|
return func(data []byte) (T, error) {
|
||||||
|
var result T
|
||||||
|
reader := bytes.NewReader(data)
|
||||||
|
// 使用大端序读取数据
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||||
|
var zero T
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultPacketHandler 创建默认的数据包处理句柄
|
||||||
|
func NewDefaultPacketHandler[T any]() *PacketHandler[T] {
|
||||||
|
return &PacketHandler[T]{
|
||||||
|
Serialize: DefaultPacketSerializer[T](),
|
||||||
|
Deserialize: DefaultPacketDeserializer[T](),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例:使用自定义类型演示序列化与反序列化
|
||||||
|
type ExampleData struct {
|
||||||
|
ID int32
|
||||||
|
Name string
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义序列化函数(处理结构体类型)
|
||||||
|
func CustomSerializer() PacketSerializer[ExampleData] {
|
||||||
|
return func(data ExampleData) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
// 先序列化基本类型字段
|
||||||
|
if err := binary.Write(&buf, binary.BigEndian, data.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 序列化字符串长度和内容
|
||||||
|
lenName := int32(len(data.Name))
|
||||||
|
if err := binary.Write(&buf, binary.BigEndian, lenName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := buf.Write([]byte(data.Name)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 序列化字节数组长度和内容
|
||||||
|
lenData := int32(len(data.Data))
|
||||||
|
if err := binary.Write(&buf, binary.BigEndian, lenData); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := buf.Write(data.Data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义反序列化函数(处理结构体类型)
|
||||||
|
func CustomDeserializer() PacketDeserializer[ExampleData] {
|
||||||
|
return func(data []byte) (ExampleData, error) {
|
||||||
|
var result ExampleData
|
||||||
|
reader := bytes.NewReader(data)
|
||||||
|
|
||||||
|
// 读取基本类型字段
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &result.ID); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取字符串长度和内容
|
||||||
|
var lenName int32
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &lenName); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
if lenName > int32(reader.Len()) {
|
||||||
|
return result, errors.New("invalid name length")
|
||||||
|
}
|
||||||
|
nameBuf := make([]byte, lenName)
|
||||||
|
if _, err := reader.Read(nameBuf); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result.Name = string(nameBuf)
|
||||||
|
|
||||||
|
// 读取字节数组长度和内容
|
||||||
|
var lenData int32
|
||||||
|
if err := binary.Read(reader, binary.BigEndian, &lenData); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
if lenData > int32(reader.Len()) {
|
||||||
|
return result, errors.New("invalid data length")
|
||||||
|
}
|
||||||
|
result.Data = make([]byte, lenData)
|
||||||
|
if _, err := reader.Read(result.Data); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/panjf2000/gnet/v2"
|
"github.com/panjf2000/gnet/v2"
|
||||||
"github.com/panjf2000/gnet/v2/pkg/logging"
|
"github.com/panjf2000/gnet/v2/pkg/logging"
|
||||||
|
"blazing/common/data/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) Boot() error {
|
func (s *Server) Boot() error {
|
||||||
@@ -35,19 +36,18 @@ func (s *Server) OnBoot(eng gnet.Engine) gnet.Action {
|
|||||||
|
|
||||||
func (s *Server) OnTraffic(conn gnet.Conn) (action gnet.Action) {
|
func (s *Server) OnTraffic(conn gnet.Conn) (action gnet.Action) {
|
||||||
|
|
||||||
|
conn.SetContext(entity.NewClientData())//注入data
|
||||||
if s.network == "tcp" {
|
if s.network == "tcp" {
|
||||||
return s.handleTcp(conn)
|
return s.handleTcp(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return gnet.None
|
return gnet.None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (s *Server) handleTcp(conn gnet.Conn) (action gnet.Action) {
|
func (s *Server) handleTcp(conn gnet.Conn) (action gnet.Action) {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
||||||
data, err := s.codec.Decode(conn)
|
data, err := s.codec.Decode(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
@@ -73,11 +73,11 @@ func (s *Server) handleTcp(conn gnet.Conn) (action gnet.Action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) parser(c gnet.Conn, line []byte) {
|
func (s *Server) parser(c gnet.Conn, line []byte) {
|
||||||
|
//todo 这里待实现注入player实体
|
||||||
s.handler.Handle(line)
|
s.handler.Handle(line)
|
||||||
}
|
}
|
||||||
func (s *Server) Start() {
|
func (s *Server) Start() {
|
||||||
|
|
||||||
err := gnet.Run(s, s.network+"://"+s.addr, gnet.WithMulticore(s.multicore))
|
err := gnet.Run(s, s.network+"://"+s.addr, gnet.WithMulticore(s.multicore))
|
||||||
logging.Infof("server exits with error: %v", err)
|
logging.Infof("server exits with error: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
98
common/socket/cmd/cmd.go
Normal file
98
common/socket/cmd/cmd.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tnnmigga/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum 辅助类型定义
|
||||||
|
type EnumValue struct {
|
||||||
|
Value int
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageCommandIDRegistry 消息命令ID注册表
|
||||||
|
var MessageCommandIDRegistry = enum.New[struct {
|
||||||
|
// 在线相关命令
|
||||||
|
Commend_OnLine int `enum:"105"` // 在线命令
|
||||||
|
Login_In int `enum:"1001"` // 玩家登录
|
||||||
|
System_Time int `enum:"1002"` // 返回当前时间戳
|
||||||
|
Map_Hot int `enum:"1004"` // 显示地图热度(此地图内玩家数量)
|
||||||
|
Gold_Online_Check_Remain int `enum:"1106"` // 返回玩家金豆数量
|
||||||
|
|
||||||
|
// 地图相关命令
|
||||||
|
Enter_Map int `enum:"2001"` // 告知后端玩家进入新地图
|
||||||
|
Leave_Map int `enum:"2002"` // 玩家离开地图
|
||||||
|
List_Map_Player int `enum:"2003"` // 返回当前地图玩家列表
|
||||||
|
Map_Ogre_List int `enum:"2004"` // 精灵刷新
|
||||||
|
|
||||||
|
// 用户信息相关命令
|
||||||
|
Get_Sim_UserInfo int `enum:"2051"` // 返回邮人物简单信息
|
||||||
|
Get_More_UserInfo int `enum:"2052"` // 返回人物详细信息
|
||||||
|
Change_Nick_Name int `enum:"2061"` // 修改玩家名字
|
||||||
|
|
||||||
|
// 动作相关命令
|
||||||
|
People_Walk int `enum:"2101"` // 玩家走路包
|
||||||
|
Chat int `enum:"2102"` // 公屏聊天
|
||||||
|
Aimat int `enum:"2104"` // 射击
|
||||||
|
|
||||||
|
// 精灵相关命令
|
||||||
|
Get_Pet_Info int `enum:"2301"` // 获取精灵详细信息
|
||||||
|
Get_Pet_List int `enum:"2303"` // 获取所有的精灵列表
|
||||||
|
Pet_Release int `enum:"2304"` // 精灵加入背包或放回仓库
|
||||||
|
Pet_Show int `enum:"2305"` // 展示精灵
|
||||||
|
Pet_Cure int `enum:"2306"` // 恢复精灵状态,NONO恢复所有精灵
|
||||||
|
Pet_Study_Skill int `enum:"2307"` // 升级学习替换技能
|
||||||
|
Pet_Default int `enum:"2308"` // 设置精灵首发
|
||||||
|
Pet_One_Cure int `enum:"2310"` // 恢复精灵状态,精灵恢复单只精灵
|
||||||
|
Pet_Skill_Switch int `enum:"2312"` // 切换精灵技能
|
||||||
|
Pet_Set_Exp int `enum:"2318"` // 分配精灵经验
|
||||||
|
Pet_Get_Exp int `enum:"2319"` // 获取积累经验
|
||||||
|
Pet_Room int `enum:"2325"` // 跟随精灵获取信息
|
||||||
|
Get_Soul_Bead_List int `enum:"2354"` // 返回元神珠信息
|
||||||
|
|
||||||
|
// 战斗相关命令
|
||||||
|
Ready_To_Fight int `enum:"2404"` // 客户端通知服务端可以开始战斗, 此包表示客户端可以开始战斗, 无需服务端回复此包内容
|
||||||
|
Use_Skill int `enum:"2405"` // 使用技能
|
||||||
|
Change_Pet int `enum:"2407"` // 切换精灵
|
||||||
|
Fight_NPC_Monster int `enum:"2408"` // 与野怪对战的申请进入战斗
|
||||||
|
Catch_Monster int `enum:"2409"` // 捕捉精灵
|
||||||
|
Challenge_Boss int `enum:"2411"` // 与当前地图的Boss类型野怪进入战斗
|
||||||
|
Note_ReadyTo_Fight int `enum:"2503"` // 通知客户端已经可以开始战斗
|
||||||
|
Note_Start_Fight int `enum:"2504"` // 在客户端告知服务端可以开始战斗后, 服务端回给客户端战斗开始
|
||||||
|
Note_Use_Skill int `enum:"2505"` // 通知使用技能
|
||||||
|
Fight_Over int `enum:"2506"` // 战斗结束
|
||||||
|
Note_Update_Skill int `enum:"2507"` // 升级获得学习技能
|
||||||
|
Note_Update_Prop int `enum:"2508"` // 返回升级后的信息
|
||||||
|
|
||||||
|
// 装备物品相关命令
|
||||||
|
Change_Cloth int `enum:"2604"` // 修改玩家装备
|
||||||
|
Item_List int `enum:"2605"` // 返回玩家物品列表
|
||||||
|
|
||||||
|
// 奖励相关命令
|
||||||
|
Talk_Count int `enum:"2701"` // 玩家领取奖励的次数(挖矿,礼包等)
|
||||||
|
Talk_Cate int `enum:"2702"` // 领取奖品的内容
|
||||||
|
Mail_Get_Unread int `enum:"2757"` // 返回邮件数量
|
||||||
|
|
||||||
|
// 系统相关命令
|
||||||
|
System_Message int `enum:"8002"` // 后端主动发送面板消息
|
||||||
|
Get_Boss_Monster int `enum:"8004"` // 返回战斗结束后的奖励包或主动发放奖励
|
||||||
|
|
||||||
|
// NONO相关命令
|
||||||
|
Nono_Info int `enum:"9003"` // 通过米米号获取nono信息
|
||||||
|
Nono_Follow_Or_Home int `enum:"9019"` // nono跟随或回家
|
||||||
|
|
||||||
|
// 特殊命令
|
||||||
|
Get_Quadruple_Exe_Time int `enum:"50007"` // 返回已使用四倍剩余时间
|
||||||
|
|
||||||
|
// 暂未处理的包
|
||||||
|
Item_Buy int `enum:"2601"` // 物品购买
|
||||||
|
Item_Sale int `enum:"2602"` // 物品出售
|
||||||
|
Friend_Add int `enum:"2151"` // 添加好友
|
||||||
|
Friend_Remove int `enum:"2153"` // 移除好友
|
||||||
|
Invite_To_Fight int `enum:"2401"` // 邀请战斗
|
||||||
|
Escape_Fight int `enum:"2410"` // 逃离战斗
|
||||||
|
Join_Game int `enum:"5001"` // 加入游戏
|
||||||
|
Game_Over int `enum:"5002"` // 游戏结束
|
||||||
|
Leave_Game int `enum:"5003"` // 离开游戏
|
||||||
|
}]()
|
||||||
|
|
||||||
14
common/socket/cmd/cmd_test.go
Normal file
14
common/socket/cmd/cmd_test.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkCmd(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
//写入
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
b.Log(MessageCommandIDRegistry.Change_Cloth)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package codec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/panjf2000/gnet/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CROSS_DOMAIN 定义跨域策略文件内容
|
|
||||||
const CROSS_DOMAIN = "<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy><cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\x00"
|
|
||||||
|
|
||||||
// TEXT 定义跨域请求的文本格式
|
|
||||||
const TEXT = "<policy-file-request/>\x00"
|
|
||||||
|
|
||||||
// Handle 处理网络连接
|
|
||||||
func Handle(conn gnet.Conn) error {
|
|
||||||
// 读取数据并检查是否为跨域请求
|
|
||||||
data, err := conn.Peek(len(TEXT))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error reading cross-domain request: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(data) == TEXT { //判断是否是跨域请求
|
|
||||||
log.Printf("Received cross-domain request from %s", conn.RemoteAddr())
|
|
||||||
// 处理跨域请求
|
|
||||||
conn.Write([]byte(CROSS_DOMAIN))
|
|
||||||
conn.Discard(len(TEXT))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -10,4 +10,5 @@ type SocketCodec interface {
|
|||||||
Encode([]byte) ([]byte, error)
|
Encode([]byte) ([]byte, error)
|
||||||
Decode(gnet.Conn) ([]byte, error)
|
Decode(gnet.Conn) ([]byte, error)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,21 @@
|
|||||||
package codec
|
package codec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"blazing/common/data/entity"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/panjf2000/gnet/v2"
|
"github.com/panjf2000/gnet/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CROSS_DOMAIN 定义跨域策略文件内容
|
||||||
|
const CROSS_DOMAIN = "<?xml version=\"1.0\"?><!DOCTYPE cross-domain-policy><cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\x00"
|
||||||
|
|
||||||
|
// TEXT 定义跨域请求的文本格式
|
||||||
|
const TEXT = "<policy-file-request/>\x00"
|
||||||
|
|
||||||
var ErrIncompletePacket = errors.New("incomplete packet")
|
var ErrIncompletePacket = errors.New("incomplete packet")
|
||||||
|
|
||||||
// TomeeSocketCodec 协议格式:
|
// TomeeSocketCodec 协议格式:
|
||||||
@@ -23,26 +31,54 @@ var ErrIncompletePacket = errors.New("incomplete packet")
|
|||||||
// * | ... ... |
|
// * | ... ... |
|
||||||
// * +-----------+
|
// * +-----------+
|
||||||
type TomeeSocketCodec struct{}
|
type TomeeSocketCodec struct{}
|
||||||
var _ SocketCodec = (*TomeeSocketCodec)(nil)
|
|
||||||
|
|
||||||
|
var _ SocketCodec = (*TomeeSocketCodec)(nil)
|
||||||
|
|
||||||
func NewTomeeSocketCodec() *TomeeSocketCodec {
|
func NewTomeeSocketCodec() *TomeeSocketCodec {
|
||||||
return &TomeeSocketCodec{}
|
return &TomeeSocketCodec{}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handle(c gnet.Conn) {
|
||||||
|
clientdata:=c.Context().(*entity.ClientData)
|
||||||
|
if(clientdata.IsCrossDomain){
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
// 读取数据并检查是否为跨域请求
|
||||||
|
data, err := c.Peek(len(TEXT))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error reading cross-domain request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(data) == TEXT { //判断是否是跨域请求
|
||||||
|
log.Printf("Received cross-domain request from %s", c.RemoteAddr())
|
||||||
|
// 处理跨域请求
|
||||||
|
c.Write([]byte(CROSS_DOMAIN))
|
||||||
|
c.Discard(len(TEXT))
|
||||||
|
|
||||||
|
clientdata.IsCrossDomain=true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
func (codec TomeeSocketCodec) Encode(buf []byte) ([]byte, error) {
|
func (codec TomeeSocketCodec) Encode(buf []byte) ([]byte, error) {
|
||||||
bodyLen := len(buf)
|
bodyLen := len(buf)
|
||||||
data := make([]byte, 4+bodyLen)
|
data := make([]byte, 4+bodyLen)
|
||||||
|
|
||||||
// 写入4字节的包长度
|
// 写入4字节的包长度
|
||||||
binary.BigEndian.PutUint32(data[:4], uint32(bodyLen))
|
binary.BigEndian.PutUint32(data[:4], uint32(bodyLen))
|
||||||
// 写入包体
|
// 写入包体
|
||||||
copy(data[4:], buf)
|
copy(data[4:], buf)
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (codec TomeeSocketCodec) Decode(c gnet.Conn) ([]byte, error) {
|
func (codec TomeeSocketCodec) Decode(c gnet.Conn) ([]byte, error) {
|
||||||
|
|
||||||
|
handle(c)
|
||||||
// 先读取4字节的包长度
|
// 先读取4字节的包长度
|
||||||
lenBuf, err := c.Peek(4)
|
lenBuf, err := c.Peek(4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -51,10 +87,10 @@ func (codec TomeeSocketCodec) Decode(c gnet.Conn) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyLen := binary.BigEndian.Uint32(lenBuf)
|
bodyLen := binary.BigEndian.Uint32(lenBuf)
|
||||||
totalLen := 4 + int(bodyLen)
|
totalLen := 4 + int(bodyLen)
|
||||||
|
|
||||||
// 检查整个包是否完整
|
// 检查整个包是否完整
|
||||||
buf, err := c.Peek(totalLen)
|
buf, err := c.Peek(totalLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -63,13 +99,13 @@ func (codec TomeeSocketCodec) Decode(c gnet.Conn) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取包体
|
// 提取包体
|
||||||
body := make([]byte, bodyLen)
|
body := make([]byte, bodyLen)
|
||||||
copy(body, buf[4:totalLen])
|
copy(body, buf[4:totalLen])
|
||||||
|
|
||||||
// 从缓冲区中丢弃已读取的数据
|
// 从缓冲区中丢弃已读取的数据
|
||||||
_, _ = c.Discard(totalLen)
|
_, _ = c.Discard(totalLen)
|
||||||
|
|
||||||
return body, nil
|
return body, nil
|
||||||
}
|
}
|
||||||
|
|||||||
2
go.work
2
go.work
@@ -4,7 +4,7 @@ use (
|
|||||||
./common
|
./common
|
||||||
./common/contrib/drivers/mysql
|
./common/contrib/drivers/mysql
|
||||||
./common/contrib/files/local
|
./common/contrib/files/local
|
||||||
./cool
|
./common/cool
|
||||||
./logic
|
./logic
|
||||||
./login
|
./login
|
||||||
./modules/base
|
./modules/base
|
||||||
|
|||||||
@@ -1,7 +1,21 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "blazing/common/socket"
|
import (
|
||||||
|
"blazing/common/bytearray/serialize"
|
||||||
|
"blazing/common/core/info"
|
||||||
|
"blazing/common/socket"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
|
||||||
|
tt:=info.NewServerInfo()
|
||||||
|
tt.OnlineID=99
|
||||||
|
tt.IP=[]byte("127.0.0.1")
|
||||||
|
tt1:=serialize.NewDefaultPacketHandler[info.ServerInfo]()
|
||||||
|
tg,_:=tt1.Serialize(*tt)
|
||||||
|
|
||||||
|
fmt.Println(tg)
|
||||||
socket.NewServer(socket.WithPort("9999")).Start()
|
socket.NewServer(socket.WithPort("9999")).Start()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user