修复了rpc模块中日志输出时参数拼接错误的问题,同时修正了RegisterLogic函数中端口映射的逻辑错误。 feat(socket): 替换错误处理方式为panic 在ServerEvent.go中将网络启动失败的返回错误改为panic处理,提高错误可见性。 feat(fight): 增加战斗加载进度控制 在LoadPercent函数中增加对FightC非
376 lines
8.4 KiB
Go
376 lines
8.4 KiB
Go
package player
|
||
|
||
import (
|
||
"blazing/common/data/share"
|
||
"blazing/common/data/xmlres"
|
||
"blazing/common/socket/errorcode"
|
||
"blazing/common/utils"
|
||
"blazing/cool"
|
||
"math/rand"
|
||
"strings"
|
||
|
||
"blazing/logic/service/common"
|
||
|
||
"blazing/logic/service/fight/info"
|
||
"blazing/logic/service/space"
|
||
|
||
"blazing/modules/blazing/model"
|
||
blservice "blazing/modules/blazing/service"
|
||
"context"
|
||
"time"
|
||
|
||
"github.com/antlabs/timer"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"github.com/gogf/gf/v2/util/gconv"
|
||
"github.com/panjf2000/gnet/v2"
|
||
)
|
||
|
||
func ConutPlayer() int {
|
||
|
||
count := 0
|
||
Mainplayer.Range(func(uint32, *Player) bool {
|
||
count++
|
||
return true // 继续遍历
|
||
})
|
||
return count
|
||
}
|
||
|
||
var Mainplayer = &utils.SyncMap[uint32, *Player]{} //玩家数据
|
||
|
||
type OgreInfo struct {
|
||
Data [9]OgrePetInfo
|
||
}
|
||
|
||
type OgrePetInfo struct {
|
||
Id uint32
|
||
Shiny uint32
|
||
Lv uint32 `struc:"skip"` //等级
|
||
}
|
||
|
||
type Player struct {
|
||
MainConn gnet.Conn
|
||
baseplayer
|
||
IsLogin bool //是否登录
|
||
|
||
StopChan timer.TimeNoder
|
||
|
||
context.Context
|
||
PVPinfo *info.PVPinfo //当前邀请的玩家ID
|
||
Onlinetime uint32 //当前登录时间
|
||
OgreInfo OgreInfo
|
||
|
||
Service *blservice.UserService
|
||
// PVP被邀请信息
|
||
HavePVPinfo []*Player
|
||
monsters [3]int
|
||
Canmon bool //可以刷怪
|
||
Changemap bool //是否切换过地图
|
||
}
|
||
|
||
// PlayerOption 定义配置 Player 的函数类型
|
||
type PlayerOption func(*Player)
|
||
|
||
func WithConn(c gnet.Conn) PlayerOption {
|
||
return func(p *Player) {
|
||
p.MainConn = c
|
||
}
|
||
}
|
||
|
||
func (p *Player) GetAction() {
|
||
|
||
}
|
||
func (p *Player) CanFight() bool {
|
||
if p.FightC != nil {
|
||
return false
|
||
}
|
||
for _, v := range p.Info.PetList {
|
||
if v.Hp > 0 { // 只要找到一个血量大于0的宠物,就可以战斗
|
||
return true
|
||
}
|
||
}
|
||
// 遍历完所有宠物,都没有血量大于0的,才不能战斗
|
||
return false
|
||
|
||
}
|
||
|
||
// 刷怪具体实现
|
||
func (p *Player) SpawnMonsters() {
|
||
// 获取当前地图的怪物配置
|
||
|
||
// 创建数据包
|
||
tt := NewTomeeHeader(2004, p.Info.UserID)
|
||
|
||
p.genMonster(p.Info.MapID) //生成野怪
|
||
|
||
p.SendPack(tt.Pack(&p.OgreInfo))
|
||
|
||
}
|
||
func (p *Player) SendPack(b []byte) error {
|
||
return p.MainConn.Context().(*ClientData).SendPack(b)
|
||
|
||
}
|
||
|
||
// 2. 从 string 类型 slice 随机选一个元素
|
||
func RandomStringFromSlice(s []string) string {
|
||
|
||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||
randomIdx := r.Intn(len(s))
|
||
return s[randomIdx]
|
||
}
|
||
|
||
// 应该根据怪物信息决定后端生成
|
||
func (p *Player) genMonster(mapid uint32) {
|
||
var oldnum, newNum int
|
||
p.monsters, oldnum, newNum = replaceOneNumber(p.monsters)
|
||
// 设置怪物信息
|
||
t1 := OgreInfo{}
|
||
mapss, ok := xmlres.MonsterMap[gconv.Int(mapid)]
|
||
|
||
if ok && mapss.Monsters != nil {
|
||
for i, m := range mapss.Monsters.Monsters { //这里是9个
|
||
id := strings.Split(m.ID, " ")
|
||
lv := strings.Split(m.Lv, " ")
|
||
ttt := OgrePetInfo{
|
||
Id: gconv.Uint32(RandomStringFromSlice(id)),
|
||
}
|
||
if ttt.Id != 0 {
|
||
ttt.Shiny = 0 //待确认是否刷新异色
|
||
ttt.Lv = gconv.Uint32(RandomStringFromSlice(lv))
|
||
}
|
||
t1.Data[i] = ttt
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if p.Changemap {
|
||
p.Changemap = false
|
||
p.OgreInfo = OgreInfo{} //切地图清空
|
||
for i := 0; i < 3; i++ {
|
||
|
||
p.OgreInfo.Data[p.monsters[i]] = t1.Data[p.monsters[i]]
|
||
}
|
||
} else {
|
||
p.OgreInfo.Data[oldnum] = OgrePetInfo{}
|
||
p.OgreInfo.Data[newNum] = t1.Data[newNum]
|
||
}
|
||
|
||
}
|
||
|
||
// 生成0-9之间三个不重复的随机数 进地图5s
|
||
func generateThreeUniqueNumbers() [3]int {
|
||
rand.Seed(time.Now().UnixNano())
|
||
selected := make(map[int]bool)
|
||
var result [3]int
|
||
index := 0
|
||
|
||
for index < 3 {
|
||
num := rand.Intn(9)
|
||
if !selected[num] {
|
||
selected[num] = true
|
||
result[index] = num
|
||
index++
|
||
}
|
||
}
|
||
return result
|
||
}
|
||
|
||
// 从三个数字中移除一个,并从剩余6个数字中选一个补充 10s
|
||
func replaceOneNumber(original [3]int) ([3]int, int, int) {
|
||
// 随机选择要移除的索引(0-2)
|
||
removeIndex := rand.Intn(3)
|
||
removedNum := original[removeIndex]
|
||
|
||
// 找出所有不在原始数组中的数字(候选数字)
|
||
candidates := []int{}
|
||
originalMap := make(map[int]bool)
|
||
for _, num := range original {
|
||
originalMap[num] = true
|
||
}
|
||
|
||
for i := 0; i < 8; i++ {
|
||
if !originalMap[i] {
|
||
candidates = append(candidates, i)
|
||
}
|
||
}
|
||
|
||
// 从候选数字中随机选择一个
|
||
newNum := candidates[rand.Intn(len(candidates))]
|
||
|
||
// 创建新数组并替换数字
|
||
newNumbers := original
|
||
newNumbers[removeIndex] = newNum
|
||
|
||
return newNumbers, removedNum, newNum
|
||
}
|
||
|
||
// 添加物品
|
||
func (p *Player) ItemAdd(t ...model.SingleItemInfo) (result []model.SingleItemInfo) {
|
||
var ttt []model.SingleItemInfo
|
||
for _, v := range t {
|
||
|
||
switch v.ItemId {
|
||
case 1: //塞尔豆
|
||
p.Info.Coins = p.Info.Coins + v.ItemCnt
|
||
|
||
case 3: //累计经验
|
||
p.Info.ExpPool = p.Info.ExpPool + v.ItemCnt
|
||
|
||
case 5: //金豆ItemAdd
|
||
p.Info.GoldBean = p.Info.GoldBean + v.ItemCnt
|
||
|
||
default:
|
||
ttt = append(ttt, v)
|
||
}
|
||
|
||
}
|
||
|
||
p.Service.Item(func(rer map[uint32]model.SingleItemInfo) bool {
|
||
|
||
for _, v := range ttt {
|
||
itemx, ok := xmlres.ItemsMAP[int(v.ItemId)]
|
||
if !ok {
|
||
cool.Loger.Error(context.TODO(), "物品不存在", v.ItemId)
|
||
|
||
t1 := NewTomeeHeader(2601, p.Info.UserID)
|
||
t1.Result = uint32(errorcode.ErrorCodes.ErrBaseItemTypeLimit)
|
||
|
||
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
||
continue
|
||
}
|
||
itemm, ok := rer[v.ItemId]
|
||
|
||
if !ok {
|
||
rer[v.ItemId] = v
|
||
result = append(result, v)
|
||
continue
|
||
}
|
||
itemm.ItemCnt += v.ItemCnt
|
||
if itemm.ItemCnt > uint32(itemx.Max) {
|
||
cool.Loger.Error(context.TODO(), "物品超过拥有最大限制", v.ItemId)
|
||
t1 := NewTomeeHeader(2601, p.Info.UserID)
|
||
t1.Result = uint32(errorcode.ErrorCodes.ErrTooManyOfItem)
|
||
|
||
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
||
continue
|
||
}
|
||
result = append(result, v)
|
||
rer[v.ItemId] = itemm
|
||
|
||
}
|
||
return true
|
||
})
|
||
return
|
||
}
|
||
|
||
func (player1 *Player) Kick() {
|
||
if player1.IsLogin {
|
||
//取成功,否则创建
|
||
//player1.Save() //先保存数据再返回
|
||
head := NewTomeeHeader(1001, player1.Info.UserID)
|
||
head.Result = uint32(errorcode.ErrorCodes.ErrAccountLoggedInElsewhere)
|
||
//实际上这里有个问题,会造成重复保存问题
|
||
|
||
player1.SendPack(head.Pack(nil))
|
||
player1.MainConn.Context().(*ClientData).CloseChan = make(chan struct{})
|
||
player1.MainConn.Context().(*ClientData).Mu.Lock()
|
||
player1.MainConn.Close()
|
||
player1.MainConn.Context().(*ClientData).Mu.Unlock()
|
||
// clientdata.Player = player
|
||
|
||
<-player1.MainConn.Context().(*ClientData).CloseChan
|
||
}
|
||
}
|
||
|
||
func (p *Player) Cheak(b error) {
|
||
if b != nil {
|
||
g.Log().Error(context.Background(), "出现错误", p.Info.UserID, b.Error())
|
||
}
|
||
|
||
}
|
||
|
||
func LeaveMap(c common.PlayerI) {
|
||
if c == nil {
|
||
return
|
||
}
|
||
if c.GetInfo() == nil {
|
||
return
|
||
}
|
||
if c.GetInfo().MapID == 0 {
|
||
return
|
||
}
|
||
t := NewTomeeHeader(2002, c.GetInfo().UserID)
|
||
|
||
for k, v := range space.GetSpace(c.GetInfo().MapID).User.Items() {
|
||
if k != c.GetInfo().UserID {
|
||
v.SendPack(t.Pack(&space.LeaveMapOutboundInfo{UserID: c.GetInfo().UserID}))
|
||
}
|
||
|
||
}
|
||
space.GetSpace(c.GetInfo().MapID).User.Remove(c.GetInfo().UserID)
|
||
}
|
||
|
||
// Save 保存玩家数据
|
||
func (p *Player) Save() {
|
||
if p.Info == nil {
|
||
return
|
||
|
||
}
|
||
if p.FightC != nil {
|
||
go func() {
|
||
|
||
defer func() {
|
||
if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
|
||
// 1. 打印错误信息
|
||
|
||
cool.Loger.Error(context.TODO(), "panic 错误:", err)
|
||
|
||
}
|
||
}()
|
||
p.FightC.Over(p, info.BattleOverReason.PlayerOffline) //玩家逃跑,但是不能锁线程
|
||
}()
|
||
|
||
}
|
||
p.Info.TimeToday = p.Info.TimeToday + uint32(time.Now().Unix()) - uint32(p.Onlinetime) //保存电池时间
|
||
p.Onlinetime = uint32(time.Now().Unix())
|
||
|
||
p.Service.Save(p.Info)
|
||
LeaveMap(p)
|
||
p.StopChan.Stop() //停止刷怪
|
||
|
||
p.IsLogin = false
|
||
|
||
Mainplayer.Delete(p.Info.UserID)
|
||
share.ShareManager.DeleteUserOnline(p.Info.UserID) //设置用户登录服务器
|
||
}
|
||
|
||
// 是否可以获得经验
|
||
func (p *Player) CanGetExp() bool {
|
||
ttt := p.Info.TimeLimit - p.Info.TimeToday
|
||
return (uint32(time.Now().Unix()) - uint32(p.Onlinetime)) <= ttt
|
||
}
|
||
|
||
// CompleteLogin 标记登录完成并通知等待者
|
||
func (lw *Player) CompleteLogin() {
|
||
|
||
if lw.Info.MapID > 500 || lw.Info.MapID == 0 { //如果位于基地,就重置到传送仓
|
||
lw.Info.MapID = 1
|
||
|
||
}
|
||
if lw.IsNewPlayer() { //重置新手地图
|
||
lw.Info.MapID = 515
|
||
}
|
||
lw.IsLogin = true
|
||
}
|
||
|
||
// 定义检查函数:判断84-87索引中是否有任意一个元素不等于3
|
||
func (lw *Player) IsNewPlayer() bool {
|
||
// 遍历84到87的索引
|
||
for i := 84; i <= 87; i++ {
|
||
if lw.Info.TaskList[i] != 3 {
|
||
return true // 只要有一个不等于3,就返回true
|
||
}
|
||
}
|
||
return false // 全部等于3则返回false
|
||
}
|