refactor: 删除战斗模块冗余代码,增加克洛斯地心入口
- 移除了 fight 包下多个冗余的结构体和接口定义 - 删除了未使用的战斗状态机和技能限制效果相关代码 - 移除了多余的 XML 注释内容
This commit is contained in:
54
logic/service/fight/battle/effect/EnumEffectTrigger.go
Normal file
54
logic/service/fight/battle/effect/EnumEffectTrigger.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package effect
|
||||||
|
|
||||||
|
import "github.com/tnnmigga/enum"
|
||||||
|
|
||||||
|
type EnumEffectTrigger string
|
||||||
|
|
||||||
|
var EffectTrigger = enum.New[struct {
|
||||||
|
OnBattleStart EnumEffectTrigger `enum:"OnBattleStart"` // 战斗开始时触发
|
||||||
|
BeforeSort EnumEffectTrigger `enum:"BeforeSort"` // 先手顺序判定前触发
|
||||||
|
BeforeUseSkillCheck EnumEffectTrigger `enum:"BeforeUseSkillCheck"` // 使用技能前检查(PP、状态等)
|
||||||
|
AfterUseSkillCheck EnumEffectTrigger `enum:"AfterUseSkillCheck"` // 使用技能检查后触发
|
||||||
|
BeforeMultiHit EnumEffectTrigger `enum:"BeforeMultiHit"` // 多段攻击开始前触发
|
||||||
|
BeforeHit EnumEffectTrigger `enum:"BeforeHit"` // 攻击命中前触发
|
||||||
|
OnCritPreDamage EnumEffectTrigger `enum:"OnCritPreDamage"` // 暴击判定成功且伤害计算前触发
|
||||||
|
PreDamage EnumEffectTrigger `enum:"PreDamage"` // 技能伤害计算前触发(增伤/减伤等)
|
||||||
|
OnHit EnumEffectTrigger `enum:"OnHit"` // 技能命中时触发
|
||||||
|
OnMiss EnumEffectTrigger `enum:"OnMiss"` // 技能未命中时触发
|
||||||
|
AfterAttacked EnumEffectTrigger `enum:"AfterAttacked"` // 被攻击后触发(受击判定)
|
||||||
|
OnDefeat EnumEffectTrigger `enum:"OnDefeat"` // 精灵被击败时触发
|
||||||
|
SkillUseEnd EnumEffectTrigger `enum:"SkillUseEnd"` // 技能使用结束后触发
|
||||||
|
OnBeforeCalculateDamage EnumEffectTrigger `enum:"OnBeforeCalculateDamage"` // 最终伤害计算前触发
|
||||||
|
OnDamage EnumEffectTrigger `enum:"OnDamage"` // 造成伤害时触发
|
||||||
|
Shield EnumEffectTrigger `enum:"Shield"` // 护盾值变化时触发
|
||||||
|
PostDamage EnumEffectTrigger `enum:"PostDamage"` // 伤害结算后触发(血量扣除后)
|
||||||
|
OnCritPostDamage EnumEffectTrigger `enum:"OnCritPostDamage"` // 暴击伤害结算后触发
|
||||||
|
OnTransform EnumEffectTrigger `enum:"OnTransform"` // 精灵变形/进化时触发
|
||||||
|
OnTransformEnd EnumEffectTrigger `enum:"OnTransformEnd"` // 变形/进化结束时触发
|
||||||
|
BeforeTransform EnumEffectTrigger `enum:"BeforeTransform"` // 变形/进化前触发
|
||||||
|
AfterTransform EnumEffectTrigger `enum:"AfterTransform"` // 变形/进化后触发
|
||||||
|
TurnStart EnumEffectTrigger `enum:"TurnStart"` // 回合开始时触发
|
||||||
|
TurnEnd EnumEffectTrigger `enum:"TurnEnd"` // 回合结束时触发
|
||||||
|
OnBeforeAddMark EnumEffectTrigger `enum:"OnBeforeAddMark"` // 添加印记前触发
|
||||||
|
OnAnyMarkAdded EnumEffectTrigger `enum:"OnAnyMarkAdded"` // 任何印记添加时触发
|
||||||
|
OnRemoveMark EnumEffectTrigger `enum:"OnRemoveMark"` // 移除印记时触发
|
||||||
|
OnMarkCreated EnumEffectTrigger `enum:"OnMarkCreated"` // 印记创建时触发
|
||||||
|
OnMarkDestroy EnumEffectTrigger `enum:"OnMarkDestroy"` // 印记销毁时触发
|
||||||
|
OnMarkDurationEnd EnumEffectTrigger `enum:"OnMarkDurationEnd"` // 印记持续回合结束时触发
|
||||||
|
OnStackBefore EnumEffectTrigger `enum:"OnStackBefore"` // 堆叠效果前触发
|
||||||
|
OnStack EnumEffectTrigger `enum:"OnStack"` // 堆叠效果触发
|
||||||
|
OnBeforeConsumeStack EnumEffectTrigger `enum:"OnBeforeConsumeStack"` // 消耗堆叠前触发
|
||||||
|
OnConsumeStack EnumEffectTrigger `enum:"OnConsumeStack"` // 消耗堆叠时触发
|
||||||
|
OnBeforeHeal EnumEffectTrigger `enum:"OnBeforeHeal"` // 治疗前触发
|
||||||
|
OnHeal EnumEffectTrigger `enum:"OnHeal"` // 治疗生效时触发
|
||||||
|
BeforeRageGain EnumEffectTrigger `enum:"BeforeRageGain"` // 增怒前触发
|
||||||
|
BeforeRageLoss EnumEffectTrigger `enum:"BeforeRageLoss"` // 减怒前触发
|
||||||
|
OnRageGain EnumEffectTrigger `enum:"OnRageGain"` // 增怒时触发
|
||||||
|
OnRageLoss EnumEffectTrigger `enum:"OnRageLoss"` // 减怒时触发
|
||||||
|
OnSwitchIn EnumEffectTrigger `enum:"OnSwitchIn"` // 精灵出战/上场时触发
|
||||||
|
OnSwitchOut EnumEffectTrigger `enum:"OnSwitchOut"` // 精灵下场时触发
|
||||||
|
OnOwnerSwitchIn EnumEffectTrigger `enum:"OnOwnerSwitchIn"` // 所属玩家精灵出战时触发
|
||||||
|
OnOwnerSwitchOut EnumEffectTrigger `enum:"OnOwnerSwitchOut"` // 所属玩家精灵下场时触发
|
||||||
|
BeforeEffect EnumEffectTrigger `enum:"BeforeEffect"` // 效果生效前触发
|
||||||
|
AfterEffect EnumEffectTrigger `enum:"AfterEffect"` // 效果生效后触发
|
||||||
|
}]()
|
||||||
215
logic/service/fight/battle/effect/effect.go
Normal file
215
logic/service/fight/battle/effect/effect.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
package effect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/badu/bus"
|
||||||
|
"github.com/tnnmigga/enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// BattleMode 枚举
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
type EnumBattleMode string
|
||||||
|
|
||||||
|
var BattleMode = enum.New[struct {
|
||||||
|
PVE EnumBattleMode `enum:"3"`
|
||||||
|
PVP EnumBattleMode `enum:"1"`
|
||||||
|
}]()
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// Effect 框架
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
type ContextType interface {
|
||||||
|
SourceID() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EffectContext struct {
|
||||||
|
Parent ContextType
|
||||||
|
Trigger EnumEffectTrigger
|
||||||
|
Container *EffectContainer
|
||||||
|
Effect *Effect
|
||||||
|
Available bool
|
||||||
|
Success bool
|
||||||
|
Done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *EffectContext) Stop() { c.Done = true }
|
||||||
|
func (c *EffectContext) SourceID() string {
|
||||||
|
if c.Container != nil {
|
||||||
|
return c.Container.ID
|
||||||
|
}
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
type EffectFunc func(ctx *EffectContext, next func())
|
||||||
|
|
||||||
|
type Effect struct {
|
||||||
|
ID string
|
||||||
|
Priority int
|
||||||
|
Triggers []EnumEffectTrigger
|
||||||
|
Condition func(*EffectContext) bool
|
||||||
|
Apply EffectFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type EffectContainer struct {
|
||||||
|
ID string
|
||||||
|
Effects []*Effect
|
||||||
|
Subs []*bus.Listener[*EffectContext]
|
||||||
|
Battle *Battle
|
||||||
|
Parent ContextType
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEffectContainer(id string, effects []*Effect, battle *Battle, parent ContextType) *EffectContainer {
|
||||||
|
c := &EffectContainer{
|
||||||
|
ID: id,
|
||||||
|
Effects: effects,
|
||||||
|
Battle: battle,
|
||||||
|
Parent: parent,
|
||||||
|
}
|
||||||
|
// 自动订阅 triggers
|
||||||
|
for _, e := range effects {
|
||||||
|
for _, trig := range e.Triggers {
|
||||||
|
sub := battle.GetTopic(trig).Sub(func(ctx *EffectContext) {
|
||||||
|
if ctx.Available && e.Apply != nil {
|
||||||
|
ctx.Effect = e
|
||||||
|
c.executeWithPriority([]*EffectContext{ctx})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
c.Subs = append(c.Subs, sub)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// done/next 执行队列,按优先级
|
||||||
|
func (c *EffectContainer) executeWithPriority(queue []*EffectContext) {
|
||||||
|
sort.SliceStable(queue, func(i, j int) bool {
|
||||||
|
return queue[i].Effect.Priority > queue[j].Effect.Priority
|
||||||
|
})
|
||||||
|
var runNext func(idx int)
|
||||||
|
runNext = func(idx int) {
|
||||||
|
if idx >= len(queue) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx := queue[idx]
|
||||||
|
if ctx.Available {
|
||||||
|
ctx.Effect.Apply(ctx, func() {
|
||||||
|
runNext(idx + 1)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
runNext(idx + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runNext(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// Battle
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
type Battle struct {
|
||||||
|
Turn int
|
||||||
|
topics map[EnumEffectTrigger]*bus.Topic[*EffectContext]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBattle() *Battle {
|
||||||
|
allTriggers := []EnumEffectTrigger{
|
||||||
|
EffectTrigger.OnBattleStart,
|
||||||
|
EffectTrigger.BeforeSort,
|
||||||
|
EffectTrigger.BeforeUseSkillCheck,
|
||||||
|
EffectTrigger.AfterUseSkillCheck,
|
||||||
|
EffectTrigger.BeforeMultiHit,
|
||||||
|
EffectTrigger.BeforeHit,
|
||||||
|
EffectTrigger.OnCritPreDamage,
|
||||||
|
EffectTrigger.PreDamage,
|
||||||
|
EffectTrigger.OnHit,
|
||||||
|
EffectTrigger.OnMiss,
|
||||||
|
EffectTrigger.AfterAttacked,
|
||||||
|
EffectTrigger.OnDefeat,
|
||||||
|
EffectTrigger.SkillUseEnd,
|
||||||
|
EffectTrigger.OnBeforeCalculateDamage,
|
||||||
|
EffectTrigger.OnDamage,
|
||||||
|
EffectTrigger.Shield,
|
||||||
|
EffectTrigger.PostDamage,
|
||||||
|
EffectTrigger.OnCritPostDamage,
|
||||||
|
EffectTrigger.OnTransform,
|
||||||
|
EffectTrigger.OnTransformEnd,
|
||||||
|
EffectTrigger.BeforeTransform,
|
||||||
|
EffectTrigger.AfterTransform,
|
||||||
|
EffectTrigger.TurnStart,
|
||||||
|
EffectTrigger.TurnEnd,
|
||||||
|
EffectTrigger.OnBeforeAddMark,
|
||||||
|
EffectTrigger.OnAnyMarkAdded,
|
||||||
|
EffectTrigger.OnRemoveMark,
|
||||||
|
EffectTrigger.OnMarkCreated,
|
||||||
|
EffectTrigger.OnMarkDestroy,
|
||||||
|
EffectTrigger.OnMarkDurationEnd,
|
||||||
|
EffectTrigger.OnStackBefore,
|
||||||
|
EffectTrigger.OnStack,
|
||||||
|
EffectTrigger.OnBeforeConsumeStack,
|
||||||
|
EffectTrigger.OnConsumeStack,
|
||||||
|
EffectTrigger.OnBeforeHeal,
|
||||||
|
EffectTrigger.OnHeal,
|
||||||
|
EffectTrigger.BeforeRageGain,
|
||||||
|
EffectTrigger.BeforeRageLoss,
|
||||||
|
EffectTrigger.OnRageGain,
|
||||||
|
EffectTrigger.OnRageLoss,
|
||||||
|
EffectTrigger.OnSwitchIn,
|
||||||
|
EffectTrigger.OnSwitchOut,
|
||||||
|
EffectTrigger.OnOwnerSwitchIn,
|
||||||
|
EffectTrigger.OnOwnerSwitchOut,
|
||||||
|
EffectTrigger.BeforeEffect,
|
||||||
|
EffectTrigger.AfterEffect,
|
||||||
|
}
|
||||||
|
topics := make(map[EnumEffectTrigger]*bus.Topic[*EffectContext])
|
||||||
|
for _, trig := range allTriggers {
|
||||||
|
topics[trig] = bus.NewTopic[*EffectContext]()
|
||||||
|
}
|
||||||
|
return &Battle{Turn: 0, topics: topics}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Battle) GetTopic(trig EnumEffectTrigger) *bus.Topic[*EffectContext] {
|
||||||
|
return b.topics[trig]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Battle) NextTurn(containers []*EffectContainer) {
|
||||||
|
b.Turn++
|
||||||
|
fmt.Printf("=== 回合 %d 开始 ===\n", b.Turn)
|
||||||
|
b.PublishTrigger(EffectTrigger.TurnStart, containers)
|
||||||
|
fmt.Println("=== 玩家操作阶段 ===")
|
||||||
|
b.PublishTrigger(EffectTrigger.TurnEnd, containers)
|
||||||
|
fmt.Printf("=== 回合 %d 结束 ===\n\n", b.Turn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Battle) PublishTrigger(trigger EnumEffectTrigger, containers []*EffectContainer) {
|
||||||
|
for _, c := range containers {
|
||||||
|
for _, e := range c.Effects {
|
||||||
|
for _, t := range e.Triggers {
|
||||||
|
if t == trigger {
|
||||||
|
ctx := &EffectContext{
|
||||||
|
Parent: c.Parent,
|
||||||
|
Trigger: trigger,
|
||||||
|
Container: c,
|
||||||
|
Effect: e,
|
||||||
|
Available: true,
|
||||||
|
}
|
||||||
|
b.GetTopic(trigger).Pub(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// 示例
|
||||||
|
// ========================
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SourceID() string { return p.ID }
|
||||||
40
logic/service/fight/battle/effect/effect_test.go
Normal file
40
logic/service/fight/battle/effect/effect_test.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package effect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_mainTT(t *testing.T) {
|
||||||
|
battle := NewBattle()
|
||||||
|
player := &Player{ID: "player1"}
|
||||||
|
|
||||||
|
container := NewEffectContainer("container1", []*Effect{
|
||||||
|
{
|
||||||
|
ID: "startEffect",
|
||||||
|
Priority: 10,
|
||||||
|
Triggers: []EnumEffectTrigger{EffectTrigger.TurnStart},
|
||||||
|
Apply: func(ctx *EffectContext, next func()) {
|
||||||
|
fmt.Println("回合开始效果: 增加护盾")
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "endEffect",
|
||||||
|
Priority: 5,
|
||||||
|
Triggers: []EnumEffectTrigger{EffectTrigger.TurnEnd},
|
||||||
|
Apply: func(ctx *EffectContext, next func()) {
|
||||||
|
fmt.Println("回合结束效果: 恢复怒气")
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, battle, player)
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
battle.NextTurn([]*EffectContainer{container})
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("测试 BattleMode 枚举:", BattleMode.PVE, BattleMode.PVP)
|
||||||
|
}
|
||||||
@@ -6,6 +6,6 @@ import "github.com/tnnmigga/enum"
|
|||||||
type EnumBattleMode int
|
type EnumBattleMode int
|
||||||
|
|
||||||
var BattleMode = enum.New[struct {
|
var BattleMode = enum.New[struct {
|
||||||
PVE EnumBattleMode // 玩家 vs AI
|
PVE EnumBattleMode `enum:"3"`
|
||||||
PVP EnumBattleMode // 玩家 vs 玩家
|
PVP EnumBattleMode `enum:"1"`
|
||||||
}]()
|
}]()
|
||||||
244
logic/service/fight/md/elo-rating-system.md
Normal file
244
logic/service/fight/md/elo-rating-system.md
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
# ELO评级系统
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
为匹配框架引入了ELO评级系统,为每个规则集单独维护ELO评级,提供更精确的玩家技能评估和匹配质量。
|
||||||
|
|
||||||
|
## 核心特性
|
||||||
|
|
||||||
|
### 🎯 规则集分离
|
||||||
|
- 每个规则集(如休闲模式、竞技模式)独立维护ELO评级
|
||||||
|
- 玩家在不同规则集下有不同的ELO分数
|
||||||
|
- 支持无限扩展新的规则集
|
||||||
|
|
||||||
|
### 📊 标准ELO算法
|
||||||
|
- 基于经典ELO评级系统
|
||||||
|
- 期望得分计算:`1 / (1 + 10^((对手ELO - 自己ELO) / 400))`
|
||||||
|
- 新ELO = 旧ELO + K * (实际得分 - 期望得分)
|
||||||
|
- 支持胜利(1分)、失败(0分)、平局(0.5分)
|
||||||
|
|
||||||
|
### ⚙️ 动态K因子
|
||||||
|
- **新手** (< 30场): K = 32 (快速调整)
|
||||||
|
- **普通** (30-100场): K = 24 (中等调整)
|
||||||
|
- **老手** (> 100场): K = 16 (稳定调整)
|
||||||
|
|
||||||
|
### 🛡️ 安全边界
|
||||||
|
- ELO范围:100 - 3000
|
||||||
|
- 初始ELO:1200
|
||||||
|
- 自动记录历史最高ELO
|
||||||
|
|
||||||
|
## 数据库设计
|
||||||
|
|
||||||
|
### player_elo_ratings 表
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE player_elo_ratings (
|
||||||
|
player_id TEXT NOT NULL, -- 玩家ID
|
||||||
|
rule_set_id TEXT NOT NULL, -- 规则集ID
|
||||||
|
elo_rating INTEGER DEFAULT 1200, -- 当前ELO评级
|
||||||
|
games_played INTEGER DEFAULT 0, -- 游戏场次
|
||||||
|
wins INTEGER DEFAULT 0, -- 胜利次数
|
||||||
|
losses INTEGER DEFAULT 0, -- 失败次数
|
||||||
|
draws INTEGER DEFAULT 0, -- 平局次数
|
||||||
|
highest_elo INTEGER DEFAULT 1200, -- 历史最高ELO
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
|
||||||
|
PRIMARY KEY (player_id, rule_set_id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心函数
|
||||||
|
|
||||||
|
- `get_or_create_player_elo()` - 获取或创建ELO记录
|
||||||
|
- `update_player_elo()` - 更新单个玩家ELO
|
||||||
|
- `batch_update_player_elos()` - 批量更新ELO(战斗结束)
|
||||||
|
- `get_elo_leaderboard()` - 获取排行榜
|
||||||
|
|
||||||
|
## API端点
|
||||||
|
|
||||||
|
### 排行榜
|
||||||
|
```
|
||||||
|
GET /api/v1/elo/leaderboard/:ruleSetId?limit=50&offset=0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 玩家ELO信息
|
||||||
|
```
|
||||||
|
GET /api/v1/elo/player/:playerId/:ruleSetId
|
||||||
|
GET /api/v1/elo/player/:playerId # 所有规则集
|
||||||
|
```
|
||||||
|
|
||||||
|
### 统计信息
|
||||||
|
```
|
||||||
|
GET /api/v1/elo/statistics/:ruleSetId
|
||||||
|
```
|
||||||
|
|
||||||
|
### 战斗预测
|
||||||
|
```
|
||||||
|
GET /api/v1/elo/predict/:playerAId/:playerBId/:ruleSetId
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置信息
|
||||||
|
```
|
||||||
|
GET /api/v1/elo/config
|
||||||
|
GET /api/v1/elo/win-rate/:eloDifference
|
||||||
|
```
|
||||||
|
|
||||||
|
## 系统集成
|
||||||
|
|
||||||
|
### 战斗结束自动更新
|
||||||
|
|
||||||
|
战斗结束时,`battleReportService` 会自动:
|
||||||
|
|
||||||
|
1. 获取两个玩家的当前ELO
|
||||||
|
2. 根据战斗结果计算新ELO
|
||||||
|
3. 原子性批量更新数据库
|
||||||
|
4. 记录详细日志
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 在 battleReportService.ts 中
|
||||||
|
if (battleData.ruleSetId && battleResult !== 'abandoned') {
|
||||||
|
await eloService.processBattleEloUpdate(
|
||||||
|
battleData.playerAId,
|
||||||
|
battleData.playerBId,
|
||||||
|
winnerId,
|
||||||
|
battleData.ruleSetId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 匹配系统集成
|
||||||
|
|
||||||
|
匹配系统已支持规则集分离,ELO系统可以:
|
||||||
|
|
||||||
|
- 基于ELO进行更精确的匹配
|
||||||
|
- 预测战斗结果概率
|
||||||
|
- 提供匹配质量评估
|
||||||
|
|
||||||
|
## 部署步骤
|
||||||
|
|
||||||
|
### 1. 数据库迁移
|
||||||
|
|
||||||
|
在Supabase SQL编辑器中执行:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 执行ELO系统迁移
|
||||||
|
\i packages/database/sql/06_add_elo_system.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 服务器更新
|
||||||
|
|
||||||
|
ELO系统已集成到现有服务器中,无需额外配置。
|
||||||
|
|
||||||
|
### 3. 测试验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试ELO计算逻辑
|
||||||
|
cd packages/server && npx tsx src/test-elo.ts
|
||||||
|
|
||||||
|
# 启动服务器测试API
|
||||||
|
node dist/cli.js server --port 8102
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 获取排行榜
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "http://localhost:8102/api/v1/elo/leaderboard/casual_standard_ruleset?limit=10"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查看玩家ELO
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "http://localhost:8102/api/v1/elo/player/player123/casual_standard_ruleset"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 预测战斗结果
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl "http://localhost:8102/api/v1/elo/predict/player1/player2/competitive_ruleset"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置选项
|
||||||
|
|
||||||
|
ELO系统支持运行时配置调整:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const eloService = new EloService(repository, calculationService)
|
||||||
|
|
||||||
|
// 更新配置
|
||||||
|
eloService.updateEloConfig({
|
||||||
|
initialElo: 1500,
|
||||||
|
kFactor: {
|
||||||
|
newbie: 40,
|
||||||
|
normal: 20,
|
||||||
|
veteran: 10
|
||||||
|
},
|
||||||
|
minElo: 200,
|
||||||
|
maxElo: 2800
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 监控和日志
|
||||||
|
|
||||||
|
### 日志记录
|
||||||
|
|
||||||
|
- ELO更新详细日志
|
||||||
|
- 计算过程追踪
|
||||||
|
- 错误处理和恢复
|
||||||
|
|
||||||
|
### 性能监控
|
||||||
|
|
||||||
|
- 数据库查询优化
|
||||||
|
- 批量更新性能
|
||||||
|
- API响应时间
|
||||||
|
|
||||||
|
## 未来扩展
|
||||||
|
|
||||||
|
### 可能的增强功能
|
||||||
|
|
||||||
|
1. **季度重置** - 定期重置ELO评级
|
||||||
|
2. **衰减机制** - 长期不活跃玩家ELO衰减
|
||||||
|
3. **匹配优化** - 基于ELO的智能匹配
|
||||||
|
4. **成就系统** - ELO里程碑奖励
|
||||||
|
5. **数据分析** - ELO分布统计和趋势分析
|
||||||
|
|
||||||
|
### 扩展新规则集
|
||||||
|
|
||||||
|
添加新规则集只需:
|
||||||
|
|
||||||
|
1. 在规则系统中定义新规则集
|
||||||
|
2. ELO系统自动支持(无需代码修改)
|
||||||
|
3. 玩家首次游戏时自动创建ELO记录
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
### 常见问题
|
||||||
|
|
||||||
|
1. **ELO记录不存在**
|
||||||
|
- 系统会自动创建初始记录
|
||||||
|
- 检查规则集ID是否正确
|
||||||
|
|
||||||
|
2. **ELO更新失败**
|
||||||
|
- 检查数据库连接
|
||||||
|
- 查看服务器日志
|
||||||
|
- 验证战斗数据完整性
|
||||||
|
|
||||||
|
3. **API响应错误**
|
||||||
|
- 确认数据库迁移已执行
|
||||||
|
- 检查API路由注册
|
||||||
|
- 验证请求参数格式
|
||||||
|
|
||||||
|
### 调试工具
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试ELO计算
|
||||||
|
npx tsx packages/server/src/test-elo.ts
|
||||||
|
|
||||||
|
# 检查数据库表
|
||||||
|
SELECT * FROM player_elo_ratings LIMIT 10;
|
||||||
|
|
||||||
|
# 查看API状态
|
||||||
|
curl "http://localhost:8102/api/v1/elo/config"
|
||||||
|
```
|
||||||
@@ -8,6 +8,7 @@ des 鼠标移上去的提示
|
|||||||
<!-- 从场景FromMap进入本场景时,玩家将出现在 (PosX, PosY)
|
<!-- 从场景FromMap进入本场景时,玩家将出现在 (PosX, PosY)
|
||||||
<Entries>节点下的表示几个进出点玩家出现的坐标
|
<Entries>节点下的表示几个进出点玩家出现的坐标
|
||||||
<changeMapComp>节点下表示切换场景组件
|
<changeMapComp>节点下表示切换场景组件
|
||||||
|
|
||||||
<funComp>节点下表示点击,然后走近触发某个动作
|
<funComp>节点下表示点击,然后走近触发某个动作
|
||||||
<autoComp>节点下表示不需要点击,走到碰撞区域就触发的动作
|
<autoComp>节点下表示不需要点击,走到碰撞区域就触发的动作
|
||||||
-->
|
-->
|
||||||
|
|||||||
Reference in New Issue
Block a user