diff --git a/common/socket/ServerEvent.go b/common/socket/ServerEvent.go
index cd70138c0..cfe1f3d0a 100644
--- a/common/socket/ServerEvent.go
+++ b/common/socket/ServerEvent.go
@@ -1,295 +1,269 @@
-package socket
-
-import (
- "context"
- "encoding/binary"
- "errors"
- "io"
- "log"
- "os"
- "sync/atomic"
- "time"
-
- "blazing/cool"
- "blazing/logic/service/common"
- "blazing/logic/service/player"
- "blazing/modules/config/service"
-
- "github.com/gogf/gf/v2/frame/g"
- "github.com/gogf/gf/v2/os/gtime"
- "github.com/panjf2000/gnet/v2"
- "github.com/valyala/bytebufferpool"
-)
-
-func (s *Server) Boot(serverid, port uint32) error {
- // go s.bootws()
- s.serverid = serverid
- s.port = port
-
- err := gnet.Run(s, s.network+"://"+s.addr,
- gnet.WithMulticore(true),
- gnet.WithTicker(true),
-
- // 其他调优配置↓
- gnet.WithTCPNoDelay(gnet.TCPNoDelay), // 禁用Nagle算法(降低延迟,适合小数据包场景)
- //gnet.WithReusePort(true), // 开启SO_REUSEPORT(多核下提升并发)
- //gnet.WithReadBufferCap(1024*64), // 读缓冲区64KB(根据业务调整,默认太小)
- // gnet.WithWriteBufferCap(1024*64), // 写缓冲区64KB
-
- //gnet.WithLockOSThread(true), // 绑定goroutine到OS线程(减少上下文切换)
- )
- if err != nil {
- panic(err)
- }
-
- return nil
-}
-
-func (s *Server) Stop() error {
- _ = s.eng.Stop(context.Background())
- s.workerPool.Release()
-
- return nil
-}
-
-func (s *Server) OnClose(c gnet.Conn, err error) (action gnet.Action) {
- defer func() {
- if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
- // 1. 打印错误信息
- if t, ok := c.Context().(*player.ClientData); ok {
- if t.Player != nil {
- if t.Player.Info != nil {
- cool.Logger.Error(context.TODO(), "OnClose 错误:", cool.Config.ServerInfo.OnlineID, t.Player.Info.UserID, err)
- t.Player.Service.Info.Save(*t.Player.Info)
- }
-
- }
-
- } else {
- cool.Logger.Error(context.TODO(), "OnClose 错误:", cool.Config.ServerInfo.OnlineID, err)
-
- }
-
- }
- }()
- // 识别 RST 导致的连接中断(错误信息含 "connection reset")
- // if err != nil && (strings.Contains(err.Error(), "connection reset") || strings.Contains(err.Error(), "reset by peer")) {
- // remoteIP := c.RemoteAddr().(*net.TCPAddr).IP.String()
-
- // log.Printf("RST 攻击检测: 来源 %s, 累计攻击次数 %d", remoteIP)
-
- // // 防护逻辑:临时封禁异常 IP(可扩展为 IP 黑名单)
- // // go s.tempBlockIP(remoteIP, 5*time.Minute)
- // }
- //fmt.Println(err, c.RemoteAddr().String(), "断开连接")
- atomic.AddInt64(&cool.Connected, -1)
-
- //logging.Infof("conn[%v] disconnected", c.RemoteAddr().String())
- v, _ := c.Context().(*player.ClientData)
-
- v.LF.Close()
- // v.LF.Close()
- //close(v.MsgChan)
- if v.Player != nil {
- v.Player.Save() //保存玩家数据
-
- }
-
- //}
- //关闭连接
- return
-}
-func (s *Server) OnTick() (delay time.Duration, action gnet.Action) {
- g.Log().Async().Info(context.Background(), gtime.Now().ISO8601(), "服务器ID", cool.Config.ServerInfo.OnlineID, "链接数", atomic.LoadInt64(&cool.Connected))
- if s.quit && atomic.LoadInt64(&cool.Connected) == 0 {
- //执行正常退出逻辑
- os.Exit(0)
- }
- return 30 * time.Second, gnet.None
-}
-func (s *Server) OnBoot(eng gnet.Engine) gnet.Action {
- s.eng = eng
-
- service.NewServerService().SetServerID(s.serverid, s.port) //设置当前服务器端口
- return gnet.None
-}
-
-func (s *Server) OnOpen(conn gnet.Conn) (out []byte, action gnet.Action) {
- if s.network != "tcp" {
- return nil, gnet.Close
- }
-
- if conn.Context() == nil {
- conn.SetContext(player.NewClientData(conn)) //注入data
- }
-
- atomic.AddInt64(&cool.Connected, 1)
-
- return nil, gnet.None
-}
-
-func (s *Server) OnTraffic(c gnet.Conn) (action gnet.Action) {
- defer func() {
- if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
- // 1. 打印错误信息
- if t, ok := c.Context().(*player.ClientData); ok {
- if t.Player != nil {
- if t.Player.Info != nil {
- cool.Logger.Error(context.TODO(), "OnTraffic 错误:", cool.Config.ServerInfo.OnlineID, t.Player.Info.UserID, err)
- t.Player.Service.Info.Save(*t.Player.Info)
-
- }
-
- }
-
- }
-
- }
- }()
-
- ws := c.Context().(*player.ClientData).Wsmsg
- if ws.Tcp { //升级失败时候防止缓冲区溢出
- return s.handleTCP(c)
-
- }
-
- tt, len1 := ws.ReadBufferBytes(c)
- if tt == gnet.Close {
-
- return gnet.Close
- }
-
- ok, action := ws.Upgrade(c)
- if action != gnet.None { //连接断开
- return action
- }
- if !ok { //升级失败,说明是tcp连接
- ws.Tcp = true
-
- return s.handleTCP(c)
-
- }
- // fmt.Println(ws.Buf.Bytes())
- c.Discard(len1)
-
- messages, err := ws.Decode(c)
- if err != nil {
- return gnet.Close
- }
- if messages == nil {
- return
- }
-
- for _, msg := range messages {
-
- s.onevent(c, msg.Payload)
- //t.OnEvent(msg.Payload)
- }
-
- return gnet.None
-}
-
-const maxBodyLen = 10 * 1024 // 业务最大包体长度,按需调整
-func (s *Server) handleTCP(conn gnet.Conn) (action gnet.Action) {
-
- conn.Context().(*player.ClientData).IsCrossDomain.Do(func() { //跨域检测
- handle(conn)
- })
-
- // handle(c)
- // 先读取4字节的包长度
- lenBuf, err := conn.Peek(4)
-
- if err != nil {
- if errors.Is(err, io.ErrShortBuffer) {
- return
- }
- return gnet.Close
- }
-
- bodyLen := binary.BigEndian.Uint32(lenBuf)
-
- if bodyLen > maxBodyLen {
- return gnet.Close
- }
-
- if conn.InboundBuffered() < int(bodyLen) {
- return
- }
- // 提取包体
- body, err := conn.Next(int(bodyLen))
- if err != nil {
- if errors.Is(err, io.ErrShortBuffer) {
- return
- }
- return gnet.Close
- }
-
- s.onevent(conn, body)
-
- if conn.InboundBuffered() > 0 {
- if err := conn.Wake(nil); err != nil { // wake up the connection manually to avoid missing the leftover data
-
- return gnet.Close
- }
- }
- return action
-
-}
-
-// CROSS_DOMAIN 定义跨域策略文件内容
-const CROSS_DOMAIN = "\x00"
-
-// TEXT 定义跨域请求的文本格式
-const TEXT = "\x00"
-
-func handle(c gnet.Conn) {
-
- // 读取数据并检查是否为跨域请求
- 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))
-
- return
- }
-
- //return
-}
-
-func (s *Server) onevent(c gnet.Conn, v []byte) {
- if t, ok := c.Context().(*player.ClientData); ok {
- var header common.TomeeHeader
- // 解析Len(0-3字节)
- header.Len = binary.BigEndian.Uint32(v[0:4])
- // 解析Version(第4字节)
- //header.Version = v[4]
- // 解析CMD(5-8字节)
- header.CMD = binary.BigEndian.Uint32(v[5:9])
- // 解析UserID(9-12字节)
- header.UserID = binary.BigEndian.Uint32(v[9:13])
- // 解析Result(13-16字节)
- //header.Result = binary.BigEndian.Uint32(v[13:17])
- // 解析数据部分(17字节之后)
- // 数据部分:直接引用切片,避免 make
- if len(v) > 17 {
- header.Data = bytebufferpool.Get()
- header.Data.Write(v[17:])
- //copy(header.Data, v[17:]) // 核心修改:拷贝数据
- }
- //t.OnEvent(header)
- // t.LF.Push(header)
- s.workerPool.Submit(func() {
- t.LF.Producer().Write(header)
- // t.LF.Producer().Write(header)
- })
-
- }
-}
+package socket
+
+import (
+ "context"
+ "encoding/binary"
+ "errors"
+ "io"
+ "log"
+ "os"
+ "sync/atomic"
+ "time"
+
+ "blazing/cool"
+ "blazing/logic/service/player"
+ "blazing/modules/config/service"
+
+ "github.com/gogf/gf/v2/frame/g"
+ "github.com/gogf/gf/v2/os/gtime"
+ "github.com/panjf2000/gnet/v2"
+)
+
+func (s *Server) Boot(serverid, port uint32) error {
+ // go s.bootws()
+ s.serverid = serverid
+ s.port = port
+
+ err := gnet.Run(s, s.network+"://"+s.addr,
+ gnet.WithMulticore(true),
+ gnet.WithTicker(true),
+
+ // 其他调优配置↓
+ gnet.WithTCPNoDelay(gnet.TCPNoDelay), // 禁用Nagle算法(降低延迟,适合小数据包场景)
+ //gnet.WithReusePort(true), // 开启SO_REUSEPORT(多核下提升并发)
+ //gnet.WithReadBufferCap(1024*64), // 读缓冲区64KB(根据业务调整,默认太小)
+ // gnet.WithWriteBufferCap(1024*64), // 写缓冲区64KB
+
+ //gnet.WithLockOSThread(true), // 绑定goroutine到OS线程(减少上下文切换)
+ )
+ if err != nil {
+ panic(err)
+ }
+
+ return nil
+}
+
+func (s *Server) Stop() error {
+ _ = s.eng.Stop(context.Background())
+ s.workerPool.Release()
+
+ return nil
+}
+
+func (s *Server) OnClose(c gnet.Conn, err error) (action gnet.Action) {
+ defer func() {
+ if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
+ // 1. 打印错误信息
+ if t, ok := c.Context().(*player.ClientData); ok {
+ if t.Player != nil {
+ if t.Player.Info != nil {
+ cool.Logger.Error(context.TODO(), "OnClose 错误:", cool.Config.ServerInfo.OnlineID, t.Player.Info.UserID, err)
+ t.Player.Service.Info.Save(*t.Player.Info)
+ }
+
+ }
+
+ } else {
+ cool.Logger.Error(context.TODO(), "OnClose 错误:", cool.Config.ServerInfo.OnlineID, err)
+
+ }
+
+ }
+ }()
+ // 识别 RST 导致的连接中断(错误信息含 "connection reset")
+ // if err != nil && (strings.Contains(err.Error(), "connection reset") || strings.Contains(err.Error(), "reset by peer")) {
+ // remoteIP := c.RemoteAddr().(*net.TCPAddr).IP.String()
+
+ // log.Printf("RST 攻击检测: 来源 %s, 累计攻击次数 %d", remoteIP)
+
+ // // 防护逻辑:临时封禁异常 IP(可扩展为 IP 黑名单)
+ // // go s.tempBlockIP(remoteIP, 5*time.Minute)
+ // }
+ //fmt.Println(err, c.RemoteAddr().String(), "断开连接")
+ atomic.AddInt64(&cool.Connected, -1)
+
+ //logging.Infof("conn[%v] disconnected", c.RemoteAddr().String())
+ v, _ := c.Context().(*player.ClientData)
+
+ v.LF.Close()
+ // v.LF.Close()
+ //close(v.MsgChan)
+ if v.Player != nil {
+ v.Player.Save() //保存玩家数据
+
+ }
+
+ //}
+ //关闭连接
+ return
+}
+func (s *Server) OnTick() (delay time.Duration, action gnet.Action) {
+ g.Log().Async().Info(context.Background(), gtime.Now().ISO8601(), "服务器ID", cool.Config.ServerInfo.OnlineID, "链接数", atomic.LoadInt64(&cool.Connected))
+ if s.quit && atomic.LoadInt64(&cool.Connected) == 0 {
+ //执行正常退出逻辑
+ os.Exit(0)
+ }
+ return 30 * time.Second, gnet.None
+}
+func (s *Server) OnBoot(eng gnet.Engine) gnet.Action {
+ s.eng = eng
+
+ service.NewServerService().SetServerID(s.serverid, s.port) //设置当前服务器端口
+ return gnet.None
+}
+
+func (s *Server) OnOpen(conn gnet.Conn) (out []byte, action gnet.Action) {
+ if s.network != "tcp" {
+ return nil, gnet.Close
+ }
+
+ if conn.Context() == nil {
+ conn.SetContext(player.NewClientData(conn)) //注入data
+ }
+
+ atomic.AddInt64(&cool.Connected, 1)
+
+ return nil, gnet.None
+}
+
+func (s *Server) OnTraffic(c gnet.Conn) (action gnet.Action) {
+ defer func() {
+ if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
+ // 1. 打印错误信息
+ if t, ok := c.Context().(*player.ClientData); ok {
+ if t.Player != nil {
+ if t.Player.Info != nil {
+ cool.Logger.Error(context.TODO(), "OnTraffic 错误:", cool.Config.ServerInfo.OnlineID, t.Player.Info.UserID, err)
+ t.Player.Service.Info.Save(*t.Player.Info)
+
+ }
+
+ }
+
+ }
+
+ }
+ }()
+
+ ws := c.Context().(*player.ClientData).Wsmsg
+ if ws.Tcp { //升级失败时候防止缓冲区溢出
+ return s.handleTCP(c)
+
+ }
+
+ tt, len1 := ws.ReadBufferBytes(c)
+ if tt == gnet.Close {
+
+ return gnet.Close
+ }
+
+ ok, action := ws.Upgrade(c)
+ if action != gnet.None { //连接断开
+ return action
+ }
+ if !ok { //升级失败,说明是tcp连接
+ ws.Tcp = true
+
+ return s.handleTCP(c)
+
+ }
+ // fmt.Println(ws.Buf.Bytes())
+ c.Discard(len1)
+
+ messages, err := ws.Decode(c)
+ if err != nil {
+ return gnet.Close
+ }
+ if messages == nil {
+ return
+ }
+
+ for _, msg := range messages {
+
+ s.onevent(c, msg.Payload)
+ //t.OnEvent(msg.Payload)
+ }
+
+ return gnet.None
+}
+
+const maxBodyLen = 10 * 1024 // 业务最大包体长度,按需调整
+func (s *Server) handleTCP(conn gnet.Conn) (action gnet.Action) {
+
+ conn.Context().(*player.ClientData).IsCrossDomain.Do(func() { //跨域检测
+ handle(conn)
+ })
+
+ // handle(c)
+ // 先读取4字节的包长度
+ lenBuf, err := conn.Peek(4)
+
+ if err != nil {
+ if errors.Is(err, io.ErrShortBuffer) {
+ return
+ }
+ return gnet.Close
+ }
+
+ bodyLen := binary.BigEndian.Uint32(lenBuf)
+
+ if bodyLen > maxBodyLen {
+ return gnet.Close
+ }
+
+ if conn.InboundBuffered() < int(bodyLen) {
+ return
+ }
+ // 提取包体
+ body, err := conn.Next(int(bodyLen))
+ if err != nil {
+ if errors.Is(err, io.ErrShortBuffer) {
+ return
+ }
+ return gnet.Close
+ }
+
+ s.onevent(conn, body)
+
+ if conn.InboundBuffered() > 0 {
+ if err := conn.Wake(nil); err != nil { // wake up the connection manually to avoid missing the leftover data
+
+ return gnet.Close
+ }
+ }
+ return action
+
+}
+
+// CROSS_DOMAIN 定义跨域策略文件内容
+const CROSS_DOMAIN = "\x00"
+
+// TEXT 定义跨域请求的文本格式
+const TEXT = "\x00"
+
+func handle(c gnet.Conn) {
+
+ // 读取数据并检查是否为跨域请求
+ 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))
+
+ return
+ }
+
+ //return
+}
+
+func (s *Server) onevent(c gnet.Conn, v []byte) {
+ if t, ok := c.Context().(*player.ClientData); ok {
+ t.PushEvent(v, s.workerPool.Submit)
+ }
+}
diff --git a/common/socket/ServerOption.go b/common/socket/ServerOption.go
index c2d243289..e0c1668e3 100644
--- a/common/socket/ServerOption.go
+++ b/common/socket/ServerOption.go
@@ -38,7 +38,7 @@ func NewServer(options ...Option) *Server {
// handler: handler.NewTomeeHandler(), //请求返回
codec: codec.NewTomeeSocketCodec(), //默认解码器 len+pack
workerPool: goroutine.Default(),
- bufferSize: 40960, //默认缓冲区大小
+ bufferSize: 40960, //默认缓冲区大小
multicore: true,
//batchRead: 8,
//discorse: true,
diff --git a/logic/controller/buy_seerdou_item.go b/logic/controller/buy_seerdou_item.go
new file mode 100644
index 000000000..ea32d522b
--- /dev/null
+++ b/logic/controller/buy_seerdou_item.go
@@ -0,0 +1,30 @@
+package controller
+
+import (
+ "blazing/common/data/xmlres"
+ "blazing/common/socket/errorcode"
+ "blazing/logic/service/player"
+)
+
+func buySeerdouBackpackItem(player *player.Player, itemID int64, count int64) (bought bool, err errorcode.ErrorCode) {
+ if itemID <= 0 || count <= 0 {
+ return false, errorcode.ErrorCodes.ErrSystemError
+ }
+
+ itemInfo, exists := xmlres.ItemsMAP[int(itemID)]
+ if !exists {
+ return false, 0
+ }
+
+ totalCost := int64(itemInfo.Price) * count
+ if totalCost > 0 && !player.GetCoins(totalCost) {
+ return false, errorcode.ErrorCodes.ErrSunDouInsufficient10016
+ }
+
+ if !player.ItemAdd(itemID, count) {
+ return false, 0
+ }
+
+ player.Info.Coins -= totalCost
+ return true, 0
+}
diff --git a/logic/controller/fight_boss野怪和地图怪.go b/logic/controller/fight_boss野怪和地图怪.go
index 4eae9927b..e29ac6b0a 100644
--- a/logic/controller/fight_boss野怪和地图怪.go
+++ b/logic/controller/fight_boss野怪和地图怪.go
@@ -7,9 +7,8 @@ import (
"strings"
"blazing/logic/service/fight"
- "blazing/logic/service/fight/info"
+ fightinfo "blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
-
"blazing/logic/service/player"
configmodel "blazing/modules/config/model"
"blazing/modules/config/service"
@@ -20,217 +19,259 @@ import (
)
// PlayerFightBoss 挑战地图boss
-// data: 包含挑战Boss信息的输入数据
-// player: 当前玩家对象
-// 返回: 战斗结果和错误码
-func (Controller) PlayerFightBoss(data1 *fight.ChallengeBossInboundInfo, p *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
- r := p.CanFight()
- if p.CanFight() != 0 {
- return nil, r
- }
- var monster *model.PetInfo
- monsterInfo := &model.PlayerInfo{}
-
- mdata := service.NewMapNodeService().GetDataNode(p.Info.MapID, data1.BossId)
- if mdata == nil {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- }
- var bosinfo []configmodel.BossConfig
- switch len(mdata.BossIds) {
- case 0:
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- case 1:
- bosinfo = service.NewBossService().Get(mdata.BossIds[0])
- default:
- bosinfo = service.NewBossService().Get(mdata.BossIds[grand.Intn(len(mdata.BossIds))])
+func (Controller) PlayerFightBoss(req *fight.ChallengeBossInboundInfo, p *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
+ if err = p.CanFight(); err != 0 {
+ return nil, err
}
- if len(bosinfo) == 0 {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ mapNode := service.NewMapNodeService().GetDataNode(p.Info.MapID, req.BossId)
+ bossConfigs, err := loadMapBossConfigs(mapNode)
+ if err != 0 {
+ return nil, err
}
- dv := 24
- ger := 0
- if bosinfo[0].IsCapture == 1 {
- dv = -1
- ger = -1
+
+ monsterInfo, leadMonsterID, err := buildBossMonsterInfo(mapNode.NodeName, bossConfigs)
+ if err != 0 {
+ return nil, err
}
- for i, bm := range bosinfo {
- monster = model.GenPetInfo(
- gconv.Int(bm.MonID), dv, //24个体
- -1,
- 0, //野怪没特性
-
- int(bm.Lv), nil, ger)
- monster.CatchTime = uint32(i)
- monster.ConfigBoss(bm.PetBaseConfig)
- effects := service.NewEffectService().Args(bm.Effect)
-
- for _, v := range effects {
- monster.EffectInfo = append(monster.EffectInfo, model.PetEffectInfo{
- Idx: uint16(v.SeIdx),
- EID: gconv.Uint16(v.Eid),
- Args: gconv.Ints(v.Args),
- })
- }
- monsterInfo.PetList = append(monsterInfo.PetList, *monster)
- }
- if bosinfo[0].IsCapture == 1 {
- monsterInfo.PetList[0].ShinyInfo = make([]data.GlowFilter, 0)
- if grand.Meet(1, 500) {
- monsterInfo.PetList[0].RandomByWeightShiny()
- }
-
- }
- monsterInfo.Nick = mdata.NodeName //xmlres.PetMAP[int(monster.ID)].DefName
-
- if len(monsterInfo.PetList) == 0 {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- }
- p.Fightinfo.Status = info.BattleMode.FIGHT_WITH_NPC
- p.Fightinfo.Mode = info.BattleMode.MULTI_MODE
+ p.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC
+ p.Fightinfo.Mode = fightinfo.BattleMode.MULTI_MODE
ai := player.NewAI_player(monsterInfo)
- ai.CanCapture = bosinfo[0].IsCapture
- if bosinfo[0].IsCapture != 0 {
- ai.CanCapture = xmlres.PetMAP[int(monster.ID)].CatchRate
- if xmlres.PetMAP[int(monster.ID)].CatchRate == 0 {
- ai.CanCapture = 0
- }
- }
-
+ ai.CanCapture = resolveBossCaptureRate(bossConfigs[0].IsCapture, leadMonsterID)
ai.Prop[0] = 2
- var fighc *fight.FightC
- fighc, _ = fight.NewFight(p, ai, p.GetPetInfo(100), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
- if mdata.WinBonusID != 0 {
- if len(bosinfo[0].Rule) == 0 {
- if foi.Reason == 0 && foi.WinnerId == p.Info.UserID {
- p.SptCompletedTask(mdata.WinBonusID, 1)
-
- }
- } else {
- //说明是带规则的
- iswin := true
- for _, v := range service.NewFightRuleService().GetByRuleIdxs(bosinfo[0].Rule) {
- r := input.GetRule(int64(v.RuleIdx))
- if r != nil {
- r.SetArgs(v.Args...)
-
- if !(r.Exec(fighc, &foi)) {
- iswin = false
- break
- }
- }
-
- }
- if iswin {
- p.SptCompletedTask(mdata.WinBonusID, 1)
- }
- }
+ var fightC *fight.FightC
+ fightC, err = fight.NewFight(p, ai, p.GetPetInfo(100), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
+ if mapNode.WinBonusID == 0 {
+ return
+ }
+ if shouldGrantBossWinBonus(fightC, p.Info.UserID, bossConfigs[0], foi) {
+ p.SptCompletedTask(mapNode.WinBonusID, 1)
}
-
- //p.Done.Exec(model.MilestoneMode.BOSS, []uint32{p.Info.MapID, data.BossId, uint32(foi.Reason)}, nil)
-
})
+ if err != 0 {
+ return nil, err
+ }
return nil, -1
}
// OnPlayerFightNpcMonster 战斗野怪
-// data: 包含战斗野怪信息的输入数据
-// player: 当前玩家对象
-// 返回: 战斗结果和错误码
-func (Controller) OnPlayerFightNpcMonster(data1 *fight.FightNpcMonsterInboundInfo, p *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
- r := p.CanFight()
- if p.CanFight() != 0 {
- return nil, r
+func (Controller) OnPlayerFightNpcMonster(req *fight.FightNpcMonsterInboundInfo, p *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
+ if err = p.CanFight(); err != 0 {
+ return nil, err
}
- if data1.Number > 9 {
+ if req.Number > 9 {
return nil, errorcode.ErrorCodes.ErrSystemError
}
- refPet := p.Data[data1.Number]
- if refPet.ID == 0 {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ refPet := p.Data[req.Number]
+ monster, monsterInfo, err := buildNpcMonsterInfo(refPet, p.Info.MapID)
+ if err != 0 {
+ return nil, err
}
- monster := model.GenPetInfo(
- int(refPet.GetID()), -1,
- -1,
- 0, //野怪没特性
-
- int(refPet.GetLevel()),
- refPet.ShinyInfo, -1)
- monster.CatchMap = p.Info.MapID //设置当前地图
- if refPet.Ext != 0 {
- if grand.Meet(1, 500) {
- monster.RandomByWeightShiny()
- }
- }
-
- monsterInfo := &model.PlayerInfo{}
- monsterInfo.Nick = xmlres.PetMAP[int(monster.ID)].DefName
- monsterInfo.PetList = append(monsterInfo.PetList, *monster)
ai := player.NewAI_player(monsterInfo)
+ ai.CanCapture = refPet.IsCapture
- ai.CanCapture = refPet.IsCapture //handleNPCFightSpecial(monster.ID)
-
- p.Fightinfo.Status = info.BattleMode.FIGHT_WITH_NPC //打野怪
- p.Fightinfo.Mode = info.BattleMode.MULTI_MODE //多人模式
-
- fight.NewFight(p, ai, p.GetPetInfo(100), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
- //p.Done.Exec(model.MilestoneMode.Moster, []uint32{p.Info.MapID, monsterInfo.PetList[0].ID, uint32(foi.Reason)}, nil)
- if foi.Reason == 0 && foi.WinnerId == p.Info.UserID && p.CanGet() {
-
- exp := uint32(xmlres.PetMAP[int(monster.ID)].YieldingExp) * monster.Level / 7
- addlevel, poolevel := p.CanGetExp()
- addexp := gconv.Float32(addlevel * gconv.Float32(exp))
-
- poolexp := gconv.Float32(poolevel) * gconv.Float32((exp))
- items := &info.S2C_GET_BOSS_MONSTER{}
-
- p.ItemAdd(3, int64(poolexp+addexp))
- items.ADDitem(3, uint32(poolexp))
-
- p.AddPetExp(foi.Winpet, int64(addexp))
- pettype := int64(xmlres.PetMAP[int(refPet.GetID())].Type)
-
- if p.CanGetItem() {
-
- item := p.GetSpace().GetDrop()
- if item != 0 {
- count := int64(grand.N(1, 2))
- ok := p.ItemAdd(item, count)
- if ok {
- items.ADDitem(uint32(item), uint32(count))
- }
- }
-
- }
- if monster.IsShiny() && p.CanGetXUAN() && pettype < 16 {
- xuan := 400686 + pettype
- count := uint32(grand.N(1, 2))
- ok := p.ItemAdd(xuan, int64(count))
- if ok {
- items.ADDitem(uint32(xuan), count)
- }
-
- }
- p.SendPackCmd(8004, items)
-
- evs := gconv.Int64s(strings.Split(xmlres.PetMAP[int(monster.ID)].YieldingEV, " "))
-
- foi.Winpet.AddEV(evs)
- //取消累计学习力掉落
- // if leve == 8 {
- // items.EV = lo.Sum(evs) - 1
- // p.Info.EVPool += lo.Sum(evs) //给予累计学习力
- // }
-
- }
+ p.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC
+ p.Fightinfo.Mode = fightinfo.BattleMode.MULTI_MODE
+ _, err = fight.NewFight(p, ai, p.GetPetInfo(100), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
+ handleNpcFightRewards(p, foi, monster)
})
+ if err != 0 {
+ return nil, err
+ }
return nil, -1
}
+
+func loadMapBossConfigs(mapNode *configmodel.MapNode) ([]configmodel.BossConfig, errorcode.ErrorCode) {
+ if mapNode == nil || len(mapNode.BossIds) == 0 {
+ return nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+
+ bossID := mapNode.BossIds[0]
+ if len(mapNode.BossIds) > 1 {
+ bossID = mapNode.BossIds[grand.Intn(len(mapNode.BossIds))]
+ }
+
+ bossConfigs := service.NewBossService().Get(bossID)
+ if len(bossConfigs) == 0 {
+ return nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+
+ return bossConfigs, 0
+}
+
+func buildBossMonsterInfo(nodeName string, bossConfigs []configmodel.BossConfig) (*model.PlayerInfo, uint32, errorcode.ErrorCode) {
+ monsterInfo := &model.PlayerInfo{Nick: nodeName}
+ var leadMonsterID uint32
+
+ for i, bossConfig := range bossConfigs {
+ dv, generation := bossFightPetArgs(bossConfig.IsCapture)
+ monster := model.GenPetInfo(
+ gconv.Int(bossConfig.MonID),
+ dv,
+ -1,
+ 0,
+ int(bossConfig.Lv),
+ nil,
+ generation,
+ )
+ if monster == nil {
+ return nil, 0, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+ monster.CatchTime = uint32(i)
+ monster.ConfigBoss(bossConfig.PetBaseConfig)
+ appendPetEffects(monster, bossConfig.Effect)
+
+ if i == 0 {
+ leadMonsterID = monster.ID
+ }
+ monsterInfo.PetList = append(monsterInfo.PetList, *monster)
+ }
+
+ if len(monsterInfo.PetList) == 0 {
+ return nil, 0, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+
+ if bossConfigs[0].IsCapture == 1 {
+ monsterInfo.PetList[0].ShinyInfo = make([]data.GlowFilter, 0)
+ if grand.Meet(1, 500) {
+ monsterInfo.PetList[0].RandomByWeightShiny()
+ }
+ }
+
+ return monsterInfo, leadMonsterID, 0
+}
+
+func bossFightPetArgs(canCapture int) (dv int, generation int) {
+ if canCapture == 1 {
+ return -1, -1
+ }
+ return 24, 0
+}
+
+func appendPetEffects(monster *model.PetInfo, effectIDs []uint32) {
+ effects := service.NewEffectService().Args(effectIDs)
+ for _, effect := range effects {
+ monster.EffectInfo = append(monster.EffectInfo, model.PetEffectInfo{
+ Idx: uint16(effect.SeIdx),
+ EID: gconv.Uint16(effect.Eid),
+ Args: gconv.Ints(effect.Args),
+ })
+ }
+}
+
+func resolveBossCaptureRate(canCapture int, petID uint32) int {
+ if canCapture == 0 {
+ return 0
+ }
+
+ petCfg, ok := xmlres.PetMAP[int(petID)]
+ if !ok || petCfg.CatchRate == 0 {
+ return 0
+ }
+
+ return petCfg.CatchRate
+}
+
+func shouldGrantBossWinBonus(fightC *fight.FightC, playerID uint32, bossConfig configmodel.BossConfig, foi model.FightOverInfo) bool {
+ if len(bossConfig.Rule) == 0 {
+ return foi.Reason == 0 && foi.WinnerId == playerID
+ }
+
+ for _, ruleConfig := range service.NewFightRuleService().GetByRuleIdxs(bossConfig.Rule) {
+ rule := input.GetRule(int64(ruleConfig.RuleIdx))
+ if rule == nil {
+ continue
+ }
+ rule.SetArgs(ruleConfig.Args...)
+ if !rule.Exec(fightC, &foi) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func buildNpcMonsterInfo(refPet player.OgrePetInfo, mapID uint32) (*model.PetInfo, *model.PlayerInfo, errorcode.ErrorCode) {
+ if refPet.ID == 0 {
+ return nil, nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+
+ monster := model.GenPetInfo(
+ refPet.GetID(),
+ -1,
+ -1,
+ 0,
+ refPet.GetLevel(),
+ refPet.ShinyInfo,
+ -1,
+ )
+ if monster == nil {
+ return nil, nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+ monster.CatchMap = mapID
+ if refPet.Ext != 0 && grand.Meet(1, 500) {
+ monster.RandomByWeightShiny()
+ }
+
+ petCfg, ok := xmlres.PetMAP[int(monster.ID)]
+ if !ok {
+ return nil, nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+
+ monsterInfo := &model.PlayerInfo{
+ Nick: petCfg.DefName,
+ PetList: []model.PetInfo{*monster},
+ }
+ return monster, monsterInfo, 0
+}
+
+func handleNpcFightRewards(p *player.Player, foi model.FightOverInfo, monster *model.PetInfo) {
+ if foi.Reason != 0 || foi.WinnerId != p.Info.UserID || !p.CanGet() {
+ return
+ }
+
+ petCfg, ok := xmlres.PetMAP[int(monster.ID)]
+ if !ok {
+ return
+ }
+
+ exp := uint32(petCfg.YieldingExp) * monster.Level / 7
+ addlevel, poolevel := p.CanGetExp()
+ addexp := gconv.Float32(addlevel * gconv.Float32(exp))
+ poolexp := gconv.Float32(poolevel) * gconv.Float32(exp)
+ rewards := &fightinfo.S2C_GET_BOSS_MONSTER{}
+
+ p.ItemAdd(3, int64(poolexp+addexp))
+ rewards.ADDitem(3, uint32(poolexp))
+ p.AddPetExp(foi.Winpet, int64(addexp))
+
+ if p.CanGetItem() {
+ itemID := p.GetSpace().GetDrop()
+ if itemID != 0 {
+ count := uint32(grand.N(1, 2))
+ if p.ItemAdd(itemID, int64(count)) {
+ rewards.ADDitem(uint32(itemID), count)
+ }
+ }
+ }
+
+ petType := int64(petCfg.Type)
+ if monster.IsShiny() && p.CanGetXUAN() && petType < 16 {
+ xuanID := uint32(400686 + petType)
+ count := uint32(grand.N(1, 2))
+ if p.ItemAdd(int64(xuanID), int64(count)) {
+ rewards.ADDitem(xuanID, count)
+ }
+ }
+
+ p.SendPackCmd(8004, rewards)
+ foi.Winpet.AddEV(gconv.Int64s(strings.Fields(petCfg.YieldingEV)))
+}
diff --git a/logic/controller/fight_塔.go b/logic/controller/fight_塔.go
index 86cbaa6d3..b821dc254 100644
--- a/logic/controller/fight_塔.go
+++ b/logic/controller/fight_塔.go
@@ -17,201 +17,239 @@ import (
"github.com/jinzhu/copier"
)
+const (
+ towerCmdChoiceTrial uint32 = 2428
+ towerCmdChoiceBrave uint32 = 2414
+ towerCmdFightTrial uint32 = 2429
+ towerCmdFightBrave uint32 = 2415
+ towerCmdFightDark uint32 = 2425
+
+ towerTaskDark int = 110
+ towerTaskBrave int = 500
+ towerTaskTrial int = 600
+)
+
+type towerChoiceState struct {
+ currentLevel *uint32
+ maxLevel *uint32
+ service *service.TowerService
+}
+
// 暗黑门进入boss
func (h Controller) FreshOpen(data *fight.C2S_OPEN_DARKPORTAL, c *player.Player) (result *fight.S2C_OPEN_DARKPORTAL, err errorcode.ErrorCode) {
-
result = &fight.S2C_OPEN_DARKPORTAL{}
- // c.Info.CurrentFreshStage = utils.Max(c.Info.CurrentFreshStage, 1)
- // c.Info.CurrentStage = utils.Max(c.Info.CurrentStage, 1)
- boss := service.NewTower110Service().Boss(uint32(data.Level))
- if len(boss) == 0 {
+
+ towerBosses := service.NewTower110Service().Boss(uint32(data.Level))
+ bossConfig, ok := firstTowerBossConfig(towerBosses)
+ if !ok {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
- if len(boss[0].BossIds) == 0 {
+
+ bosses := service.NewBossService().Get(bossConfig.BossIds[0])
+ if len(bosses) == 0 {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
- result = &fight.S2C_OPEN_DARKPORTAL{}
- r := service.NewBossService().Get(boss[0].BossIds[0])
- result.CurBossID = uint32(r[0].MonID)
+ result.CurBossID = uint32(bosses[0].MonID)
c.CurDark = uint32(data.Level)
defer c.GetSpace().LeaveMap(c)
return result, 0
}
// FreshChoiceFightLevel 处理玩家选择挑战模式(试炼之塔或勇者之塔)
-// 根据不同的CMD值设置玩家的挑战状态和地图信息,并返回当前挑战层级信息
-// 参数:
-//
-// data: 客户端发送的挑战层级选择请求数据,包含CMD和挑战层级
-// c: 玩家对象,包含玩家的详细信息
-//
-// 返回值:
-//
-// result: 服务器返回给客户端的挑战层级信息,包含当前战斗层级和Boss ID
-// err: 错误码,表示处理过程中是否出现错误
func (h Controller) FreshChoiceFightLevel(data *fight.C2S_FRESH_CHOICE_FIGHT_LEVEL, c *player.Player) (result *fight.S2C_FreshChoiceLevelRequestInfo, err errorcode.ErrorCode) {
-
result = &fight.S2C_FreshChoiceLevelRequestInfo{}
c.Info.CurrentFreshStage = utils.Max(c.Info.CurrentFreshStage, 1)
c.Info.CurrentStage = utils.Max(c.Info.CurrentStage, 1)
+ choiceState, ok := towerChoiceRuntime(c, data.Head.CMD)
+ if !ok {
+ return nil, errorcode.ErrorCodes.ErrSystemError
+ }
+
if data.Level > 0 {
- switch data.Head.CMD {
- case 2428: //试炼之塔
- if data.Level > uint(c.Info.MaxFreshStage) && data.Level != 1 {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- }
-
- c.Info.CurrentFreshStage = uint32(data.Level)
- case 2414: //勇者之塔
- if data.Level > uint(c.Info.MaxStage) && data.Level != 1 {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- }
- c.Info.CurrentStage = uint32(data.Level)
-
+ if !canSelectTowerLevel(data.Level, *choiceState.maxLevel) {
+ return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
-
+ *choiceState.currentLevel = uint32(data.Level)
}
- var boss []configmodel.BaseTowerConfig
- switch data.Head.CMD {
- case 2428: //试炼之塔
- result.CurFightLevel = uint32(c.Info.CurrentFreshStage)
- boss = service.NewTower600Service().Boss(c.Info.CurrentFreshStage)
+ result.CurFightLevel = *choiceState.currentLevel
+ appendTowerBossPreview(&result.BossId, choiceState.service.Boss(*choiceState.currentLevel))
- case 2414: //勇者之塔
-
- result.CurFightLevel = uint32(c.Info.CurrentStage)
- boss = service.NewTower500Service().Boss(c.Info.CurrentStage)
- //next := service.NewTower600Service().Boss(c.Info.CurrentFreshStage + 1)
-
- }
- if len(boss) != 0 && len(boss[0].BossIds) != 0 {
- //单节点,取获取到的一个,然后因为不是剧情,所以只有一层
- r := service.NewBossService().Get(boss[0].BossIds[0])
- for _, v := range r {
- result.BossId = append(result.BossId, uint32(v.MonID))
- }
-
- }
- // 重置玩家的Canmon标志位为0,表示可以刷怪
atomic.StoreUint32(&c.Canmon, 0)
- // 在函数结束时将玩家传送到对应地图
defer c.GetSpace().LeaveMap(c)
return result, 0
}
-func (h Controller) FreshLeaveFightLevel(data *fight.FRESH_LEAVE_FIGHT_LEVEL, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
+func (h Controller) FreshLeaveFightLevel(data *fight.FRESH_LEAVE_FIGHT_LEVEL, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
+ _ = data
defer c.GetSpace().EnterMap(c)
out := info.NewOutInfo()
copier.CopyWithOption(out, c.GetInfo(), copier.Option{DeepCopy: true})
- //c.SendPackCmd(2001, out)
return result, 0
}
func (h Controller) PetTawor(data *fight.StartTwarInboundInfo, c *player.Player) (result *fight.S2C_ChoiceLevelRequestInfo, err errorcode.ErrorCode) {
- r := c.CanFight()
- if c.CanFight() != 0 {
- return nil, r
+ if err = c.CanFight(); err != 0 {
+ return nil, err
}
+
+ bossList, currentLevel, taskID, ok := towerFightBosses(c, data.Head.CMD)
+ if !ok {
+ return nil, errorcode.ErrorCodes.ErrSystemError
+ }
+
+ currentBoss, ok := firstTowerBossConfig(bossList)
+ if !ok {
+ return nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+
+ result = &fight.S2C_ChoiceLevelRequestInfo{CurFightLevel: currentLevel}
+ appendTowerNextBossPreview(&result.BossID, bossList)
+
+ monsterInfo, ok := buildTowerMonsterInfo(currentBoss)
+ if !ok {
+ return nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+
c.Fightinfo.Mode = fightinfo.BattleMode.MULTI_MODE
c.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC
- monsterInfo := &model.PlayerInfo{}
- var boss []configmodel.BaseTowerConfig
-
- result = &fight.S2C_ChoiceLevelRequestInfo{}
- switch data.Head.CMD {
- case 2429: //试炼之塔
- boss = service.NewTower600Service().Boss(c.Info.CurrentFreshStage, c.Info.CurrentFreshStage+1)
-
- result.CurFightLevel = uint32(c.Info.CurrentFreshStage)
- case 2415: //勇者之塔
- boss = service.NewTower500Service().Boss(c.Info.CurrentStage, c.Info.CurrentStage+1)
-
- result.CurFightLevel = uint32(c.Info.CurrentStage)
- case 2425:
- boss = service.NewTower110Service().Boss(c.CurDark)
-
- }
- if len(boss) > 1 {
- r := service.NewBossService().Get(boss[1].BossIds[0])
- for _, v := range r {
- result.BossID = append(result.BossID, uint32(v.MonID))
- }
-
- }
-
- bosss := service.NewBossService().Get(boss[0].BossIds[0])
- monsterInfo.Nick = boss[0].Name
- for i, r := range bosss {
-
- monster := model.GenPetInfo(int(r.MonID), 24, int(r.Nature), 0, int(r.Lv), nil, 0)
- if r.Hp != 0 {
- monster.Hp = uint32(r.Hp)
- monster.MaxHp = uint32(r.Hp)
-
- }
-
- for i, v := range r.Prop {
- if v != 0 {
- monster.Prop[i] = v
- }
-
- }
-
- if len(r.SKill) != 0 {
- for i := 0; i < len(monster.SkillList); i++ {
- if r.SKill[i] != 0 {
- monster.SkillList[i].ID = r.SKill[i]
- }
-
- }
-
- }
-
- effects := service.NewEffectService().Args(r.Effect)
-
- for _, v := range effects {
- monster.EffectInfo = append(monster.EffectInfo, model.PetEffectInfo{
- Idx: uint16(v.SeIdx),
- EID: gconv.Uint16(v.Eid),
- Args: gconv.Ints(v.Args),
- })
- }
- monster.CatchTime = uint32(i)
- monsterInfo.PetList = append(monsterInfo.PetList, *monster)
-
- }
ai := player.NewAI_player(monsterInfo)
-
_, err = fight.NewFight(c, ai, c.GetPetInfo(100), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
-
- if foi.Reason == 0 && foi.WinnerId == c.Info.UserID { //我放获胜
- switch data.Head.CMD {
- case 2429: //试炼之塔
- c.TawerCompletedTask(600, int(c.Info.CurrentFreshStage))
- c.Info.CurrentFreshStage++
- if c.Info.CurrentFreshStage >= c.Info.MaxFreshStage {
- c.Info.MaxFreshStage = c.Info.CurrentFreshStage
- }
-
- case 2415: //勇者之塔
- c.TawerCompletedTask(500, int(c.Info.CurrentStage))
- c.Info.CurrentStage++
- if c.Info.CurrentStage >= c.Info.MaxStage {
- c.Info.MaxStage = c.Info.CurrentStage
- }
- case 2425:
- c.TawerCompletedTask(110, int(c.CurDark))
- }
-
+ if foi.Reason != 0 || foi.WinnerId != c.Info.UserID {
+ return
}
-
- }) ///开始对战,房主方以及被邀请方
+ handleTowerFightWin(c, data.Head.CMD, taskID, currentLevel)
+ })
return
}
+
+func towerChoiceRuntime(c *player.Player, cmd uint32) (towerChoiceState, bool) {
+ switch cmd {
+ case towerCmdChoiceTrial:
+ return towerChoiceState{
+ currentLevel: &c.Info.CurrentFreshStage,
+ maxLevel: &c.Info.MaxFreshStage,
+ service: service.NewTower600Service(),
+ }, true
+ case towerCmdChoiceBrave:
+ return towerChoiceState{
+ currentLevel: &c.Info.CurrentStage,
+ maxLevel: &c.Info.MaxStage,
+ service: service.NewTower500Service(),
+ }, true
+ default:
+ return towerChoiceState{}, false
+ }
+}
+
+func towerFightBosses(c *player.Player, cmd uint32) ([]configmodel.BaseTowerConfig, uint32, int, bool) {
+ switch cmd {
+ case towerCmdFightTrial:
+ return service.NewTower600Service().Boss(c.Info.CurrentFreshStage, c.Info.CurrentFreshStage+1), c.Info.CurrentFreshStage, towerTaskTrial, true
+ case towerCmdFightBrave:
+ return service.NewTower500Service().Boss(c.Info.CurrentStage, c.Info.CurrentStage+1), c.Info.CurrentStage, towerTaskBrave, true
+ case towerCmdFightDark:
+ return service.NewTower110Service().Boss(c.CurDark), c.CurDark, towerTaskDark, true
+ default:
+ return nil, 0, 0, false
+ }
+}
+
+func canSelectTowerLevel(targetLevel uint, maxLevel uint32) bool {
+ return targetLevel == 1 || targetLevel <= uint(maxLevel)
+}
+
+func firstTowerBossConfig(bossList []configmodel.BaseTowerConfig) (configmodel.BaseTowerConfig, bool) {
+ if len(bossList) == 0 || len(bossList[0].BossIds) == 0 {
+ return configmodel.BaseTowerConfig{}, false
+ }
+ return bossList[0], true
+}
+
+func appendTowerBossPreview(dst *[]uint32, bossList []configmodel.BaseTowerConfig) {
+ bossConfig, ok := firstTowerBossConfig(bossList)
+ if !ok {
+ return
+ }
+
+ bosses := service.NewBossService().Get(bossConfig.BossIds[0])
+ for _, boss := range bosses {
+ *dst = append(*dst, uint32(boss.MonID))
+ }
+}
+
+func appendTowerNextBossPreview(dst *[]uint32, bossList []configmodel.BaseTowerConfig) {
+ if len(bossList) < 2 || len(bossList[1].BossIds) == 0 {
+ return
+ }
+
+ bosses := service.NewBossService().Get(bossList[1].BossIds[0])
+ for _, boss := range bosses {
+ *dst = append(*dst, uint32(boss.MonID))
+ }
+}
+
+func buildTowerMonsterInfo(towerBoss configmodel.BaseTowerConfig) (*model.PlayerInfo, bool) {
+ bosses := service.NewBossService().Get(towerBoss.BossIds[0])
+ if len(bosses) == 0 {
+ return nil, false
+ }
+
+ monsterInfo := &model.PlayerInfo{Nick: towerBoss.Name}
+ for i, boss := range bosses {
+ monster := model.GenPetInfo(int(boss.MonID), 24, int(boss.Nature), 0, int(boss.Lv), nil, 0)
+ if boss.Hp != 0 {
+ monster.Hp = uint32(boss.Hp)
+ monster.MaxHp = uint32(boss.Hp)
+ }
+
+ for statIdx, prop := range boss.Prop {
+ if prop != 0 {
+ monster.Prop[statIdx] = prop
+ }
+ }
+
+ for skillIdx := 0; skillIdx < len(monster.SkillList) && skillIdx < len(boss.SKill); skillIdx++ {
+ if boss.SKill[skillIdx] != 0 {
+ monster.SkillList[skillIdx].ID = boss.SKill[skillIdx]
+ }
+ }
+
+ effects := service.NewEffectService().Args(boss.Effect)
+ for _, effect := range effects {
+ monster.EffectInfo = append(monster.EffectInfo, model.PetEffectInfo{
+ Idx: uint16(effect.SeIdx),
+ EID: gconv.Uint16(effect.Eid),
+ Args: gconv.Ints(effect.Args),
+ })
+ }
+
+ monster.CatchTime = uint32(i)
+ monsterInfo.PetList = append(monsterInfo.PetList, *monster)
+ }
+
+ return monsterInfo, true
+}
+
+func handleTowerFightWin(c *player.Player, cmd uint32, taskID int, currentLevel uint32) {
+ c.TawerCompletedTask(taskID, int(currentLevel))
+
+ switch cmd {
+ case towerCmdFightTrial:
+ c.Info.CurrentFreshStage++
+ if c.Info.CurrentFreshStage >= c.Info.MaxFreshStage {
+ c.Info.MaxFreshStage = c.Info.CurrentFreshStage
+ }
+ case towerCmdFightBrave:
+ c.Info.CurrentStage++
+ if c.Info.CurrentStage >= c.Info.MaxStage {
+ c.Info.MaxStage = c.Info.CurrentStage
+ }
+ }
+}
diff --git a/logic/controller/item_buy.go b/logic/controller/item_buy.go
index 020781d4a..a5e5722dc 100644
--- a/logic/controller/item_buy.go
+++ b/logic/controller/item_buy.go
@@ -1,7 +1,6 @@
package controller
import (
- "blazing/common/data/xmlres"
"blazing/common/socket/errorcode"
"blazing/modules/config/service"
@@ -12,73 +11,34 @@ import (
// 防止封包通过领取来获取道具
// BuyItem 购买单个道具
-// data: 包含购买道具信息的输入数据
-// player: 当前玩家对象
-// 返回: 购买结果和错误码
func (h Controller) BuyItem(data *item.BuyInboundInfo, player *player.Player) (result *item.BuyOutboundInfo, err errorcode.ErrorCode) {
- itemInfo, exists := xmlres.ItemsMAP[int(data.ItemId)]
- if !exists {
- return &item.BuyOutboundInfo{Coins: player.Info.Coins}, 0
+ result = &item.BuyOutboundInfo{Coins: player.Info.Coins}
+
+ bought, err := buySeerdouBackpackItem(player, data.ItemId, data.Count)
+ if err != 0 {
+ return result, err
+ }
+ if !bought {
+ result.Coins = player.Info.Coins
+ return result, 0
}
- // 免费道具直接添加
- if itemInfo.Price == 0 {
- if player.ItemAdd(data.ItemId, data.Count) {
- return &item.BuyOutboundInfo{
- ItemId: data.ItemId,
- Level: 1,
- Count: data.Count,
- Coins: player.Info.Coins,
- }, 0
- }
- return &item.BuyOutboundInfo{Coins: player.Info.Coins}, 0
- }
-
- // 需要付费的道具
- totalCost := int64(data.Count) * int64(itemInfo.Price)
- if !player.GetCoins(totalCost) {
- return &item.BuyOutboundInfo{Coins: player.Info.Coins}, errorcode.ErrorCodes.ErrSunDouInsufficient10016
- }
-
- if player.ItemAdd(data.ItemId, data.Count) {
- player.Info.Coins -= totalCost
- return &item.BuyOutboundInfo{
- ItemId: data.ItemId,
- Level: 1,
- Count: data.Count,
- Coins: player.Info.Coins,
- }, 0
- }
-
- // 购买失败,返还赛尔豆
- player.Info.Coins += totalCost
- return &item.BuyOutboundInfo{Coins: player.Info.Coins}, 0
+ result.ItemId = data.ItemId
+ result.Level = 1
+ result.Count = data.Count
+ result.Coins = player.Info.Coins
+ return result, 0
}
// BuyMultipleItems 批量购买道具
-// data: 包含批量购买道具信息的输入数据
-// player: 当前玩家对象
-// 返回: 批量购买结果和错误码
func (h Controller) BuyMultipleItems(data *item.BuyMultiInboundInfo, player *player.Player) (result *item.BuyMultiOutboundInfo, err errorcode.ErrorCode) {
for _, itemID := range data.ItemIds {
- itemInfo, exists := xmlres.ItemsMAP[int(itemID)]
- if !exists {
- continue
- }
-
- // 免费道具直接添加
- if itemInfo.Price == 0 {
- player.ItemAdd(int64(itemID), 1)
- continue
- }
-
- // 需要付费的道具
- if !player.GetCoins(int64(itemInfo.Price)) {
+ bought, buyErr := buySeerdouBackpackItem(player, int64(itemID), 1)
+ if buyErr == errorcode.ErrorCodes.ErrSunDouInsufficient10016 {
break
}
-
- if player.ItemAdd(int64(itemID), 1) {
- player.Info.Coins -= int64(itemInfo.Price)
+ if buyErr != 0 || !bought {
+ continue
}
}
@@ -88,11 +48,7 @@ func (h Controller) BuyMultipleItems(data *item.BuyMultiInboundInfo, player *pla
}
// BuyGoldItem 使用金豆购买商品
-// data: 包含金豆购买商品信息的输入数据
-// player: 当前玩家对象
-// 返回: 金豆购买结果和错误码
func (h Controller) BuyGoldItem(data *item.C2S_GOLD_BUY_PRODUCT, player *player.Player) (result *item.S2C_GoldBuyProductInfo, err errorcode.ErrorCode) {
- //product, exists := xmlres.GoldProductMap[int(data.ProductID)]
pro := service.NewShopService().Get(data.ProductID)
if pro == nil {
return nil, errorcode.ErrorCodes.ErrTooManyProducts
@@ -105,15 +61,6 @@ func (h Controller) BuyGoldItem(data *item.C2S_GOLD_BUY_PRODUCT, player *player.
}
}
- // if pro.QuotaType != 0 {
- // if data.Count > int64(pro.QuotaLimit) {
- // return nil, errorcode.ErrorCodes.ErrExceedStock
- // }
- // if player.Service.Talk.Cheak(0, int(data.ProductID)) {
- // return nil, errorcode.ErrorCodes.ErrExceedStock
- // }
- // }
-
var usegold uint64
switch data.Type {
@@ -136,12 +83,10 @@ func (h Controller) BuyGoldItem(data *item.C2S_GOLD_BUY_PRODUCT, player *player.
return nil, errorcode.ErrorCodes.ErrSystemError
}
usegold = uint64(data.Count) * uint64(pro.JindouPrice*100)
-
}
switch data.Type {
case 0:
-
if player.ItemAdd(pro.ProductID, data.Count) {
player.Info.Coins -= int64(usegold)
}
@@ -157,9 +102,6 @@ func (h Controller) BuyGoldItem(data *item.C2S_GOLD_BUY_PRODUCT, player *player.
if player.ItemAdd(pro.ProductID, data.Count) {
player.User.UpdateGold(player.Info.UserID, -int64(usegold))
}
-
- //购买成功,执行记录
-
}
player.SendPackCmd(1105, item.GoldOnlineRemainOutboundInfo{
diff --git a/logic/controller/item_use.go b/logic/controller/item_use.go
index 1db177769..ddd443d4f 100644
--- a/logic/controller/item_use.go
+++ b/logic/controller/item_use.go
@@ -22,16 +22,8 @@ const (
// c: 当前玩家对象
// 返回: 道具列表和错误码
func (h Controller) GetUserItemList(data *item.ItemListInboundInfo, c *player.Player) (result *item.ItemListOutboundInfo, err errorcode.ErrorCode) {
- result = &item.ItemListOutboundInfo{}
-
- items := c.Service.Item.Get(data.Param1, data.Param2)
- result.ItemList = make([]model.SingleItemInfo, len(items))
- for i, itemData := range items {
- result.ItemList[i] = model.SingleItemInfo{
- ItemId: itemData.ItemId,
- ItemCnt: uint32(itemData.ItemCnt),
- LeftTime: ItemDefaultLeftTime,
- }
+ result = &item.ItemListOutboundInfo{
+ ItemList: c.Service.Item.GetUserItemList(data.Param1, data.Param2, ItemDefaultLeftTime),
}
return result, 0
}
@@ -48,7 +40,7 @@ func (h Controller) UsePetItemOutOfFight(data *item.C2S_USE_PET_ITEM_OUT_OF_FIGH
itemID := uint32(data.ItemID)
if c.Service.Item.CheakItem(itemID) == 0 {
- return nil, errorcode.ErrorCodes.ErrSystemError
+ return nil, errorcode.ErrorCodes.ErrInsufficientItems
}
itemCfg, ok := xmlres.ItemsMAP[int(itemID)]
@@ -195,7 +187,7 @@ func (h Controller) ResetNature(data *item.C2S_PET_RESET_NATURE, c *player.Playe
}
if c.Service.Item.CheakItem(data.ItemId) <= 0 {
- return nil, errorcode.ErrorCodes.ErrSystemError
+ return nil, errorcode.ErrorCodes.ErrInsufficientItems
}
currentHP := currentPet.Hp
@@ -218,7 +210,7 @@ func (h Controller) UseSpeedupItem(data *item.C2S_USE_SPEEDUP_ITEM, c *player.Pl
// 1. 校验道具是否存在且数量充足
itemCount := c.Service.Item.CheakItem(data.ItemID)
if itemCount <= 0 {
- return nil, errorcode.ErrorCodes.ErrSystemError // 道具不足复用系统错误码(可根据需求改为专属错误码)
+ return nil, errorcode.ErrorCodes.ErrInsufficientItems
}
result = &item.S2C_USE_SPEEDUP_ITEM{}
@@ -275,7 +267,7 @@ func (h Controller) UseEnergyXishou(data *item.C2S_USE_ENERGY_XISHOU, c *player.
// 1. 校验道具是否存在且数量充足
itemCount := c.Service.Item.CheakItem(data.ItemID)
if itemCount <= 0 {
- return nil, errorcode.ErrorCodes.ErrSystemError
+ return nil, errorcode.ErrorCodes.ErrInsufficientItems
}
if c.Info.EnergyTime != 0 {
return nil, errorcode.ErrorCodes.ErrItemInUse
@@ -311,7 +303,7 @@ func (h Controller) UseAutoFightItem(data *item.C2S_USE_AUTO_FIGHT_ITEM, c *play
itemCount := c.Service.Item.CheakItem(data.ItemID)
if itemCount <= 0 {
- return nil, errorcode.ErrorCodes.ErrSystemError
+ return nil, errorcode.ErrorCodes.ErrInsufficientItems
}
if c.Info.AutoFightTime != 0 {
return nil, errorcode.ErrorCodes.ErrItemInUse
diff --git a/logic/controller/login_getserver.go b/logic/controller/login_getserver.go
deleted file mode 100644
index 098c44fce..000000000
--- a/logic/controller/login_getserver.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package controller
-
-// var sg singleflight.Group
-
-// var ServerList []rpc.ServerInfo
-
-// func fetchData() (any, error) {
-// ServerList = rpc.GetServerInfoList() //todo 待修改增加缓存
-// return ServerList, nil
-// }
-
-// GetServerOnline 处理命令: 105
-// func (h Controller) GetServerOnline(data *user.SidInfo, c gnet.Conn) (result *rpc.CommendSvrInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
-// result = rpc.NewInInfo()
-// // tt, _ := cool.CacheManager.Keys(context.Background())
-// //g.Dump(tt)
-// t1 := hex.EncodeToString(data.Sid)
-// userid, ok := playerservice.User.Load(t1)
-// if !ok || userid != data.Head.UserID {
-
-// defer c.Close()
-// return
-// }
-
-// result.IsVip = 1
-// result.ServerList = rpc.GetServerInfoList(service.NewBaseSysUserService().GetPerson(data.Head.UserID).Debug)
-// ser := playerservice.NewUserService(data.Head.UserID)
-// f, b := ser.Friend.Get()
-// for _, v := range f {
-// result.FriendInfo = append(result.FriendInfo, rpc.FriendInfo{Userid: v, TimePoke: 1})
-// }
-// result.BlackInfo = b
-// defer func() {
-
-// // share.ShareManager.DeleteSession(t1)
-
-// kickErr := ser.Info.Kick(data.Head.UserID)
-// if kickErr != nil {
-// fmt.Println("踢人失败", kickErr)
-
-// }
-// logininfo := ser.Info.SetLogin()
-// if logininfo != nil {
-// cool.CacheManager.Set(context.TODO(), fmt.Sprintf("player:%d", data.Head.UserID), logininfo, 0)
-// cool.CacheManager.Set(context.Background(), fmt.Sprintf("session:%d", data.Head.UserID), t1, 0)
-
-// }
-
-// }()
-
-// return
-
-// //return //TODO 这里待实现改成接口调用Ret方法
-// }
diff --git a/logic/controller/nono.go b/logic/controller/nono.go
index 5ca209ef4..9851a8276 100644
--- a/logic/controller/nono.go
+++ b/logic/controller/nono.go
@@ -4,20 +4,25 @@ import (
"blazing/common/socket/errorcode"
"blazing/logic/service/nono"
"blazing/logic/service/player"
+)
- "github.com/jinzhu/copier"
+const (
+ nonoFuncValue = 255
+ nonoDefaultNum = 1
+ nonoDefaultPower = 0
+ nonoDefaultLevel = 12
+ nonoPetCureCost int64 = 50
)
func (h Controller) NonoFollowOrHome(data *nono.NonoFollowOrHomeInInfo, c *player.Player) (result *nono.NonoFollowOutInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
c.Info.NONO.Flag = data.Flag
result = &nono.NonoFollowOutInfo{
-
UserID: data.Head.UserID,
SuperStage: data.Flag,
Flag: data.Flag,
Nick: "",
Color: c.Info.NONO.NonoColor,
- Power: 0,
+ Power: nonoDefaultPower,
}
return
@@ -25,22 +30,26 @@ func (h Controller) NonoFollowOrHome(data *nono.NonoFollowOrHomeInInfo, c *playe
// GetNonoInfo 获取nono信息
func (h *Controller) GetNonoInfo(data *nono.NonoInboundInfo, c *player.Player) (result *nono.NonoOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
+ _ = data
- result = &nono.NonoOutboundInfo{}
- copier.Copy(result, &c.Info.NONO)
- result.UserID = c.Info.UserID
-
- for i := 0; i < 20; i++ {
- result.Func[i] = 255
+ result = &nono.NonoOutboundInfo{
+ UserID: c.Info.UserID,
+ Nick: c.Info.NONO.Nick,
+ SuperNono: nonoDefaultNum,
+ Color: c.Info.NONO.NonoColor,
+ Num: nonoDefaultNum,
+ SuperLevel: nonoDefaultLevel,
+ SuperStage: c.Info.NONO.Flag,
+ Power: nonoDefaultPower,
+ SuperEnergy: nonoDefaultPower,
+ }
+ for i := range result.Func {
+ result.Func[i] = nonoFuncValue
}
- result.Num = 1
- result.SuperNono = 1
- result.SuperLevel = 12
return
}
func (h *Controller) SwitchFlying(data *nono.SwitchFlyingInboundInfo, c *player.Player) (result *nono.SwitchFlyingOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
-
result = &nono.SwitchFlyingOutboundInfo{
UserId: data.Head.UserID,
Type: data.Type,
@@ -51,16 +60,16 @@ func (h *Controller) SwitchFlying(data *nono.SwitchFlyingInboundInfo, c *player.
}
func (h *Controller) PlayerPetCure(data *nono.PetCureInboundInfo, c *player.Player) (result *nono.PetCureOutboundEmpty, err errorcode.ErrorCode) { //这个时候player应该是空的
+ _ = data
if c.GetSpace().Owner.UserID == c.Info.UserID {
return result, errorcode.ErrorCodes.ErrChampionCannotHeal
}
- if !c.GetCoins(50) {
+ if !c.GetCoins(nonoPetCureCost) {
return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
}
- for i := 0; i < len(c.Info.PetList); i++ {
+ for i := range c.Info.PetList {
c.Info.PetList[i].Cure()
-
}
- c.Info.Coins -= 50
+ c.Info.Coins -= nonoPetCureCost
return
}
diff --git a/logic/controller/pet_elo.go b/logic/controller/pet_elo.go
index ef61343ec..7fdc7668b 100644
--- a/logic/controller/pet_elo.go
+++ b/logic/controller/pet_elo.go
@@ -18,34 +18,34 @@ func (h Controller) PetELV(data *C2S_PET_EVOLVTION, c *player.Player) (result *f
return nil, errorcode.ErrorCodes.Err10401
}
- flag := xmlres.PetMAP[int(currentPet.ID)].EvolvFlag
-
- if flag == 0 {
+ petCfg, ok := xmlres.PetMAP[int(currentPet.ID)]
+ if !ok || petCfg.EvolvFlag == 0 {
return nil, errorcode.ErrorCodes.ErrPokemonNotEvolveReady
}
- if xmlres.PetMAP[int(currentPet.ID)].EvolvingLv > int(currentPet.Level) {
+ if petCfg.EvolvingLv > int(currentPet.Level) {
return nil, errorcode.ErrorCodes.ErrPokemonNotEvolveReady
}
- evinfo := xmlres.EVOLVMAP[flag].Branches[data.Index-1]
- if c.Service.Item.CheakItem(uint32(evinfo.EvolvItem)) < int64(evinfo.EvolvItemCount) {
+ evolveCfg, ok := xmlres.EVOLVMAP[petCfg.EvolvFlag]
+ if !ok || data.Index == 0 || int(data.Index) > len(evolveCfg.Branches) {
+ return nil, errorcode.ErrorCodes.ErrPokemonNotEvolveReady
+ }
+
+ branch := evolveCfg.Branches[data.Index-1]
+ if branch.EvolvItem != 0 && c.Service.Item.CheakItem(uint32(branch.EvolvItem)) < int64(branch.EvolvItemCount) {
return nil, errorcode.ErrorCodes.ErrInsufficientItemsMulti
}
- if evinfo.EvolvItem != 0 {
- c.Service.Item.UPDATE(uint32(evinfo.EvolvItem), -evinfo.EvolvItemCount)
+ if branch.EvolvItem != 0 {
+ c.Service.Item.UPDATE(uint32(branch.EvolvItem), -branch.EvolvItemCount)
}
- currentPet.ID = uint32(xmlres.EVOLVMAP[flag].Branches[data.Index-1].MonTo)
+ currentPet.ID = uint32(branch.MonTo)
currentPet.Update(true)
currentPet.CalculatePetPane(100)
- currentPet.Update(true)
updateOutbound := &info.PetUpdateOutboundInfo{}
-
var petUpdateInfo info.UpdatePropInfo
-
copier.Copy(&petUpdateInfo, currentPet)
-
updateOutbound.Data = append(updateOutbound.Data, petUpdateInfo)
c.SendPackCmd(2508, updateOutbound) //准备包由各自发,因为协议不一样
return nil, -1
diff --git a/logic/controller/pet_ev.go b/logic/controller/pet_ev.go
index 4e01ce420..eabf6cdd3 100644
--- a/logic/controller/pet_ev.go
+++ b/logic/controller/pet_ev.go
@@ -5,8 +5,11 @@ import (
"blazing/logic/service/common"
"blazing/logic/service/fight"
"blazing/logic/service/player"
+)
- "github.com/samber/lo"
+const (
+ petEVMaxTotal uint32 = 510
+ petEVMaxSingle uint32 = 255
)
// PetEVDiy 自定义分配宠物努力值(EV)
@@ -18,30 +21,39 @@ func (h Controller) PetEVDiy(data *PetEV, c *player.Player) (result *fight.NullO
if !found {
return nil, errorcode.ErrorCodes.Err10401
}
- // 分配超过510的数据
- if lo.Sum(data.EVs[:]) > 510 {
- return nil, errorcode.ErrorCodes.Err10401
- }
+ var targetTotal uint32
+ var currentTotal uint32
for i, evValue := range data.EVs {
- // 分配超过255的数据
- if evValue > 255 {
+ currentEV := currentPet.Ev[i]
+
+ // 单项分配不能超过上限。
+ if evValue > petEVMaxSingle {
return nil, errorcode.ErrorCodes.Err10401
}
- // 分配比之前点数少的
- if evValue < currentPet.Ev[i] {
+
+ targetTotal += evValue
+ if targetTotal > petEVMaxTotal {
return nil, errorcode.ErrorCodes.Err10401
}
- }
- if lo.Sum(data.EVs[:]) < lo.Sum(currentPet.Ev[:]) {
- return nil, errorcode.ErrorCodes.Err10401
+
+ // 不允许回退已分配的努力值。
+ if evValue < currentEV {
+ return nil, errorcode.ErrorCodes.Err10401
+ }
+ currentTotal += currentEV
}
- usedEV := lo.Sum(data.EVs[:]) - lo.Sum(currentPet.Ev[:])
- // 加的比池子还多
+ usedEV := targetTotal - currentTotal
+ if usedEV == 0 {
+ return result, 0
+ }
+
+ // 新增分配不能超过玩家累计学习力池。
if int64(usedEV) > c.Info.EVPool {
return nil, errorcode.ErrorCodes.Err10401
}
+
currentPet.Ev = data.EVs
currentPet.CalculatePetPane(100)
c.Info.EVPool -= int64(usedEV)
diff --git a/logic/controller/pet_fusion.go b/logic/controller/pet_fusion.go
index e36809f88..7daa47bc5 100644
--- a/logic/controller/pet_fusion.go
+++ b/logic/controller/pet_fusion.go
@@ -10,133 +10,159 @@ import (
"github.com/alpacahq/alpacadecimal"
"github.com/gogf/gf/v2/util/grand"
- "github.com/samber/lo"
+)
+
+const (
+ petFusionCost = 1000
+ petFusionKeepAuxItemID = 300043
+ petFusionFailureItemID = 300044
+ petFusionSoulID = 1000017
)
func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result *pet.PetFusionInfo, err errorcode.ErrorCode) {
- if !c.GetCoins(1000) {
- return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
+ result = &pet.PetFusionInfo{
+ SoulID: petFusionSoulID,
}
- c.Info.Coins -= 1000
- // g.Dump(c.Info.PetList)
- //防止同一只
if data.Mcatchtime == data.Auxcatchtime {
return result, errorcode.ErrorCodes.ErrPokemonNotFusionReady
}
- _, Mcatchpetinfo, ok := c.FindPet(data.Mcatchtime)
+ _, masterPet, ok := c.FindPet(data.Mcatchtime)
if !ok {
return result, errorcode.ErrorCodes.ErrPokemonNotFusionReady2
}
-
- if xmlres.PetMAP[int(Mcatchpetinfo.ID)].FuseMaster == 0 {
+ masterCfg, ok := xmlres.PetMAP[int(masterPet.ID)]
+ if !ok || masterCfg.FuseMaster == 0 {
return result, errorcode.ErrorCodes.ErrPokemonNotFusionReady3
}
- _, Auxpetinfo, ok := c.FindPet(data.Auxcatchtime)
+
+ _, auxPet, ok := c.FindPet(data.Auxcatchtime)
if !ok {
return result, errorcode.ErrorCodes.ErrPokemonNotFusionReady2
}
-
- if xmlres.PetMAP[int(Auxpetinfo.ID)].FuseSub == 0 {
+ auxCfg, ok := xmlres.PetMAP[int(auxPet.ID)]
+ if !ok || auxCfg.FuseSub == 0 {
return result, errorcode.ErrorCodes.ErrPokemonNotFusionReady3
}
- //println(len(c.Info.PetList), data.Mcatchtime, data.Auxcatchtime, Mcatchpetinfo.CatchTime, Auxpetinfo.CatchTime)
- ///性格生成
- var natureId int32 = -1
- if Auxpetinfo.Nature == Mcatchpetinfo.Nature {
- natureId = int32(Auxpetinfo.Nature)
+
+ materialCounts := collectItemCounts(data.Item1[:])
+ if !hasEnoughItems(c, materialCounts) {
+ return result, errorcode.ErrorCodes.ErrInsufficientItems
}
- for _, v := range c.Service.Item.CheakItemM(data.Item1[:]...) {
-
- if v.ItemCnt <= 0 {
- return result, errorcode.ErrorCodes.ErrInsufficientItems
- }
- }
-
- resid := int(service.NewPetFusionService().Data(Mcatchpetinfo.ID, Auxpetinfo.ID, Mcatchpetinfo.Level+Auxpetinfo.Level))
- effect := int(service.NewPetFusionMaterialService().Data(data.Item1))
-
+ materialService := service.NewPetFusionMaterialService()
+ effect := int(materialService.Data(data.Item1))
if effect == 0 {
return result, errorcode.ErrorCodes.ErrSpiritOrbNotExists
}
- //c.Service.Item.UPDATEM(data.Item1[:], -1)
- // utils.CountSliceElements(data.Item1[:])
-
- for _, v := range data.Item1 {
- err := c.Service.Item.UPDATE(v, -1)
- if err != nil {
- return result, errorcode.ErrorCodes.ErrInsufficientItems
- }
+ fusionService := service.NewPetFusionService()
+ resultPetID := int(fusionService.Data(masterPet.ID, auxPet.ID, masterPet.Level+auxPet.Level))
+ if !c.GetCoins(petFusionCost) {
+ return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
}
- result = &pet.PetFusionInfo{
- SoulID: 1000017,
+ consumeItems(c, materialCounts)
+ c.Info.Coins -= petFusionCost
- CostItemFlag: 0,
- }
- if resid == 0 {
-
- _, ok := lo.Find(data.GoldItem1[:], func(item uint32) bool {
- return item == 300044
- })
- if c.Service.Item.CheakItem(300044) > 0 && ok {
- c.Service.Item.UPDATE(300044, -1)
+ if resultPetID == 0 {
+ if useOptionalItem(c, data.GoldItem1[:], petFusionFailureItemID) {
result.CostItemFlag = 1
-
+ } else if auxPet.Level > 5 {
+ auxPet.Downgrade(auxPet.Level - 5)
} else {
- if Auxpetinfo.Level > 5 {
- // Auxpetinfo.Level = Auxpetinfo.Level - 5
- Auxpetinfo.Downgrade(Auxpetinfo.Level - 5)
-
- } else {
- Auxpetinfo.Downgrade(1)
-
- }
-
+ auxPet.Downgrade(1)
}
-
return &pet.PetFusionInfo{}, 0
}
- dv1 := alpacadecimal.NewFromInt(2).Div(alpacadecimal.NewFromInt(3)).Mul(alpacadecimal.NewFromInt(int64(Mcatchpetinfo.Dv)))
- dv2 := alpacadecimal.NewFromInt(1).Div(alpacadecimal.NewFromInt(3)).Mul(alpacadecimal.NewFromInt(int64(Auxpetinfo.Dv)))
+ natureID := int32(-1)
+ if auxPet.Nature == masterPet.Nature {
+ natureID = int32(auxPet.Nature)
+ }
+
+ dv1 := alpacadecimal.NewFromInt(2).Div(alpacadecimal.NewFromInt(3)).Mul(alpacadecimal.NewFromInt(int64(masterPet.Dv)))
+ dv2 := alpacadecimal.NewFromInt(1).Div(alpacadecimal.NewFromInt(3)).Mul(alpacadecimal.NewFromInt(int64(auxPet.Dv)))
dv := dv1.Add(dv2).Add(alpacadecimal.NewFromInt(1)).IntPart()
- r := model.GenPetInfo(resid, int(dv), int(natureId), effect, 1, nil, -1)
- r.OldCatchTime = Mcatchpetinfo.CatchTime
+ newPet := model.GenPetInfo(resultPetID, int(dv), int(natureID), effect, 1, nil, -1)
+ newPet.OldCatchTime = masterPet.CatchTime
- shinycont := 1
- if Mcatchpetinfo.IsShiny() || Auxpetinfo.IsShiny() {
- shinycont = 50
+ shinyChance := 1
+ if masterPet.IsShiny() || auxPet.IsShiny() {
+ shinyChance = 50
}
- if Mcatchpetinfo.IsShiny() && Auxpetinfo.IsShiny() {
- shinycont = 100
+ if masterPet.IsShiny() && auxPet.IsShiny() {
+ shinyChance = 100
+ }
+ if grand.Meet(shinyChance, 100) {
+ newPet.RandomByWeightShiny()
}
- if grand.Meet(shinycont, 100) {
- r.RandomByWeightShiny()
- }
- c.Service.Pet.PetAdd(r, 0)
- println(c.Info.UserID, "进行融合", len(c.Info.PetList), Mcatchpetinfo.ID, Auxpetinfo.ID, r.ID)
+ c.Service.Pet.PetAdd(newPet, 0)
+ println(c.Info.UserID, "进行融合", len(c.Info.PetList), masterPet.ID, auxPet.ID, newPet.ID)
c.PetDel(data.Mcatchtime)
- _, ok2 := lo.Find(data.GoldItem1[:], func(item uint32) bool {
- return item == 300043
- })
-
- if c.Service.Item.CheakItem(300043) > 0 && ok2 {
- c.Service.Item.UPDATE(300043, -1)
+ if useOptionalItem(c, data.GoldItem1[:], petFusionKeepAuxItemID) {
result.CostItemFlag = 1
} else {
-
c.PetDel(data.Auxcatchtime)
-
}
- result.ObtainTime = r.CatchTime
- result.StarterCpTm = r.ID
- //todo材料扣除
+
+ result.ObtainTime = newPet.CatchTime
+ result.StarterCpTm = newPet.ID
return result, 0
}
+
+func collectItemCounts(itemIDs []uint32) map[uint32]int {
+ counts := make(map[uint32]int, len(itemIDs))
+ for _, itemID := range itemIDs {
+ counts[itemID]++
+ }
+ return counts
+}
+
+func hasEnoughItems(c *player.Player, itemCounts map[uint32]int) bool {
+ if len(itemCounts) == 0 {
+ return true
+ }
+
+ itemIDs := make([]uint32, 0, len(itemCounts))
+ for itemID := range itemCounts {
+ itemIDs = append(itemIDs, itemID)
+ }
+
+ hadItems := c.Service.Item.CheakItemM(itemIDs...)
+ ownedCounts := make(map[uint32]int64, len(hadItems))
+ for _, item := range hadItems {
+ ownedCounts[item.ItemId] = item.ItemCnt
+ }
+
+ for itemID, need := range itemCounts {
+ if ownedCounts[itemID] < int64(need) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func consumeItems(c *player.Player, itemCounts map[uint32]int) {
+ for itemID, count := range itemCounts {
+ _ = c.Service.Item.UPDATE(itemID, -count)
+ }
+}
+
+func useOptionalItem(c *player.Player, itemIDs []uint32, target uint32) bool {
+ if c.Service.Item.CheakItem(target) <= 0 {
+ return false
+ }
+ for _, itemID := range itemIDs {
+ if itemID == target {
+ _ = c.Service.Item.UPDATE(target, -1)
+ return true
+ }
+ }
+ return false
+}
diff --git a/logic/controller/pet_繁殖.go b/logic/controller/pet_繁殖.go
index 6006b03a9..48f624af5 100644
--- a/logic/controller/pet_繁殖.go
+++ b/logic/controller/pet_繁殖.go
@@ -10,7 +10,6 @@ import (
"time"
"github.com/gogf/gf/v2/util/grand"
- "github.com/samber/lo"
"github.com/yudeguang/ratelimit"
)
@@ -35,40 +34,27 @@ func (ctl Controller) GetBreedInfo(
// 前端到后端
func (ctl Controller) GetBreedPet(
data *pet.C2S_GET_BREED_PET, playerObj *player.Player) (result *pet.S2C_GET_BREED_PET, err errorcode.ErrorCode) { //这个时候player应该是空的
- _, fPet, found := playerObj.FindPet(data.MaleCatchTime)
- if !found {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- }
- if fPet.Gender != 1 {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- }
- if fPet.Generation > 9 {
+ _, malePet, found := playerObj.FindPet(data.MaleCatchTime)
+ if !found || !canBreedPet(malePet, 1) {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
+
result = &pet.S2C_GET_BREED_PET{}
+ compatibleFemaleIDs := buildBreedPetIDSet(service.NewEggService().GetData(malePet.ID))
+ if len(compatibleFemaleIDs) == 0 {
+ return result, 0
+ }
- MPETS := service.NewEggService().GetData(fPet.ID)
-
- // 这里只是示例,实际应该根据雄性精灵的catchTime查找可繁殖的雌性精灵
- for _, v := range playerObj.Info.PetList {
- if v.Level < 50 {
+ result.FemaleList = make([]uint32, 0, len(playerObj.Info.PetList))
+ for i := range playerObj.Info.PetList {
+ femalePet := &playerObj.Info.PetList[i]
+ if !canBreedPet(femalePet, 2) {
continue
}
- //不是雌性
- if v.Gender != 2 {
+ if _, ok := compatibleFemaleIDs[femalePet.ID]; !ok {
continue
}
- if v.Generation > 9 {
- continue
- }
- _, ok := lo.Find(MPETS, func(item int32) bool {
- return item == int32(v.ID)
- })
- if !ok {
- continue
- }
- // 如果是雌性精灵,且可以繁殖,则添加到列表
- result.FemaleList = append(result.FemaleList, v.CatchTime)
+ result.FemaleList = append(result.FemaleList, femalePet.CatchTime)
}
return result, 0
@@ -78,34 +64,38 @@ func (ctl Controller) GetBreedPet(
// 前端到后端
func (ctl Controller) StartBreed(
data *pet.C2S_START_BREED, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
-
- if !player.GetCoins(1000) {
- return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
- }
- player.Info.Coins -= 1000
- _, MalePet, found := player.FindPet(data.Male)
- if !found {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- }
- _, Female, found := player.FindPet(data.Female)
- if !found {
- return nil, errorcode.ErrorCodes.ErrPokemonNotExists
- }
- // TODO: 实现开始繁殖的具体逻辑
- result = &fight.NullOutboundInfo{}
- r := player.Service.Egg.StartBreed(MalePet, Female)
- if !r {
+ if data.Male == data.Female {
return nil, errorcode.ErrorCodes.ErrCannotPerformAction
}
- MalePet.Generation++
- Female.Generation++
+ _, malePet, found := player.FindPet(data.Male)
+ if !found || !canBreedPet(malePet, 1) {
+ return nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+ _, femalePet, found := player.FindPet(data.Female)
+ if !found || !canBreedPet(femalePet, 2) {
+ return nil, errorcode.ErrorCodes.ErrPokemonNotExists
+ }
+ if !canBreedPair(malePet.ID, femalePet.ID) {
+ return nil, errorcode.ErrorCodes.ErrCannotPerformAction
+ }
+ if !player.GetCoins(breedCost) {
+ return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
+ }
- r1 := grand.Meet(1, 2)
- if r1 {
- MalePet.Gender = 0
+ result = &fight.NullOutboundInfo{}
+ if !player.Service.Egg.StartBreed(malePet, femalePet) {
+ return nil, errorcode.ErrorCodes.ErrCannotPerformAction
+ }
+
+ player.Info.Coins -= breedCost
+ malePet.Generation++
+ femalePet.Generation++
+
+ if grand.Meet(1, 2) {
+ malePet.Gender = 0
} else {
- Female.Gender = 0
+ femalePet.Gender = 0
}
return result, 0
@@ -113,6 +103,23 @@ func (ctl Controller) StartBreed(
// GetEggList 获取精灵蛋数组
// 前端到后端无数据 请求协议
+func canBreedPet(p *model.PetInfo, gender int) bool {
+ return p != nil && p.Level >= breedMinLevel && p.Gender == gender && p.Generation <= breedMaxGeneration
+}
+
+func buildBreedPetIDSet(ids []int32) map[uint32]struct{} {
+ ret := make(map[uint32]struct{}, len(ids))
+ for _, id := range ids {
+ ret[uint32(id)] = struct{}{}
+ }
+ return ret
+}
+
+func canBreedPair(maleID, femaleID uint32) bool {
+ _, ok := buildBreedPetIDSet(service.NewEggService().GetData(maleID))[femaleID]
+ return ok
+}
+
func (ctl Controller) GetEggList(
data *pet.C2S_GET_EGG_LIST, player *player.Player) (result *pet.S2C_GET_EGG_LIST, err errorcode.ErrorCode) { //这个时候player应该是空的
@@ -130,6 +137,12 @@ func (ctl Controller) GetEggList(
}
+const (
+ breedMinLevel = 50
+ breedMaxGeneration = 9
+ breedCost = 1000
+)
+
var limiter *ratelimit.Rule = ratelimit.NewRule()
// 简单规则案例
diff --git a/logic/controller/room_buy.go b/logic/controller/room_buy.go
index 434ab9cd0..a02510221 100644
--- a/logic/controller/room_buy.go
+++ b/logic/controller/room_buy.go
@@ -1,28 +1,26 @@
package controller
import (
- "blazing/common/data/xmlres"
"blazing/common/socket/errorcode"
"blazing/logic/service/player"
"blazing/logic/service/room"
)
// BuyFitment 购买基地家具
-// data: 包含家具ID和购买数量的输入信息
-// c: 当前玩家对象
-// 返回: 购买结果和错误码
func (h Controller) BuyFitment(data *room.C2S_BUY_FITMENT, c *player.Player) (result *room.S2C_BUY_FITMENT, err errorcode.ErrorCode) {
- result = &room.S2C_BUY_FITMENT{}
+ result = &room.S2C_BUY_FITMENT{Coins: c.Info.Coins}
- itemConfig := xmlres.ItemsMAP[int(data.ID)]
- totalCost := int64(itemConfig.Price) * int64(data.Count)
-
- if !c.GetCoins((totalCost)) {
- return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
+ bought, err := buySeerdouBackpackItem(c, int64(data.ID), int64(data.Count))
+ if err != 0 {
+ return nil, err
}
- c.Service.Item.UPDATE(data.ID, int(data.Count))
- c.Info.Coins -= int64(totalCost)
+ if !bought {
+ result.Coins = c.Info.Coins
+ return result, 0
+ }
+
result.ID = data.ID
+ result.Count = data.Count
result.Coins = c.Info.Coins
- return
+ return result, 0
}
diff --git a/logic/controller/user_cdk.go b/logic/controller/user_cdk.go
index 8684beece..15cc8606a 100644
--- a/logic/controller/user_cdk.go
+++ b/logic/controller/user_cdk.go
@@ -12,7 +12,12 @@ import (
func (h Controller) CDK(data *user.C2S_GET_GIFT_COMPLETE, player *player.Player) (result *user.S2C_GET_GIFT_COMPLETE, err errorcode.ErrorCode) {
result = &user.S2C_GET_GIFT_COMPLETE{}
- r := service.NewCdkService().Get(data.PassText)
+ cdkService := service.NewCdkService()
+ rewardPetService := service.NewPetRewardService()
+ itemRewardService := service.NewItemService()
+ now := time.Now()
+
+ r := cdkService.Get(data.PassText)
if r == nil {
return nil, errorcode.ErrorCodes.ErrMolecularCodeNotExists
}
@@ -20,38 +25,36 @@ func (h Controller) CDK(data *user.C2S_GET_GIFT_COMPLETE, player *player.Player)
return nil, errorcode.ErrorCodes.ErrMolecularCodeFrozen
}
- if r.ValidEndTime.Compare(time.Now()) == -1 {
+ if r.ValidEndTime.Compare(now) == -1 {
return nil, errorcode.ErrorCodes.ErrMolecularCodeExpired
}
if !player.Service.Cdk.CanGet(uint32(r.ID)) {
return
}
- if !service.NewCdkService().Set(data.PassText) {
+ if !cdkService.Set(data.PassText) {
return nil, errorcode.ErrorCodes.ErrMolecularCodeGiftsGone
}
result.Flag = 1
- for _, v := range r.ElfRewardIds {
- pet := service.NewPetRewardService().Get(v)
- if pet != nil {
- peti := model.GenPetInfo(int(pet.MonID), int(pet.DV), int(pet.Nature), int(pet.Effect), int(pet.Lv), nil, 0)
- player.Service.Pet.PetAdd(peti, 0)
- result.PetGift = append(result.PetGift, user.PetGiftInfo{PetID: peti.ID, CacthTime: peti.CatchTime})
+ for _, rewardID := range r.ElfRewardIds {
+ pet := rewardPetService.Get(rewardID)
+ if pet == nil {
+ continue
}
+ petInfo := model.GenPetInfo(int(pet.MonID), int(pet.DV), int(pet.Nature), int(pet.Effect), int(pet.Lv), nil, 0)
+ player.Service.Pet.PetAdd(petInfo, 0)
+ result.PetGift = append(result.PetGift, user.PetGiftInfo{PetID: petInfo.ID, CacthTime: petInfo.CatchTime})
}
- for _, itemID := range r.ItemRewardIds {
- iteminfo := service.NewItemService().GetItemCount(itemID)
- player.ItemAdd(iteminfo.ItemId, iteminfo.ItemCnt)
-
- result.GiftList = append(result.GiftList, user.GiftInfo{GiftID: iteminfo.ItemId, Count: iteminfo.ItemCnt})
-
+ for _, rewardID := range r.ItemRewardIds {
+ itemInfo := itemRewardService.GetItemCount(rewardID)
+ player.ItemAdd(itemInfo.ItemId, itemInfo.ItemCnt)
+ result.GiftList = append(result.GiftList, user.GiftInfo{GiftID: itemInfo.ItemId, Count: itemInfo.ItemCnt})
}
if r.TitleRewardIds != 0 {
player.Service.Title.Give(r.TitleRewardIds)
result.Tile = r.TitleRewardIds
-
}
player.Service.Cdk.Log(uint32(r.ID))
diff --git a/logic/service/common/pack.go b/logic/service/common/pack.go
index e356b5c56..520f8aee9 100644
--- a/logic/service/common/pack.go
+++ b/logic/service/common/pack.go
@@ -6,7 +6,6 @@ import (
"fmt"
"github.com/lunixbochs/struc"
- "github.com/valyala/bytebufferpool"
)
// TomeeHeader 结构体字段定义
@@ -17,9 +16,9 @@ type TomeeHeader struct {
UserID uint32 `json:"userId"`
//Error uint32 `json:"error" struc:"skip"`
- Result uint32 `json:"result"`
- Data *bytebufferpool.ByteBuffer `json:"data" struc:"skip"` //组包忽略此字段// struc:"skip"
- Res []byte `struc:"skip"`
+ Result uint32 `json:"result"`
+ Data []byte `json:"data" struc:"skip"` //组包忽略此字段// struc:"skip"
+ Res []byte `struc:"skip"`
//Return []byte `struc:"skip"` //返回记录
}
diff --git a/logic/service/fight/effect/795_799.go b/logic/service/fight/effect/795_799.go
index f743e0b67..36fe261f0 100644
--- a/logic/service/fight/effect/795_799.go
+++ b/logic/service/fight/effect/795_799.go
@@ -68,22 +68,20 @@ type Effect796Sub struct {
RoundEffectArg0Base
}
-func (e *Effect796Sub) OnSkill() bool {
- if len(e.Args()) < 2 || e.Args()[1].Cmp(alpacadecimal.Zero) <= 0 {
- return true
+func (e *Effect796Sub) TurnEnd() {
+ if e.Ctx().Our.CurrentPet.Info.Hp > 0 && e.Ctx().Opp.CurrentPet.Info.Hp > 0 &&
+ len(e.Args()) >= 2 && e.Args()[1].Cmp(alpacadecimal.Zero) > 0 {
+ damage := e.Ctx().Opp.CurrentPet.GetHP().Div(e.Args()[1])
+ if damage.Cmp(alpacadecimal.Zero) > 0 {
+ e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
+ Type: info.DamageType.Percent,
+ Damage: damage,
+ })
+ e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, damage)
+ }
}
- damage := e.Ctx().Opp.CurrentPet.GetHP().Div(e.Args()[1])
- if damage.Cmp(alpacadecimal.Zero) <= 0 {
- return true
- }
-
- e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{
- Type: info.DamageType.Percent,
- Damage: damage,
- })
- e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, damage)
- return true
+ e.EffectNode.TurnEnd()
}
// Effect 797: 消除对手回合类效果,消除成功{0}回合内对手无法通过自身技能恢复体力
@@ -166,7 +164,7 @@ type Effect799 struct {
node.EffectNode
}
-func (e *Effect799) OnSkill() bool {
+func (e *Effect799) Skill_Use() bool {
if len(e.Args()) < 2 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 {
return true
}
diff --git a/logic/service/fight/effect/810_814.go b/logic/service/fight/effect/810_814.go
index 8d23d4faf..0ec09d899 100644
--- a/logic/service/fight/effect/810_814.go
+++ b/logic/service/fight/effect/810_814.go
@@ -1,7 +1,6 @@
package effect
import (
- "blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/fight/node"
diff --git a/logic/service/player/pack.go b/logic/service/player/pack.go
index 6f93ffd4e..63c8dfe19 100644
--- a/logic/service/player/pack.go
+++ b/logic/service/player/pack.go
@@ -20,7 +20,6 @@ import (
"github.com/gogf/gf/v2/os/glog"
"github.com/lunixbochs/struc"
"github.com/panjf2000/gnet/v2"
- "github.com/valyala/bytebufferpool"
)
// getUnderlyingValue 递归解析reflect.Value,解包指针、interface{}到底层具体类型
@@ -48,44 +47,73 @@ func getUnderlyingValue(val reflect.Value) (reflect.Value, error) {
}
}
-// XORDecryptU 优化后的异或解密:减少内存分配,支持复用缓冲区
-// XORDecryptU 基于bytebufferpool优化的异或解密函数
-// 保留原有接口,无侵入式优化,高频调用下大幅减少内存分配和GC
+// XORDecryptU 原地执行异或解密,避免额外分配和拷贝。
func XORDecryptU(encryptedData []byte, key uint32) []byte {
if len(encryptedData) == 0 {
return []byte{}
}
- // 1. 栈上分配密钥字节数组(无GC压力,保留原优化)
var keyBytes [4]byte
binary.BigEndian.PutUint32(keyBytes[:], key)
keyLen := len(keyBytes)
- // 2. 从bytebufferpool获取池化缓冲区(替代make分配)
- buf := bytebufferpool.Get()
- defer bytebufferpool.Put(buf) // 函数结束自动归还缓冲区到池
-
- // 3. 调整缓冲区长度,匹配待解密数据(避免扩容)
- buf.B = buf.B[:0] // 清空原有数据,保留底层数组
- if cap(buf.B) < len(encryptedData) {
- // 若缓冲区容量不足,直接扩容(bytebufferpool会自动管理)
- buf.B = make([]byte, len(encryptedData))
- } else {
- // 容量足够,直接调整长度
- buf.B = buf.B[:len(encryptedData)]
- }
-
- // 4. 核心异或解密逻辑(直接操作buf.B,无额外内存分配)
- decrypted := buf.B
for i, b := range encryptedData {
- decrypted[i] = b ^ keyBytes[i%keyLen]
+ encryptedData[i] = b ^ keyBytes[i%keyLen]
}
- // 5. 拷贝结果(关键:避免返回池化缓冲区,防止被后续调用覆盖)
- result := make([]byte, len(decrypted))
- copy(result, decrypted)
+ return encryptedData
+}
- return result
+var packetDataPoolSizes = [...]int{64, 128, 256, 512, 1024, 2048, 4096}
+
+var packetDataPools = func() []sync.Pool {
+ pools := make([]sync.Pool, len(packetDataPoolSizes))
+ for i, size := range packetDataPoolSizes {
+ size := size
+ pools[i].New = func() any {
+ return make([]byte, size)
+ }
+ }
+ return pools
+}()
+
+func getPacketData(size int) []byte {
+ if size <= 0 {
+ return nil
+ }
+ for i, poolSize := range packetDataPoolSizes {
+ if size <= poolSize {
+ return packetDataPools[i].Get().([]byte)[:size]
+ }
+ }
+ return make([]byte, size)
+}
+
+func putPacketData(buf []byte) {
+ if buf == nil {
+ return
+ }
+ for i, poolSize := range packetDataPoolSizes {
+ if cap(buf) == poolSize {
+ packetDataPools[i].Put(buf[:poolSize])
+ return
+ }
+ }
+}
+
+func (h *ClientData) PushEvent(v []byte, submit func(task func()) error) {
+ var header common.TomeeHeader
+ header.Len = binary.BigEndian.Uint32(v[0:4])
+ header.CMD = binary.BigEndian.Uint32(v[5:9])
+ header.UserID = binary.BigEndian.Uint32(v[9:13])
+ if dataLen := len(v) - 17; dataLen > 0 {
+ header.Data = getPacketData(dataLen)
+ copy(header.Data, v[17:])
+ }
+
+ _ = submit(func() {
+ h.LF.Producer().Write(header)
+ })
}
// 重写
@@ -119,13 +147,13 @@ func (h *ClientData) OnEvent(data common.TomeeHeader) {
return
}
if data.Data != nil {
- defer bytebufferpool.Put(data.Data)
- data.Res = XORDecryptU(data.Data.Bytes(), h.Player.Hash)
+ defer putPacketData(data.Data)
+ data.Res = XORDecryptU(data.Data, h.Player.Hash)
}
- } else {
- defer bytebufferpool.Put(data.Data)
- data.Res = data.Data.Bytes()
+ } else if data.Data != nil {
+ defer putPacketData(data.Data)
+ data.Res = data.Data
}
if cool.Config.ServerInfo.IsDebug != 0 {
fmt.Println("接收数据", data.UserID, data.CMD)
@@ -172,7 +200,7 @@ func (h *ClientData) OnEvent(data common.TomeeHeader) {
// fmt.Println(data.CMD, "接收 变量的地址 ", &t.Info, t.Info.UserID)
- params = append(params, ptrValue1, reflect.ValueOf(h.Conn.Context().(*ClientData).Player))
+ params = append(params, ptrValue1, reflect.ValueOf(h.Player))
} else {
params = append(params, ptrValue1, reflect.ValueOf(h.Conn))
@@ -209,14 +237,10 @@ func (h *ClientData) OnEvent(data common.TomeeHeader) {
type ClientData struct {
IsCrossDomain sync.Once //是否跨域过
Player *Player //客户实体
- //Mu sync.RWMutex
- ERROR_CONNUT int
- Wsmsg *WsCodec
- Conn gnet.Conn
- LF *lockfree.Lockfree[common.TomeeHeader]
- //SaveL sync.Once //保存锁
- // MsgChan chan common.TomeeHeader
- //SaveDone chan struct{}
+ ERROR_CONNUT int
+ Wsmsg *WsCodec
+ Conn gnet.Conn
+ LF *lockfree.Lockfree[common.TomeeHeader]
}
func (p *ClientData) GetPlayer(userid uint32) *Player { //TODO 这里待优化,可能存在内存泄漏问题
@@ -232,42 +256,18 @@ func (p *ClientData) GetPlayer(userid uint32) *Player { //TODO 这里待优化,
// return nil
}
func NewClientData(c gnet.Conn) *ClientData {
- // 创建事件处理器
- // 创建消费端串行处理的Lockfree
-
cd := &ClientData{
-
Conn: c,
Wsmsg: &WsCodec{},
- // MsgChan: make(chan common.TomeeHeader, 100),
}
- //cd.LF = make(chan common.TomeeHeader, 8)
- // cd.LF = gqueue.NewTQueue[common.TomeeHeader](1)
- // go func() {
- // for {
- // select {
- // case queueItem := <-cd.LF.C:
- // cd.OnEvent(queueItem)
-
- // }
- // }
-
- // }()
cd.LF = lockfree.NewLockfree(
8,
cd,
lockfree.NewConditionBlockStrategy(),
)
- // // // 启动Lockfree
if err := cd.LF.Start(); err != nil {
panic(err)
}
- // 启动每个连接的独立消费协程
- // go cd.consumeMsg()
- // // // 启动Lockfree
- // // if err := cd.LF.Start(); err != nil {
- // // panic(err)
- // // }
return cd
}
@@ -305,16 +305,11 @@ func XORDecrypt(encryptedData []byte, keyStr string) []byte {
}
func (p *ClientData) SendPack(b []byte) error {
- cli, ok := p.Conn.Context().(*ClientData)
- if !ok {
- return fmt.Errorf("链接错误,取消发包")
-
- }
- if cli.Wsmsg == nil {
+ if p.Wsmsg == nil {
return fmt.Errorf("ws空")
}
- if cli.Wsmsg.Upgraded {
+ if p.Wsmsg.Upgraded {
// This is the echo server
wsutil.WriteServerMessage(p.Conn, ws.OpBinary, b)
diff --git a/modules/player/service/cdk_log.go b/modules/player/service/cdk_log.go
index 4e1ab9190..2fdc72ed8 100644
--- a/modules/player/service/cdk_log.go
+++ b/modules/player/service/cdk_log.go
@@ -3,19 +3,33 @@ package service
import (
"blazing/cool"
"blazing/modules/player/model"
+ "sync"
"github.com/gogf/gf/v2/frame/g"
)
type CdkService struct {
BaseService
+
+ mu sync.RWMutex
+ claimedCode map[uint32]struct{}
+ cacheLoaded bool
}
func (s *CdkService) CanGet(id uint32) bool {
- m1, _ := s.dbm(s.Model).Where("code_id", id).Exist()
+ if s.isClaimed(id) {
+ return false
+ }
- return !m1
+ if err := s.loadClaimedCodes(); err != nil {
+ exists, _ := s.dbm(s.Model).Where("code_id", id).Exist()
+ if exists {
+ s.markClaimed(id)
+ }
+ return !exists
+ }
+ return !s.isClaimed(id)
}
func (s *CdkService) Log(id uint32) {
@@ -26,17 +40,59 @@ func (s *CdkService) Log(id uint32) {
"is_vip": cool.Config.ServerInfo.IsVip,
}
- m.Data(data).Insert()
+ if _, err := m.Data(data).Insert(); err == nil {
+ s.markClaimed(id)
+ }
+}
+func (s *CdkService) loadClaimedCodes() error {
+ s.mu.RLock()
+ if s.cacheLoaded {
+ s.mu.RUnlock()
+ return nil
+ }
+ s.mu.RUnlock()
+
+ var logs []model.CdkLog
+ if err := s.dbm(s.Model).Fields("code_id").Scan(&logs); err != nil {
+ return err
+ }
+
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.cacheLoaded {
+ return nil
+ }
+
+ s.claimedCode = make(map[uint32]struct{}, len(logs))
+ for _, log := range logs {
+ s.claimedCode[log.CodeID] = struct{}{}
+ }
+ s.cacheLoaded = true
+ return nil
+}
+
+func (s *CdkService) isClaimed(id uint32) bool {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ _, ok := s.claimedCode[id]
+ return ok
+}
+
+func (s *CdkService) markClaimed(id uint32) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.claimedCode == nil {
+ s.claimedCode = make(map[uint32]struct{})
+ }
+ s.claimedCode[id] = struct{}{}
}
func NewCdkService(id uint32) *CdkService {
return &CdkService{
-
BaseService: BaseService{userid: id,
-
Service: &cool.Service{Model: model.NewCdkLog()},
},
+ claimedCode: make(map[uint32]struct{}),
}
-
}
diff --git a/modules/player/service/item.go b/modules/player/service/item.go
index a00795a3b..c0dd4735c 100644
--- a/modules/player/service/item.go
+++ b/modules/player/service/item.go
@@ -26,6 +26,21 @@ func (s *ItemService) Get(min, max uint32) []model.Item {
return ttt
}
+
+func (s *ItemService) GetUserItemList(min, max, leftTime uint32) []model.SingleItemInfo {
+ var items []model.SingleItemInfo
+ s.dbm(s.Model).
+ Fields("item_id,item_cnt").
+ WhereBetween("item_id", min, max).
+ Where("item_cnt >", 0).
+ Scan(&items)
+
+ for i := range items {
+ items[i].LeftTime = leftTime
+ }
+
+ return items
+}
func (s *ItemService) UPDATE(id uint32, count int) error {
if cool.Config.ServerInfo.IsVip != 0 && count < 0 {