feat(player): 重构任务状态管理逻辑

将任务状态相关的 GetTask 和 SetTask 方法从 player 包迁移至 model.PlayerInfo 结构体中,
统一通过 c.Info 调
This commit is contained in:
2025-12-08 19:16:37 +08:00
parent 8983222dcb
commit ec0552b59a
13 changed files with 192 additions and 138 deletions

View File

@@ -121,9 +121,8 @@ func (h Controller) PlayerFightBoss(data *fight.ChallengeBossInboundInfo, c *pla
fight.NewFight(c, ai, func(foi *info.FightOverInfo) {
if taskid != 0 {
if foi.Reason == 0 && foi.WinnerId == c.Info.UserID {
if c.GetTask(taskid) == player.Unaccepted {
c.SetTask(taskid, player.Completed) //设置完成任务
c.Info.TaskList[taskid-1] = 3
if c.Info.GetTask(taskid) == model.Unaccepted {
c.Info.SetTask(taskid, model.Completed) //设置完成任务
moinfo.PetList[0].Downgrade(1)
PetID := moinfo.PetList[0].ID

View File

@@ -58,6 +58,7 @@ func (h *Controller) Login(data *user.MAIN_LOGIN_IN, c gnet.Conn) (result *user.
t.Service = blservice.NewUserService(data.Head.UserID)
t.User = service.NewBaseSysUserService()
t.Info = t.Service.Info.Personself()
if t.Info == nil {
defer c.Close()
return

View File

@@ -19,8 +19,8 @@ func (h Controller) AcceptTask(data *task.AcceptTaskInboundInfo, c *player.Playe
// //isdaliy = true
// }
if c.GetTask(int(data.TaskId)) == player.Unaccepted {
c.SetTask(int(data.TaskId), player.Accepted)
if c.Info.GetTask(int(data.TaskId)) == model.Unaccepted {
c.Info.SetTask(int(data.TaskId), model.Accepted)
}
c.Service.Task.Exec(uint32(data.TaskId), func(t *model.TaskEX) bool {
@@ -55,12 +55,12 @@ func (h Controller) AddTaskBuf(data *task.AddTaskBufInboundInfo, c *player.Playe
* 完成任务
*/
func (h Controller) Complete_Task(data *task.CompleteTaskInboundInfo, c *player.Player) (result *task.CompleteTaskOutboundInfo, err errorcode.ErrorCode) {
if c.GetTask(int(data.TaskId)) != player.Accepted { //如果任务没有接受或者已经完成Complete_Task
if c.Info.GetTask(int(data.TaskId)) != model.Accepted { //如果任务没有接受或者已经完成Complete_Task
return result, 0
}
c.SetTask(int(data.TaskId), player.Completed)
c.Info.SetTask(int(data.TaskId), model.Completed)
result = &task.CompleteTaskOutboundInfo{
TaskId: data.TaskId,
@@ -112,8 +112,8 @@ func (h Controller) Get_Task_Buf(data *task.GetTaskBufInboundInfo, c *player.Pla
*/
func (h Controller) Delete_Task(data *task.DeleteTaskInboundInfo, c *player.Player) (result *task.DeleteTaskOutboundInfo, err errorcode.ErrorCode) {
if c.GetTask(int(data.TaskId)) == player.Accepted {
c.SetTask(int(data.TaskId), player.Unaccepted)
if c.Info.GetTask(int(data.TaskId)) == model.Accepted {
c.Info.SetTask(int(data.TaskId), model.Unaccepted)
return &task.DeleteTaskOutboundInfo{TaskId: data.TaskId}, 0
}

View File

@@ -8,7 +8,6 @@ import (
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"blazing/logic/service/space"
"math/rand"
"strings"
"sync/atomic"
@@ -17,7 +16,6 @@ import (
blservice "blazing/modules/blazing/service"
"context"
"time"
"github.com/antlabs/timer"
"github.com/gogf/gf/v2/frame/g"
@@ -187,8 +185,7 @@ func (p *Player) SendPack(b []byte) error {
// 2. 从 string 类型 slice 随机选一个元素
func RandomStringFromSlice(s []string) string {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
randomIdx := r.Intn(len(s))
randomIdx := grand.Intn(len(s))
return s[randomIdx]
}
@@ -321,7 +318,8 @@ func (p *Player) ItemAdd(t ...model.ItemInfo) (result []model.ItemInfo) {
}
if p.Service.Item.CheakItem(v.ItemId)+v.ItemCnt > uint32(itemx.Max) {
cool.Loger.Error(context.TODO(), "物品超过拥有最大限制", v.ItemId)
println(p.Info.UserID, "物品超过拥有最大限制", v.ItemId)
t1 := common.NewTomeeHeader(2601, p.Info.UserID)
t1.Result = uint32(errorcode.ErrorCodes.ErrTooManyOfItem)

View File

@@ -3,6 +3,7 @@ package player
import (
"blazing/common/data/share"
"blazing/cool"
"blazing/modules/blazing/model"
"blazing/logic/service/fight/info"
"blazing/logic/service/space"
@@ -71,7 +72,7 @@ func (lw *Player) CompleteLogin() {
}
if lw.IsNewPlayer() { //重置新手地图,放到机械仓
lw.SetTask(4, Completed) //设置新手任务默认完成
lw.Info.SetTask(4, model.Completed) //设置新手任务默认完成
lw.Info.MapID = 8
if len(lw.Info.PetList) == 0 {
rr := lw.Service.Pet.PetInfo(0)
@@ -88,7 +89,7 @@ func (lw *Player) CompleteLogin() {
func (lw *Player) IsNewPlayer() bool {
// 遍历84到87的索引
for i := 84; i <= 87; i++ {
if lw.GetTask(i) != Completed {
if lw.Info.GetTask(i) != model.Completed {
return true // 只要有一个不等于3就返回true
}
}

View File

@@ -1,45 +0,0 @@
package player
import "fmt"
// TaskStatus 任务状态(与 AS3 对应)
type TaskStatus uint8
const (
Unaccepted TaskStatus = 0 // 未接受AS3 中 0 或 2 映射至此)
Accepted TaskStatus = 1 // 已接受
Completed TaskStatus = 3 // 已完成
Reserved TaskStatus = 2 // 预留AS3 中映射为未接受)
)
// SetTask 设置第 i 个任务的状态0 ≤ i < 2000
func (m *Player) SetTask(i int, status TaskStatus) error {
i-- //下标减1
if i < 0 || i >= 4000 {
return fmt.Errorf("index out of range: %d (must be 0-1999)", i)
}
byteIdx := i / 4
bitOffset := (i % 4) * 2
// 清除原有 2 位
m.Info.TaskList[byteIdx] &^= 0x3 << bitOffset
// 设置新状态(确保只取低 2 位)
m.Info.TaskList[byteIdx] |= byte(status&0x3) << bitOffset
return nil
}
// GetTask 获取第 i 个任务的状态
func (m *Player) GetTask(i int) TaskStatus {
i-- //下标减1
// if i < 0 || i >= 2000 {
// return 0, fmt.Errorf("index out of range: %d", i)
// }
byteIdx := i / 4
bitOffset := (i % 4) * 2
return TaskStatus((m.Info.TaskList[byteIdx] >> bitOffset) & 0x3)
}

View File

@@ -1,10 +1,5 @@
package space
import (
"golang.org/x/sync/singleflight"
)
var requestGroup singleflight.Group // SingleFlight 实例
// MapHotInfo 表示地图热度信息
type MapHotInfo struct {
MapID uint32 `json:"mapId"` // 地图ID

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/jinzhu/copier"
"github.com/samber/lo"
)
@@ -397,7 +398,7 @@ func GenPetInfo(
dv, natureId, abilityTypeEnum, shinyid, level int,
) *PetInfo {
// 创建随机源
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
//rng := rand.New(rand.NewSource(time.Now().UnixNano()))
// 初始化精灵
p := &PetInfo{
@@ -414,14 +415,14 @@ func GenPetInfo(
// ---- 性格 ----
if natureId == -1 {
p.Nature = uint32(rng.Intn(25))
p.Nature = uint32(grand.Intn(25))
} else {
p.Nature = uint32(natureId)
}
// ---- 个体值DV----
if dv == -1 {
p.Dv = uint32(CalculateIndividualValue(rng))
p.Dv = uint32(CalculateIndividualValue())
} else {
if dv < 0 {
dv = 0
@@ -447,7 +448,7 @@ func GenPetInfo(
}
case abilityTypeEnum == -1:
// 随机特性
randomIndex := rng.Intn(len(xmlres.PlayerEffectMAP))
randomIndex := grand.Intn(len(xmlres.PlayerEffectMAP))
var i int
for _, v := range xmlres.PlayerEffectMAP {
@@ -512,8 +513,8 @@ func CalculateIndividual(a int) int {
// CalculateIndividualValue 计算个体值0-31
// 接收外部随机数生成器,便于控制随机性和复用
func CalculateIndividualValue(random *rand.Rand) int {
func CalculateIndividualValue() int {
// 生成0-40000的随机数作为个体值计算的输入
a := random.Intn(40001)
a := grand.Intn(40001)
return CalculateIndividual(a)
}

View File

@@ -2,6 +2,7 @@ package model
import (
"blazing/cool"
"fmt"
"math"
"github.com/creasty/defaults"
@@ -67,6 +68,48 @@ func NewPlayerInfo() PlayerInfo {
return l
}
// TaskStatus 任务状态(与 AS3 对应)
type TaskStatus uint8
const (
Unaccepted TaskStatus = 0 // 未接受AS3 中 0 或 2 映射至此)
Accepted TaskStatus = 1 // 已接受
Completed TaskStatus = 3 // 已完成
Reserved TaskStatus = 2 // 预留AS3 中映射为未接受)
)
// SetTask 设置第 i 个任务的状态0 ≤ i < 2000
func (m *PlayerInfo) SetTask(i int, status TaskStatus) error {
i-- //下标减1
if i < 0 || i >= 4000 {
return fmt.Errorf("index out of range: %d (must be 0-1999)", i)
}
byteIdx := i / 4
bitOffset := (i % 4) * 2
// 清除原有 2 位
m.TaskList[byteIdx] &^= 0x3 << bitOffset
// 设置新状态(确保只取低 2 位)
m.TaskList[byteIdx] |= byte(status&0x3) << bitOffset
return nil
}
// GetTask 获取第 i 个任务的状态
func (m *PlayerInfo) GetTask(i int) TaskStatus {
i-- //下标减1
// if i < 0 || i >= 2000 {
// return 0, fmt.Errorf("index out of range: %d", i)
// }
byteIdx := i / 4
bitOffset := (i % 4) * 2
return TaskStatus((m.TaskList[byteIdx] >> bitOffset) & 0x3)
}
type PlayerInfo struct {
ExpPool uint32 `struc:"skip" json:"exp_pool"` // 累计经验池
//LastResetTime time.Time `struc:"skip" json:"last_reset_time"` // 重置时间,比如电池和每日任务

View File

@@ -85,7 +85,8 @@ func (s *InfoService) Personself() *model.PlayerInfo {
tttL, ok := xmlres.TaskMap[i]
if ok {
if tttL.Type == 1 { //日常任务
tt.Data.TaskList[i-1] = 0 //重置每日任务
tt.Data.SetTask(i, model.Unaccepted)
}
}

View File

@@ -6,8 +6,11 @@ import (
"blazing/modules/blazing/model"
dictmodel "blazing/modules/dict/model"
"blazing/modules/dict/service"
"context"
"strings"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
)
@@ -27,14 +30,24 @@ func NewPetFusionMaterialService() *PetFusionMaterialService {
}
}
var (
cachePetFusionMaterialService = gcache.New()
)
func (s *PetFusionMaterialService) ModifyBefore(ctx context.Context, method string, param g.MapStrAny) (err error) {
cachePetFusionMaterialService.Clear(context.Background())
return nil
}
// 获取融合材料的特性,返回两个值,一个是指定的特性,另一个是如果配方没找到的情况下,默认的配置
func (s *PetFusionMaterialService) Data(Material1 [4]uint32) uint32 {
fusion, _ := service.NewDictInfoService().DataOne("fusion")
cacheKey := strings.Join(gconv.Strings(Material1[:]), ":")
println(cacheKey, "获取融合id")
fusions := utils.ToMap(fusion, func(t dictmodel.DictInfo) uint32 {
fusions := utils.ToMap(service.NewDictInfoService().DataOne("fusion"), func(t dictmodel.DictInfo) uint32 {
return gconv.Uint32(t.Remark)
return gconv.Uint32(t.Remark) //物品id转id
})
for _, v := range Material1 {
_, ok := fusions[v]
@@ -44,42 +57,46 @@ func (s *PetFusionMaterialService) Data(Material1 [4]uint32) uint32 {
}
}
m := cool.DBM(s.Model)
ret, err := cachePetFusionMaterialService.GetOrSetFuncLock(context.Background(), cacheKey, func(context.Context) (interface{}, error) {
var effect model.PetFusionMaterial //一个特性应该是唯一的,但是我们要获取默认随机特性
condition := g.Map{
"material1": fusions[Material1[0]].ID,
"material2": fusions[Material1[1]].ID,
"material3": fusions[Material1[2]].ID,
"material4": fusions[Material1[3]].ID,
"is_enable": 1,
}
m.Where(condition).Scan(&effect)
//这时候有可能效果是空的,那么这时候就再次查询默认的特性,保证每次必会生成一个数据库有的特性
//也许这个时候的特性配方就是随机从数据库中查找一个特性
m := cool.DBM(s.Model)
effect2, _ := service.NewDictInfoService().DataOne("effect")
effect2s := utils.ToMap(effect2, func(t dictmodel.DictInfo) uint32 {
var effect *model.PetFusionMaterial //一个特性应该是唯一的,但是我们要获取默认随机特性
condition := g.Map{
"material1": fusions[Material1[0]].ID,
"material2": fusions[Material1[1]].ID,
"material3": fusions[Material1[2]].ID,
"material4": fusions[Material1[3]].ID,
"is_enable": 1,
}
m.Where(condition).Scan(&effect)
//这时候有可能效果是空的,那么这时候就再次查询默认的特性,保证每次必会生成一个数据库有的特性
//也许这个时候的特性配方就是随机从数据库中查找一个特性
return effect, nil
}, 0)
effect2s := utils.ToMap(service.NewDictInfoService().DataOne("effect"), func(t dictmodel.DictInfo) uint32 {
return gconv.Uint32(t.ID)
})
effect := ret.Interface().(*model.PetFusionMaterial)
if effect.Trait1Idx != 0 {
r := grand.Intn(4)
switch r {
case 0:
return gconv.Uint32(effect2s[effect.Trait1Idx].Remark)
case 1:
return gconv.Uint32(effect2s[effect.Trait2Idx].Remark)
case 2:
return gconv.Uint32(effect2s[effect.Trait3Idx].Remark)
case 3:
return gconv.Uint32(effect2s[effect.Trait4Idx].Remark)
}
if err != nil {
r := grand.Intn(len(service.NewDictInfoService().DataOne("effect")) - 1)
return gconv.Uint32(service.NewDictInfoService().DataOne("effect")[r].Remark)
}
r := grand.Intn(len(effect2) - 1)
return gconv.Uint32(effect2[r].Remark)
r := grand.Intn(4)
switch r {
case 0:
return gconv.Uint32(effect2s[effect.Trait1Idx].Remark)
case 1:
return gconv.Uint32(effect2s[effect.Trait2Idx].Remark)
case 2:
return gconv.Uint32(effect2s[effect.Trait3Idx].Remark)
case 3:
return gconv.Uint32(effect2s[effect.Trait4Idx].Remark)
}
return 0
}

View File

@@ -3,9 +3,13 @@ package service
import (
"blazing/cool"
"blazing/modules/blazing/model"
"context"
"fmt"
"strings"
"github.com/alpacahq/alpacadecimal"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/util/grand"
)
@@ -24,22 +28,21 @@ func NewPetFusionService() *PetFusionService {
}
}
var (
cachePetFusionService = gcache.New()
)
func (s *PetFusionService) ModifyBefore(ctx context.Context, method string, param g.MapStrAny) (err error) {
cachePetFusionService.Clear(context.Background())
return nil
}
//获取主副精灵融合的id,如果不存在,那就给一个保底的id
func (s *PetFusionService) Data(p1, p2, rand uint32) uint32 {
m := cool.DBM(s.Model)
rand = uint32(alpacadecimal.NewFromInt(int64(rand)).Mul(alpacadecimal.NewFromFloat(0.5)).IntPart())
var pet []model.PetFusion //一个特性应该是唯一的,但是我们要获取默认随机特性
condition := g.Map{
"main_pet_id": p1,
"sub_pet_id": p2,
"is_enable": 1,
// "hits between ? and ?" : g.Slice{1, 10},
// "exp > 0" : nil,
// "category" : g.Slice{100, 200},
}
m.Where(condition).Scan(&pet)
pet := s.getData(p1, p2)
for _, v := range pet {
rr := grand.Intn(100)
@@ -51,19 +54,49 @@ func (s *PetFusionService) Data(p1, p2, rand uint32) uint32 {
if len(pet) > 0 {
return 0
}
var pets []model.PetFusion
m = cool.DBM(s.Model)
m.Where("is_enable", 1).Where("is_default", 1).Scan(&pets)
if len(pets) == 0 {
return 0
}
pets := s.def()
res := pets[grand.Intn(len(pets)-1)]
rr := grand.Intn(100)
if rr < int(res.Probability+int32(rand)) {
return uint32(res.ResultPetID)
}
//到这里相当于直接失败
return 0
}
func (s *PetFusionService) getData(p1, p2 uint32) []model.PetFusion {
cacheKey := strings.Join([]string{fmt.Sprintf("%d", p1), fmt.Sprintf("%d", p2)}, ":")
//println(cacheKey, "获取融合id")
ret, _ := cachePetFusionService.GetOrSetFuncLock(context.Background(), cacheKey, func(context.Context) (interface{}, error) {
m := cool.DBM(s.Model)
var pet []model.PetFusion //一个特性应该是唯一的,但是我们要获取默认随机特性
condition := g.Map{
"main_pet_id": p1,
"sub_pet_id": p2,
"is_enable": 1,
// "hits between ? and ?" : g.Slice{1, 10},
// "exp > 0" : nil,
// "category" : g.Slice{100, 200},
}
m.Where(condition).Scan(&pet)
return pet, nil
}, 0)
return ret.Interface().([]model.PetFusion)
}
func (s *PetFusionService) def() []model.PetFusion {
//println(cacheKey, "获取融合id")
ret, _ := cachePetFusionService.GetOrSetFuncLock(context.Background(), "-1", func(context.Context) (interface{}, error) {
var pets []model.PetFusion
m := cool.DBM(s.Model)
m.Where("is_enable", 1).Where("is_default", 1).Scan(&pets)
return pets, nil
}, 0)
return ret.Interface().([]model.PetFusion)
}

View File

@@ -8,6 +8,7 @@ import (
"blazing/modules/dict/model"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/util/gconv"
)
@@ -53,29 +54,38 @@ func (s *DictInfoService) Data(ctx context.Context, types []string) (data interf
}
return
}
func (s *DictInfoService) DataOne(types string) (data []model.DictInfo, err error) {
func (s *DictInfoService) DataOne(types string) (data []model.DictInfo) {
var (
dictInfoModel = model.NewDictInfo()
dictTypeModel = model.NewDictType()
)
// 如果typeData为空, 则返回空
var ty *model.DictType
mType := cool.DBM(dictTypeModel)
mType.Where("key in (?)", types).Scan(&ty)
ret, _ := cacheDict.GetOrSetFunc(context.Background(), types, func(context.Context) (interface{}, error) {
// 如果typeData为空, 则返回空
var ty *model.DictType
mType := cool.DBM(dictTypeModel)
mType.Where("key in (?)", types).Scan(&ty)
// 如果typeData为空, 则返回空
if ty == nil {
return []model.DictInfo{}, nil
}
m := cool.DBM(dictInfoModel)
var ress []model.DictInfo
m.Where("typeId", ty.ID).Scan(&ress)
// 如果typeData为空, 则返回空
if ty == nil {
return []model.DictInfo{}, nil
}
m := cool.DBM(dictInfoModel)
var ress []model.DictInfo
m.Where("typeId", ty.ID).Scan(&ress)
return ress, nil
}, 0)
return ret.Interface().([]model.DictInfo)
return ress, nil
}
var (
cacheDict = gcache.New()
)
// ModifyAfter 修改后
func (s *DictInfoService) ModifyAfter(ctx context.Context, method string, param map[string]interface{}) (err error) {
cacheDict.Clear(context.Background())
if method == "Delete" {
// 删除后,同时删除子节点
ids, ok := param["ids"]