perf: 优化战斗逻辑性能与内存分配

This commit is contained in:
xinian
2026-04-11 09:39:00 +08:00
parent 5bfdb5c32b
commit 5f5634d999
6 changed files with 122 additions and 52 deletions

View File

@@ -44,6 +44,7 @@ func (f *FightC) openActionWindow() {
f.actionMu.Lock()
f.acceptActions = true
f.pendingActions = f.pendingActions[:0]
f.pendingHead = 0
f.actionRound.Store(uint32(f.Round))
f.actionMu.Unlock()
}
@@ -52,6 +53,7 @@ func (f *FightC) closeActionWindow() {
f.actionMu.Lock()
f.acceptActions = false
f.pendingActions = f.pendingActions[:0]
f.pendingHead = 0
f.actionRound.Store(0)
f.actionMu.Unlock()
@@ -73,8 +75,10 @@ func (f *FightC) submitAction(act action.BattleActionI) {
f.actionMu.Unlock()
return
}
f.compactPendingActionsLocked()
replaceIndex := -1
for i, pending := range f.pendingActions {
for i := f.pendingHead; i < len(f.pendingActions); i++ {
pending := f.pendingActions[i]
if pending == nil || actionSlotKeyFromAction(pending) != actionSlotKeyFromAction(act) {
continue
}
@@ -105,15 +109,23 @@ func (f *FightC) submitAction(act action.BattleActionI) {
func (f *FightC) nextAction() action.BattleActionI {
f.actionMu.Lock()
if len(f.pendingActions) == 0 {
if f.pendingHead >= len(f.pendingActions) {
f.pendingActions = f.pendingActions[:0]
f.pendingHead = 0
f.actionMu.Unlock()
return nil
}
act := f.pendingActions[0]
copy(f.pendingActions, f.pendingActions[1:])
f.pendingActions = f.pendingActions[:len(f.pendingActions)-1]
hasMore := len(f.pendingActions) > 0
act := f.pendingActions[f.pendingHead]
f.pendingActions[f.pendingHead] = nil
f.pendingHead++
hasMore := f.pendingHead < len(f.pendingActions)
if !hasMore {
f.pendingActions = f.pendingActions[:0]
f.pendingHead = 0
} else {
f.compactPendingActionsLocked()
}
notify := f.actionNotify
f.actionMu.Unlock()
@@ -127,6 +139,22 @@ func (f *FightC) nextAction() action.BattleActionI {
return act
}
func (f *FightC) compactPendingActionsLocked() {
if f.pendingHead == 0 {
return
}
if f.pendingHead < len(f.pendingActions)/2 && len(f.pendingActions) < cap(f.pendingActions) {
return
}
remaining := len(f.pendingActions) - f.pendingHead
copy(f.pendingActions, f.pendingActions[f.pendingHead:])
for i := remaining; i < len(f.pendingActions); i++ {
f.pendingActions[i] = nil
}
f.pendingActions = f.pendingActions[:remaining]
f.pendingHead = 0
}
// 玩家逃跑/无响应/掉线
func (f *FightC) Over(c common.PlayerI, res model.EnumBattleOverReason) {
if f.closefight {

View File

@@ -467,12 +467,7 @@ func (f *FightC) TURNOVER(cur *input.Input) {
if cur == nil {
return
}
for _, pet := range cur.BenchPets() {
if pet != nil && pet.Info.Hp > 0 {
_hasBackup = true
break
}
}
_hasBackup = cur.HasLivingBench()
f.sendLegacySpriteDie(cur, _hasBackup)
f.Broadcast(func(ff *input.Input) {

View File

@@ -39,6 +39,7 @@ type FightC struct {
actionNotify chan struct{}
acceptActions bool
pendingActions []action.BattleActionI // 待处理动作队列,同一战斗位最多保留一个动作
pendingHead int
actionRound atomic.Uint32
quit chan struct{}
@@ -250,20 +251,13 @@ func (f *FightC) sideHasActionableSlots(side int) bool {
}
func (f *FightC) slotNeedsAction(in *input.Input) bool {
var bench []*info.BattlePetEntity
if in == nil {
return false
}
if current := in.CurrentPet(); current != nil && current.Info.Hp > 0 {
return true
}
bench = in.BenchPets()
for _, pet := range bench {
if pet != nil && pet.Info.Hp > 0 {
return true
}
}
return false
return in.HasLivingBench()
}
func (f *FightC) setActionAttackValue(act action.BattleActionI) {

View File

@@ -107,18 +107,14 @@ func (our *Input) GetProp(id int) alpacadecimal.Decimal {
}
func (our *Input) GetEffect(etype EnumEffectType, id int) Effect {
var ret []Effect
pr := EffectIDCombiner{}
pr.Combine(etype, 0, gconv.Uint16(id))
for _, v := range our.Effects {
if v.ID().Base == pr.Base && v.Alive() {
ret = append(ret, v)
our.ensureEffectIndex()
bucket := our.effectsByBase[pr.Base]
for i := len(bucket) - 1; i >= 0; i-- {
if bucket[i] != nil && bucket[i].Alive() {
return bucket[i]
}
}
if len(ret) > 0 {
return ret[len(ret)-1]
}
return nil
}
@@ -178,6 +174,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
if e == nil {
return nil
}
our.ensureEffectIndex()
ctx := e.Ctx()
if ctx != nil {
if ctx.Source == nil {
@@ -204,7 +201,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
//TODO 先激活
//fmt.Println("产生回合数", e.ID(), e.Duration())
// 如果已有同 ID 的效果,尝试叠加
for _, v := range our.Effects {
for _, v := range our.effectsByBase[e.ID().Base] {
if v == e {
return nil //完全相同,跳过执行
}
@@ -219,7 +216,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
if !v.CanStack() { //说明进行了替换
v.Alive(false) //不允许叠层,取消效果
e.Duration(utils.Max(e.Duration(), v.Duration()))
our.Effects = append(our.Effects, e)
our.appendEffect(e)
return v //这里把V替换掉了
} else {
//默认给叠一层
@@ -237,7 +234,7 @@ func (our *Input) AddEffect(in *Input, e Effect) Effect {
}
//无限叠加比如能力提升类buff
// 如果没有同 ID 的效果,直接添加
our.Effects = append(our.Effects, e)
our.appendEffect(e)
return nil
}
@@ -309,6 +306,34 @@ func (our *Input) CancelTurn(in *Input) {
}
func (our *Input) ensureEffectIndex() {
if our == nil {
return
}
if our.effectsByBase == nil {
our.effectsByBase = make(map[int64][]Effect, len(our.Effects))
}
if our.indexedEffects > len(our.Effects) {
our.effectsByBase = make(map[int64][]Effect, len(our.Effects))
our.indexedEffects = 0
}
for our.indexedEffects < len(our.Effects) {
effect := our.Effects[our.indexedEffects]
if effect != nil {
our.effectsByBase[effect.ID().Base] = append(our.effectsByBase[effect.ID().Base], effect)
}
our.indexedEffects++
}
}
func (our *Input) appendEffect(effect Effect) {
if our == nil || effect == nil {
return
}
our.Effects = append(our.Effects, effect)
our.ensureEffectIndex()
}
// // 消除全部 断回合效果,但是我放下场的时候应该断掉所有的回合类效果
// func (our *Input) CancelAll() {
// our.Effects = make([]Effect, 0)

View File

@@ -15,6 +15,13 @@ import (
"github.com/jinzhu/copier"
)
var statusBonuses = map[info.EnumPetStatus]float64{
info.PetStatus.Paralysis: 1.5,
info.PetStatus.Poisoned: 1.5,
info.PetStatus.Sleep: 2.0,
// /info.BattleStatus.Frozen: 2.0,
}
type Input struct {
CanChange uint32 //是否可以死亡切换CanChange
// CanAction bool //是否可以行动
@@ -27,9 +34,11 @@ type Input struct {
CanCapture int
Finished bool //是否加载完成
// info.BattleActionI
Effects []Effect //effects 实际上全局就是effect无限回合 //effects容器 技能的
EffectCache []Effect //这里是命中前执行的容器,也就是命中前执行的所有逻辑相关,理论上一个effect被激活,就应该同时将其他的effect取消激活
EffectLost []Effect
Effects []Effect //effects 实际上全局就是effect无限回合 //effects容器 技能的
EffectCache []Effect //这里是命中前执行的容器,也就是命中前执行的所有逻辑相关,理论上一个effect被激活,就应该同时将其他的effect取消激活
EffectLost []Effect
effectsByBase map[int64][]Effect
indexedEffects int
// 删掉伤害记录,可以在回调中记录,而不是每次调用记录
*model.AttackValue
FightC common.FightI
@@ -57,6 +66,7 @@ func NewInput(c common.FightI, p common.PlayerI) *Input {
ret := &Input{FightC: c, Player: p}
ret.Effects = make([]Effect, 0)
ret.CurPet = make([]*info.BattlePetEntity, 0)
ret.effectsByBase = make(map[int64][]Effect)
// t := Geteffect(EffectType.Damage, 0)
// t.Effect.SetArgs(ret)
@@ -115,6 +125,27 @@ func (our *Input) BenchPets() []*info.BattlePetEntity {
return bench
}
func (our *Input) HasLivingBench() bool {
if our == nil {
return false
}
current := our.CurrentPet()
currentCatchTime := uint32(0)
if current != nil {
currentCatchTime = current.Info.CatchTime
}
for _, pet := range our.AllPet {
if pet == nil || pet.Info.Hp == 0 {
continue
}
if current != nil && pet.Info.CatchTime == currentCatchTime {
continue
}
return true
}
return false
}
func (our *Input) OpponentSlots() []*Input {
if our == nil {
return nil
@@ -299,20 +330,12 @@ func (our *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.Chang
// GetStatusBonus 获取最高的状态倍率
// 遍历状态数组返回存在的状态中最高的倍率无状态则返回1.0
func (our *Input) GetStatusBonus() float64 {
// 异常状态倍率映射表(状态索引 -> 倍率)
var statusBonuses = map[info.EnumPetStatus]float64{
info.PetStatus.Paralysis: 1.5,
info.PetStatus.Poisoned: 1.5,
info.PetStatus.Sleep: 2.0,
// /info.BattleStatus.Frozen: 2.0,
}
maxBonus := 1.0 // 默认无状态倍率
for statusIdx := 0; statusIdx < 20; statusIdx++ {
t := our.InitEffect(EffectType.Status, statusIdx)
t := our.GetEffect(EffectType.Status, statusIdx)
// 检查状态是否存在数组中值为1表示存在该状态
if t != nil && t.Stack() > 0 {
if t != nil && t.Alive() {
if bonus, exists := statusBonuses[info.EnumPetStatus(statusIdx)]; exists && bonus > maxBonus {
maxBonus = bonus
}

View File

@@ -24,6 +24,7 @@ type EffectNode struct {
canStack bool // 最大叠加层数 ,正常都是不允许叠加的,除了衰弱特殊效果 ,异常和能力的叠层
isFirst bool
SideEffectArgs []int // 附加效果参数
cachedArgs []alpacadecimal.Decimal
// owner bool //是否作用自身
Success bool // 是否执行成功 成功XXX失败XXX
arget bool // 传出作用对象,默认0是自身,1是作用于对面
@@ -240,18 +241,22 @@ func (e *EffectNode) SetArgs(t *input.Input, a ...int) {
e.Input = t
if len(a) > 0 {
e.SideEffectArgs = a
e.cachedArgs = e.cachedArgs[:0]
for _, v := range a {
e.cachedArgs = append(e.cachedArgs, alpacadecimal.NewFromInt(int64(v)))
}
}
}
func (e *EffectNode) Args() []alpacadecimal.Decimal {
var ret []alpacadecimal.Decimal
for _, v := range e.SideEffectArgs {
ret = append(ret, alpacadecimal.NewFromInt(int64(v)))
if len(e.cachedArgs) == len(e.SideEffectArgs) {
return e.cachedArgs
}
return ret
e.cachedArgs = e.cachedArgs[:0]
for _, v := range e.SideEffectArgs {
e.cachedArgs = append(e.cachedArgs, alpacadecimal.NewFromInt(int64(v)))
}
return e.cachedArgs
}