2025-09-14 01:35:16 +08:00
|
|
|
|
package player
|
2025-09-04 02:00:57 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
2025-12-21 17:18:33 +00:00
|
|
|
|
"blazing/common/data"
|
2026-02-23 04:01:57 +08:00
|
|
|
|
"blazing/common/data/xmlres"
|
2025-10-25 15:06:05 +08:00
|
|
|
|
"blazing/common/socket/errorcode"
|
|
|
|
|
|
"blazing/cool"
|
2025-11-18 23:41:31 +00:00
|
|
|
|
"blazing/logic/service/common"
|
2025-11-15 22:17:43 +00:00
|
|
|
|
"blazing/logic/service/fight/info"
|
2025-11-16 11:56:57 +08:00
|
|
|
|
"blazing/logic/service/space"
|
2026-02-02 23:11:14 +08:00
|
|
|
|
"fmt"
|
2026-04-05 11:14:25 +08:00
|
|
|
|
"sync"
|
2025-11-18 22:16:55 +00:00
|
|
|
|
"sync/atomic"
|
2026-02-02 23:11:14 +08:00
|
|
|
|
"time"
|
2025-09-04 02:00:57 +08:00
|
|
|
|
|
2025-12-06 23:59:00 +08:00
|
|
|
|
"blazing/modules/base/service"
|
|
|
|
|
|
|
2025-12-26 23:46:10 +08:00
|
|
|
|
config "blazing/modules/config/service"
|
2026-01-21 20:46:05 +00:00
|
|
|
|
dictrvice "blazing/modules/dict/service"
|
2026-01-19 18:51:56 +08:00
|
|
|
|
blservice "blazing/modules/player/service"
|
2025-09-04 02:00:57 +08:00
|
|
|
|
"context"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
2025-12-16 06:54:27 +00:00
|
|
|
|
"github.com/gogf/gf/v2/util/gconv"
|
2026-02-23 04:07:23 +08:00
|
|
|
|
"github.com/gogf/gf/v2/util/grand"
|
2026-01-23 14:59:15 +00:00
|
|
|
|
csmap "github.com/mhmtszr/concurrent-swiss-map"
|
2025-10-14 03:07:55 +08:00
|
|
|
|
"github.com/panjf2000/gnet/v2"
|
2025-09-04 02:00:57 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
// Mainplayer 全局玩家数据存储映射
|
2026-03-16 22:30:12 +08:00
|
|
|
|
var Mainplayer = csmap.New[uint32, *ClientData]()
|
2025-09-04 02:00:57 +08:00
|
|
|
|
|
|
|
|
|
|
type OgrePetInfo struct {
|
2026-02-08 17:57:42 +08:00
|
|
|
|
ID uint32
|
2025-12-21 17:18:33 +00:00
|
|
|
|
ShinyLen uint32 `json:"-" struc:"sizeof=ShinyInfo"`
|
|
|
|
|
|
ShinyInfo []data.GlowFilter `json:"ShinyInfo,omitempty"`
|
|
|
|
|
|
Lv uint32 `struc:"skip"` //等级
|
2026-02-27 14:48:10 +08:00
|
|
|
|
//XUANCAI int64 `struc:"skip"`
|
|
|
|
|
|
//Item int64 `struc:"skip"` //奖励,如果有的话
|
|
|
|
|
|
Ext uint32 `struc:"skip"` //是否变尼尔尼奥
|
|
|
|
|
|
IsCapture int `struc:"skip"`
|
2025-09-04 02:00:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 19:22:04 +08:00
|
|
|
|
func (o *OgrePetInfo) GetID() int {
|
|
|
|
|
|
|
|
|
|
|
|
if o.Ext != 0 {
|
|
|
|
|
|
return int(o.Ext)
|
|
|
|
|
|
}
|
|
|
|
|
|
return int(o.ID)
|
|
|
|
|
|
}
|
2026-02-26 19:28:02 +08:00
|
|
|
|
func (o *OgrePetInfo) GetLevel() int {
|
|
|
|
|
|
|
|
|
|
|
|
if o.Ext != 0 {
|
|
|
|
|
|
return 16
|
|
|
|
|
|
}
|
|
|
|
|
|
return int(o.Lv)
|
|
|
|
|
|
}
|
2025-12-14 20:32:54 +08:00
|
|
|
|
|
2026-02-26 13:38:57 +08:00
|
|
|
|
// handleNPCFightSpecial 处理NPC战斗特殊情况
|
2026-02-27 15:01:02 +08:00
|
|
|
|
func (o *OgrePetInfo) HandleNPCFightSpecial(can int) {
|
2026-03-04 20:21:02 +08:00
|
|
|
|
if can == 0 && o.Ext == 0 { //不能抓并且不是尼尔
|
2026-02-27 15:01:02 +08:00
|
|
|
|
o.IsCapture = 0
|
|
|
|
|
|
return
|
2026-02-26 13:38:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-27 15:01:02 +08:00
|
|
|
|
petCfg, ok := xmlres.PetMAP[o.GetID()]
|
2026-02-26 13:38:57 +08:00
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
|
|
|
|
o.IsCapture = 0
|
|
|
|
|
|
} else {
|
|
|
|
|
|
o.IsCapture = gconv.Int(petCfg.CatchRate)
|
|
|
|
|
|
}
|
2026-02-27 14:48:10 +08:00
|
|
|
|
if o.Ext != 0 {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
var co *data.GlowFilter
|
2026-02-26 13:38:57 +08:00
|
|
|
|
|
2026-02-27 14:48:10 +08:00
|
|
|
|
if cool.Config.ServerInfo.IsVip != 0 { //测试服,百分百异色
|
|
|
|
|
|
co = config.NewShinyService().RandShiny(o.ID)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if o.IsCapture != 0 && grand.Meet(1, 500) {
|
|
|
|
|
|
|
|
|
|
|
|
co = config.NewShinyService().RandomByWeightShiny(o.ID)
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if co != nil && len(o.ShinyInfo) == 0 {
|
|
|
|
|
|
o.ShinyInfo = append(o.ShinyInfo, *co)
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2026-02-26 13:38:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-04 02:00:57 +08:00
|
|
|
|
type Player struct {
|
2025-10-14 03:07:55 +08:00
|
|
|
|
MainConn gnet.Conn
|
2025-09-21 14:56:37 +00:00
|
|
|
|
baseplayer
|
2025-09-04 02:00:57 +08:00
|
|
|
|
IsLogin bool //是否登录
|
2026-03-12 14:35:27 +08:00
|
|
|
|
IsJoin bool //是否加入RPC对局 用重连函数写临时缓存join,重连后统一join
|
2025-11-16 20:30:17 +00:00
|
|
|
|
Done
|
2025-09-21 14:56:37 +00:00
|
|
|
|
|
2026-02-02 23:25:05 +08:00
|
|
|
|
MapNPC *time.Timer
|
2025-09-04 02:00:57 +08:00
|
|
|
|
|
|
|
|
|
|
context.Context
|
2025-12-24 19:03:11 +08:00
|
|
|
|
Fightinfo info.Fightinfo // 当前邀请的玩家ID
|
2025-11-18 22:16:55 +00:00
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
Logintime uint32 // 当前登录时间
|
2026-02-23 04:12:12 +08:00
|
|
|
|
OgrePet
|
2026-04-01 02:48:09 +08:00
|
|
|
|
PetDisplay PetDisplayState
|
2025-09-21 14:56:37 +00:00
|
|
|
|
|
2025-10-21 00:41:59 +08:00
|
|
|
|
Service *blservice.UserService
|
2025-12-06 23:59:00 +08:00
|
|
|
|
User *service.BaseSysUserService
|
2025-10-21 00:41:59 +08:00
|
|
|
|
// PVP被邀请信息
|
2025-11-18 23:41:31 +00:00
|
|
|
|
HavePVPinfo []common.PlayerI
|
2025-09-21 08:00:58 +00:00
|
|
|
|
monsters [3]int
|
2025-12-24 19:03:11 +08:00
|
|
|
|
// 0 无,1可以刷怪,2是切换过地图
|
2026-01-02 04:11:37 +08:00
|
|
|
|
Canmon uint32 // 可以刷怪
|
|
|
|
|
|
CurDark uint32
|
2026-02-08 02:11:46 +08:00
|
|
|
|
Hash uint32
|
2026-04-02 23:05:18 +08:00
|
|
|
|
|
|
|
|
|
|
ArenaFlags uint32
|
2026-04-05 11:14:25 +08:00
|
|
|
|
|
|
|
|
|
|
logoutMu sync.Mutex
|
|
|
|
|
|
logoutDone chan struct{}
|
|
|
|
|
|
logoutSaved bool
|
2025-09-04 02:00:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 12:18:07 +08:00
|
|
|
|
const (
|
|
|
|
|
|
ArenaFlagNoSwitchPet uint32 = 1 << iota
|
|
|
|
|
|
ArenaFlagNoHeal
|
|
|
|
|
|
ArenaFlagNoPVP
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const arenaHostFlagMask = ArenaFlagNoSwitchPet | ArenaFlagNoHeal | ArenaFlagNoPVP
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) addArenaFlag(mask uint32) {
|
|
|
|
|
|
for {
|
|
|
|
|
|
current := atomic.LoadUint32(&p.ArenaFlags)
|
|
|
|
|
|
next := current | mask
|
|
|
|
|
|
if current == next || atomic.CompareAndSwapUint32(&p.ArenaFlags, current, next) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) clearArenaFlag(mask uint32) {
|
|
|
|
|
|
for {
|
|
|
|
|
|
current := atomic.LoadUint32(&p.ArenaFlags)
|
|
|
|
|
|
next := current &^ mask
|
|
|
|
|
|
if current == next || atomic.CompareAndSwapUint32(&p.ArenaFlags, current, next) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) HasArenaFlag(mask uint32) bool {
|
|
|
|
|
|
return atomic.LoadUint32(&p.ArenaFlags)&mask != 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) SetArenaHostFlags() {
|
|
|
|
|
|
p.addArenaFlag(arenaHostFlagMask)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) ClearArenaHostFlags() {
|
|
|
|
|
|
p.clearArenaFlag(arenaHostFlagMask)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) IsArenaHost() bool {
|
|
|
|
|
|
return p.HasArenaFlag(arenaHostFlagMask)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) IsArenaSwitchLocked() bool {
|
|
|
|
|
|
return p.HasArenaFlag(ArenaFlagNoSwitchPet)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) IsArenaHealLocked() bool {
|
|
|
|
|
|
return p.HasArenaFlag(ArenaFlagNoHeal)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (p *Player) IsArenaPVPLocked() bool {
|
|
|
|
|
|
return p.HasArenaFlag(ArenaFlagNoPVP)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-28 23:16:59 +08:00
|
|
|
|
type OgrePet struct {
|
|
|
|
|
|
Data [9]OgrePetInfo
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-12 04:28:20 +08:00
|
|
|
|
func (p *Player) GetCoins(amount int64) bool {
|
|
|
|
|
|
if int64(p.Info.Coins) < amount {
|
2025-11-13 21:36:18 +08:00
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
2025-11-25 16:36:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-10 22:09:15 +08:00
|
|
|
|
func (p *Player) UseGold(amount int64) bool {
|
2025-12-24 19:03:11 +08:00
|
|
|
|
if p.User.GetGold(uint(p.Info.UserID)) < amount {
|
2025-11-25 16:36:55 +08:00
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
2025-11-13 21:36:18 +08:00
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
|
2025-09-05 22:40:36 +08:00
|
|
|
|
func (p *Player) GetAction() {
|
|
|
|
|
|
|
2025-11-18 23:41:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
// InvitePlayer 邀请玩家进行对战
|
2025-11-18 23:41:31 +00:00
|
|
|
|
func (f *Player) InvitePlayer(ff common.PlayerI) {
|
|
|
|
|
|
f.HavePVPinfo = append(f.HavePVPinfo, ff)
|
2025-11-20 15:19:13 +08:00
|
|
|
|
tt := common.NewTomeeHeader(2501, f.GetInfo().UserID)
|
|
|
|
|
|
f.SendPack(tt.Pack(&info.NoteInviteToFightOutboundInfo{
|
|
|
|
|
|
UserID: ff.GetInfo().UserID,
|
|
|
|
|
|
Nick: ff.GetInfo().Nick,
|
|
|
|
|
|
Mode: ff.Getfightinfo().Mode,
|
|
|
|
|
|
}))
|
2025-11-16 11:56:57 +08:00
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
|
|
|
|
|
|
// Getfightinfo 获取玩家的战斗信息
|
2025-11-18 22:16:55 +00:00
|
|
|
|
func (p *Player) Getfightinfo() info.Fightinfo {
|
2025-11-18 23:41:31 +00:00
|
|
|
|
return p.Fightinfo
|
2025-11-16 20:30:17 +00:00
|
|
|
|
}
|
2025-11-20 15:19:13 +08:00
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
// QuitFight 退出战斗
|
|
|
|
|
|
func (p *Player) QuitFight() {
|
2025-11-20 05:57:29 +08:00
|
|
|
|
p.FightC = nil
|
2025-11-26 15:25:10 +08:00
|
|
|
|
atomic.StoreUint32(&p.Fightinfo.Mode, 0)
|
2025-11-19 00:09:12 +00:00
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
|
|
|
|
|
|
// GetSpace 获取玩家所在的空间
|
2025-11-16 11:56:57 +08:00
|
|
|
|
func (p *Player) GetSpace() *space.Space {
|
|
|
|
|
|
return space.GetSpace(p.Info.MapID)
|
2025-10-31 00:53:22 +08:00
|
|
|
|
}
|
2025-11-13 02:43:00 +08:00
|
|
|
|
|
2026-04-15 00:07:36 +08:00
|
|
|
|
func (p *Player) CurrentMapPetLevelLimit() uint32 {
|
|
|
|
|
|
if p == nil {
|
|
|
|
|
|
return 100
|
|
|
|
|
|
}
|
|
|
|
|
|
currentSpace := p.GetSpace()
|
|
|
|
|
|
if currentSpace != nil && currentSpace.IsLevelBreakMap {
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
return 100
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 03:22:59 +08:00
|
|
|
|
func (p *Player) IsCurrentMapLevelBreak() bool {
|
|
|
|
|
|
return p != nil && p.CurrentMapPetLevelLimit() == 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
// CanFight 检查玩家是否可以进行战斗
|
2025-11-19 00:09:12 +00:00
|
|
|
|
// 0无战斗,1PVP,2,BOOS,3PVE
|
2026-02-25 16:18:10 +08:00
|
|
|
|
func (p *Player) CanFight() errorcode.ErrorCode {
|
2025-11-21 02:40:27 +08:00
|
|
|
|
if len(p.Info.PetList) == 0 {
|
|
|
|
|
|
atomic.StoreUint32(&p.Fightinfo.Mode, 0)
|
2026-02-25 16:18:10 +08:00
|
|
|
|
return errorcode.ErrorCodes.ErrBattleCancelled
|
2025-11-21 02:40:27 +08:00
|
|
|
|
}
|
2025-11-18 22:16:55 +00:00
|
|
|
|
|
2025-11-20 05:57:29 +08:00
|
|
|
|
if p.FightC != nil {
|
2025-11-21 02:40:27 +08:00
|
|
|
|
atomic.StoreUint32(&p.Fightinfo.Mode, 0)
|
2026-02-25 16:18:10 +08:00
|
|
|
|
return errorcode.ErrorCodes.ErrBattleEnded
|
2025-11-20 05:57:29 +08:00
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
|
|
|
|
|
|
for _, pet := range p.Info.PetList {
|
|
|
|
|
|
if pet.Hp > 0 { // 只要找到一个血量大于0的宠物,就可以战斗
|
2026-02-25 16:18:10 +08:00
|
|
|
|
return 0
|
2025-11-18 22:16:55 +00:00
|
|
|
|
}
|
2025-10-31 00:53:22 +08:00
|
|
|
|
}
|
2025-12-24 19:03:11 +08:00
|
|
|
|
|
2025-11-20 05:57:29 +08:00
|
|
|
|
// 遍历完所有宠物,都没有血量大于0的,才不能战斗
|
2025-11-21 02:40:27 +08:00
|
|
|
|
atomic.StoreUint32(&p.Fightinfo.Mode, 0)
|
2026-02-25 16:18:10 +08:00
|
|
|
|
return errorcode.ErrorCodes.ErrPokemonNoStamina
|
2025-09-05 22:40:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-01 00:40:19 +08:00
|
|
|
|
func (p *Player) SendPack(b []byte) error {
|
2025-11-01 01:08:47 +08:00
|
|
|
|
if p.MainConn == nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
2026-01-20 22:08:36 +00:00
|
|
|
|
psocket, ok := p.MainConn.Context().(*ClientData)
|
2025-11-01 01:08:47 +08:00
|
|
|
|
if ok {
|
2026-01-20 22:08:36 +00:00
|
|
|
|
return psocket.SendPack(b)
|
2026-02-07 18:21:52 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
return fmt.Errorf("链接错误,取消发包")
|
|
|
|
|
|
|
2025-11-01 01:08:47 +08:00
|
|
|
|
}
|
2026-02-10 22:09:15 +08:00
|
|
|
|
|
2025-09-21 08:00:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-24 19:03:11 +08:00
|
|
|
|
// 添加物品 返回成功添加的物品
|
2026-04-01 20:10:29 +08:00
|
|
|
|
// ItemAddBatch 批量添加物品,普通道具先按 ItemAdd 的规则校验上限,再批量落库。
|
|
|
|
|
|
func (p *Player) ItemAddBatch(items []data.ItemInfo) []data.ItemInfo {
|
|
|
|
|
|
if len(items) == 0 {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sendErr := func(code errorcode.ErrorCode) {
|
|
|
|
|
|
t1 := common.NewTomeeHeader(2601, p.Info.UserID)
|
|
|
|
|
|
t1.Result = uint32(code)
|
|
|
|
|
|
p.SendPack(t1.Pack(nil))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
|
regularItems = make([]data.ItemInfo, 0, len(items))
|
|
|
|
|
|
regularIndexes = make([]int, 0, len(items))
|
|
|
|
|
|
itemIDs = make([]uint32, 0, len(items))
|
|
|
|
|
|
seenIDs = make(map[uint32]struct{}, len(items))
|
|
|
|
|
|
specialSuccess = make(map[int]bool, len(items))
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
for idx, item := range items {
|
|
|
|
|
|
if item.ItemCnt <= 0 {
|
|
|
|
|
|
sendErr(errorcode.ErrorCodes.ErrSystemError200007)
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch item.ItemId {
|
|
|
|
|
|
case 1, 3, 5, 9:
|
|
|
|
|
|
if p.ItemAdd(item.ItemId, item.ItemCnt) {
|
|
|
|
|
|
specialSuccess[idx] = true
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
regularItems = append(regularItems, item)
|
|
|
|
|
|
regularIndexes = append(regularIndexes, idx)
|
|
|
|
|
|
|
|
|
|
|
|
itemID := uint32(item.ItemId)
|
|
|
|
|
|
if _, ok := seenIDs[itemID]; ok {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
seenIDs[itemID] = struct{}{}
|
|
|
|
|
|
itemIDs = append(itemIDs, itemID)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if len(regularItems) == 0 {
|
|
|
|
|
|
result := make([]data.ItemInfo, 0, len(items))
|
|
|
|
|
|
for idx, item := range items {
|
|
|
|
|
|
if specialSuccess[idx] {
|
|
|
|
|
|
result = append(result, item)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
currentItems := p.Service.Item.CheakItemM(itemIDs...)
|
|
|
|
|
|
currentMap := make(map[uint32]int64, len(currentItems))
|
|
|
|
|
|
for _, item := range currentItems {
|
|
|
|
|
|
currentMap[item.ItemId] = item.ItemCnt
|
|
|
|
|
|
}
|
|
|
|
|
|
maxMap := dictrvice.NewDictInfoService().GetMaxMap(itemIDs...)
|
|
|
|
|
|
|
|
|
|
|
|
batchItems := make([]data.ItemInfo, 0, len(regularItems))
|
|
|
|
|
|
pendingMap := make(map[uint32]int64, len(itemIDs))
|
|
|
|
|
|
for _, item := range regularItems {
|
|
|
|
|
|
itemID := uint32(item.ItemId)
|
|
|
|
|
|
itemmax := maxMap[itemID]
|
|
|
|
|
|
if itemmax == 0 {
|
|
|
|
|
|
cool.Logger.Error(context.TODO(), "物品不存在", p.Info.UserID, item.ItemId)
|
|
|
|
|
|
sendErr(errorcode.ErrorCodes.ErrSystemError200007)
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if currentMap[itemID]+pendingMap[itemID]+item.ItemCnt > int64(itemmax) {
|
|
|
|
|
|
println(p.Info.UserID, "物品超过拥有最大限制", item.ItemId)
|
|
|
|
|
|
sendErr(errorcode.ErrorCodes.ErrTooManyOfItem)
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pendingMap[itemID] += item.ItemCnt
|
|
|
|
|
|
batchItems = append(batchItems, item)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addedRegularItems, err := p.Service.Item.AddItemsChecked(batchItems, currentMap)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
sendErr(errorcode.ErrorCodes.ErrSystemError200007)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
regularSuccess := make(map[int]bool, len(addedRegularItems))
|
|
|
|
|
|
regularPos := 0
|
|
|
|
|
|
for idx, item := range regularItems {
|
|
|
|
|
|
if regularPos < len(addedRegularItems) &&
|
|
|
|
|
|
addedRegularItems[regularPos].ItemId == item.ItemId &&
|
|
|
|
|
|
addedRegularItems[regularPos].ItemCnt == item.ItemCnt {
|
|
|
|
|
|
regularSuccess[regularIndexes[idx]] = true
|
|
|
|
|
|
regularPos++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result := make([]data.ItemInfo, 0, len(items))
|
|
|
|
|
|
for idx, item := range items {
|
|
|
|
|
|
if specialSuccess[idx] || regularSuccess[idx] {
|
|
|
|
|
|
result = append(result, item)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-12 04:28:20 +08:00
|
|
|
|
func (p *Player) ItemAdd(ItemId, ItemCnt int64) (result bool) {
|
|
|
|
|
|
if ItemCnt <= 0 {
|
|
|
|
|
|
t1 := common.NewTomeeHeader(2601, p.Info.UserID)
|
|
|
|
|
|
t1.Result = uint32(errorcode.ErrorCodes.ErrSystemError200007)
|
|
|
|
|
|
|
|
|
|
|
|
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
|
|
|
|
|
}
|
2025-12-08 21:11:12 +08:00
|
|
|
|
switch ItemId {
|
|
|
|
|
|
case 1: //塞尔豆
|
|
|
|
|
|
p.Info.Coins = p.Info.Coins + ItemCnt
|
|
|
|
|
|
return true
|
|
|
|
|
|
case 3: //累计经验
|
|
|
|
|
|
p.Info.ExpPool = p.Info.ExpPool + ItemCnt
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
case 5: //金豆ItemAdd
|
2025-12-17 00:05:03 +08:00
|
|
|
|
p.User.UpdateGold(p.Info.UserID, int64(ItemCnt*100))
|
2025-12-08 21:11:12 +08:00
|
|
|
|
return true
|
2025-12-26 20:38:08 +08:00
|
|
|
|
case 9: //学习力
|
|
|
|
|
|
p.Info.EVPool = p.Info.EVPool + ItemCnt
|
2025-12-08 21:11:12 +08:00
|
|
|
|
|
|
|
|
|
|
default:
|
2026-01-21 20:46:05 +00:00
|
|
|
|
itemmax := dictrvice.NewDictInfoService().GetMax(ItemId)
|
|
|
|
|
|
if itemmax == 0 {
|
2026-02-02 18:32:41 +08:00
|
|
|
|
cool.Logger.Error(context.TODO(), "物品不存在", p.Info.UserID, ItemId)
|
2025-10-25 15:06:05 +08:00
|
|
|
|
|
2025-11-19 16:11:02 +08:00
|
|
|
|
t1 := common.NewTomeeHeader(2601, p.Info.UserID)
|
2025-12-04 01:33:37 +08:00
|
|
|
|
t1.Result = uint32(errorcode.ErrorCodes.ErrSystemError200007)
|
2025-10-25 15:06:05 +08:00
|
|
|
|
|
2025-11-02 18:56:16 +08:00
|
|
|
|
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
2025-12-08 21:11:12 +08:00
|
|
|
|
return false
|
|
|
|
|
|
|
2025-11-02 18:56:16 +08:00
|
|
|
|
}
|
2025-10-25 15:06:05 +08:00
|
|
|
|
|
2026-02-12 04:28:20 +08:00
|
|
|
|
if p.Service.Item.CheakItem(uint32(ItemId))+int64(ItemCnt) > int64(itemmax) {
|
2025-12-08 19:16:37 +08:00
|
|
|
|
|
2025-12-08 21:11:12 +08:00
|
|
|
|
println(p.Info.UserID, "物品超过拥有最大限制", ItemId)
|
2025-11-19 16:11:02 +08:00
|
|
|
|
t1 := common.NewTomeeHeader(2601, p.Info.UserID)
|
2025-11-02 18:56:16 +08:00
|
|
|
|
t1.Result = uint32(errorcode.ErrorCodes.ErrTooManyOfItem)
|
2025-10-25 15:06:05 +08:00
|
|
|
|
|
2025-11-02 18:56:16 +08:00
|
|
|
|
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
2025-12-08 21:11:12 +08:00
|
|
|
|
return false
|
2025-10-25 15:06:05 +08:00
|
|
|
|
}
|
2026-04-14 13:06:28 +08:00
|
|
|
|
if err := p.Service.Item.UPDATE(uint32(ItemId), gconv.Int(ItemCnt)); err != nil {
|
|
|
|
|
|
cool.Logger.Error(context.TODO(), "item add update failed", p.Info.UserID, ItemId, ItemCnt, err)
|
|
|
|
|
|
|
|
|
|
|
|
t1 := common.NewTomeeHeader(2601, p.Info.UserID)
|
|
|
|
|
|
t1.Result = uint32(errorcode.ErrorCodes.ErrSystemError200007)
|
|
|
|
|
|
|
|
|
|
|
|
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
2025-12-08 21:11:12 +08:00
|
|
|
|
return true
|
2025-11-02 18:56:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-08 21:11:12 +08:00
|
|
|
|
return false
|
2025-09-11 01:07:00 +08:00
|
|
|
|
}
|
2026-02-07 00:18:14 +08:00
|
|
|
|
|
|
|
|
|
|
// Kick 是否热更退出
|
|
|
|
|
|
func (player1 *Player) Kick(isquit bool) {
|
2026-02-02 23:47:06 +08:00
|
|
|
|
if player1.Info == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-02-03 23:08:06 +08:00
|
|
|
|
|
2026-01-25 23:17:46 +08:00
|
|
|
|
head := common.NewTomeeHeader(1001, player1.Info.UserID)
|
2025-11-01 14:31:19 +08:00
|
|
|
|
|
2026-02-07 00:18:14 +08:00
|
|
|
|
head.Result = uint32(errorcode.ErrorCodes.ErrAccountLoggedInElsewhere)
|
|
|
|
|
|
if isquit {
|
|
|
|
|
|
head.Result = uint32(errorcode.ErrorCodes.ErrXinPlanSleepMode)
|
|
|
|
|
|
}
|
2026-02-02 23:11:14 +08:00
|
|
|
|
// 实际上这里有个问题,会造成重复保存问题
|
2026-01-25 23:17:46 +08:00
|
|
|
|
|
|
|
|
|
|
player1.SendPack(head.Pack(nil))
|
2026-02-03 22:44:13 +08:00
|
|
|
|
|
2026-01-25 23:17:46 +08:00
|
|
|
|
CloseChan := make(chan struct{})
|
|
|
|
|
|
|
|
|
|
|
|
player1.MainConn.CloseWithCallback(func(c gnet.Conn, err error) error {
|
|
|
|
|
|
close(CloseChan)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-02 23:11:14 +08:00
|
|
|
|
// --- 新增超时机制核心代码 ---
|
|
|
|
|
|
// 设定超时时间(可根据业务需求调整,这里以3秒为例)
|
2026-02-24 07:31:21 +08:00
|
|
|
|
const kickTimeout = 10 * time.Second
|
2026-04-05 11:14:25 +08:00
|
|
|
|
timeout := false
|
2026-02-02 23:11:14 +08:00
|
|
|
|
select {
|
|
|
|
|
|
case <-CloseChan:
|
|
|
|
|
|
// 正常流程:连接关闭回调已执行,CloseChan 被关闭
|
|
|
|
|
|
case <-time.After(kickTimeout):
|
2026-04-05 11:14:25 +08:00
|
|
|
|
timeout = true
|
|
|
|
|
|
}
|
|
|
|
|
|
player1.SaveOnDisconnect()
|
|
|
|
|
|
if timeout {
|
|
|
|
|
|
service.NewBaseSysLogService().RecordKick(uint32(player1.Info.UserID), fmt.Sprintf("踢人操作超时(超时时间:%v)", kickTimeout))
|
2026-02-02 23:11:14 +08:00
|
|
|
|
}
|
2025-10-26 14:56:29 +08:00
|
|
|
|
}
|
2025-09-06 01:47:08 +08:00
|
|
|
|
|
2026-02-03 23:08:06 +08:00
|
|
|
|
// 1是延迟踢人,0是强制踢人
|
|
|
|
|
|
func (player1 *Player) KickMessage() {
|
|
|
|
|
|
if player1.Info == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
// 取成功,否则创建
|
|
|
|
|
|
// player1.Save() //先保存数据再返回
|
|
|
|
|
|
head := common.NewTomeeHeader(1001, player1.Info.UserID)
|
|
|
|
|
|
|
|
|
|
|
|
head.Result = uint32(errorcode.ErrorCodes.ErrXinPlanSleepMode)
|
|
|
|
|
|
// 实际上这里有个问题,会造成重复保存问题
|
|
|
|
|
|
|
|
|
|
|
|
player1.SendPack(head.Pack(nil))
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-04 02:00:57 +08:00
|
|
|
|
func (p *Player) Cheak(b error) {
|
|
|
|
|
|
if b != nil {
|
|
|
|
|
|
g.Log().Error(context.Background(), "出现错误", p.Info.UserID, b.Error())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2026-01-19 18:51:56 +08:00
|
|
|
|
|
|
|
|
|
|
func (p *Player) GiveTitle(id uint32) {
|
2026-02-23 12:39:57 +08:00
|
|
|
|
r := p.Service.Title.Can(id) //已经有了就不给了
|
2026-02-26 16:58:25 +08:00
|
|
|
|
if r {
|
2026-02-23 12:39:57 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-01-19 18:51:56 +08:00
|
|
|
|
p.Service.Title.Give(id)
|
|
|
|
|
|
p.SendPackCmd(50005, &info.S2C_50005{
|
|
|
|
|
|
Title: id,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|