feat: 增强踢人逻辑与BOSS脚本支持
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
优化踢人超时处理和僵尸连接清理,支持BOSS动作脚本并增加测试,修复事件匹配与战斗循环中的并发问题。
This commit is contained in:
@@ -2,6 +2,10 @@ package model
|
||||
|
||||
import (
|
||||
"blazing/cool"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,3 +50,47 @@ func NewBossConfig() *BossConfig {
|
||||
func init() {
|
||||
cool.CreateTable(&BossConfig{})
|
||||
}
|
||||
|
||||
// RunHookActionScript 执行BOSS脚本中的 hookAction,并传入 fight 的 hookaction 参数。
|
||||
// 返回值遵循 HookAction 语义:true 允许继续出手,false 阻止继续出手。
|
||||
func (b *BossConfig) RunHookActionScript(hookAction any) (bool, error) {
|
||||
if b == nil || strings.TrimSpace(b.Script) == "" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
program, err := goja.Compile("boss_hook_action.js", b.Script, false)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("compile boss script: %w", err)
|
||||
}
|
||||
|
||||
vm := goja.New()
|
||||
if _, err = vm.RunProgram(program); err != nil {
|
||||
return false, fmt.Errorf("run boss script: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
callable goja.Callable
|
||||
ok bool
|
||||
)
|
||||
for _, fnName := range []string{"hookAction", "HookAction", "hookaction"} {
|
||||
callable, ok = goja.AssertFunction(vm.Get(fnName))
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false, fmt.Errorf("boss script function not found: hookAction")
|
||||
}
|
||||
|
||||
result, err := callable(goja.Undefined(), vm.ToValue(hookAction))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("execute boss hookAction: %w", err)
|
||||
}
|
||||
|
||||
// 与既有HookAction默认行为保持一致:未显式返回时视为允许继续出手。
|
||||
if goja.IsUndefined(result) || goja.IsNull(result) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return result.ToBoolean(), nil
|
||||
}
|
||||
|
||||
44
modules/config/model/boss_pet_test.go
Normal file
44
modules/config/model/boss_pet_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package model
|
||||
|
||||
import "testing"
|
||||
|
||||
type testHookAction struct {
|
||||
Allow bool
|
||||
Round int
|
||||
}
|
||||
|
||||
func TestBossConfigRunHookActionScript(t *testing.T) {
|
||||
boss := &BossConfig{
|
||||
Script: `
|
||||
function hookAction(hookaction) {
|
||||
return hookaction.Allow && hookaction.Round >= 2;
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
ok, err := boss.RunHookActionScript(testHookAction{Allow: true, Round: 2})
|
||||
if err != nil {
|
||||
t.Fatalf("RunHookActionScript returned error: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("RunHookActionScript = false, want true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBossConfigRunHookActionScriptEmptyReturnDefaultsTrue(t *testing.T) {
|
||||
boss := &BossConfig{
|
||||
Script: `
|
||||
function hookAction(hookaction) {
|
||||
var _ = hookaction;
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
ok, err := boss.RunHookActionScript(testHookAction{Allow: false, Round: 1})
|
||||
if err != nil {
|
||||
t.Fatalf("RunHookActionScript returned error: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("RunHookActionScript = false, want true")
|
||||
}
|
||||
}
|
||||
@@ -179,19 +179,51 @@ func (s *InfoService) Gensession() string {
|
||||
func (s *InfoService) Kick(id uint32) error {
|
||||
|
||||
useid1, err := share.ShareManager.GetUserOnline(id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
if err != nil || useid1 == 0 {
|
||||
// 请求进入时已经离线,视为成功
|
||||
return nil
|
||||
}
|
||||
|
||||
cl, ok := cool.GetClientOnly(useid1)
|
||||
if ok {
|
||||
err := cl.KickPerson(id) //实现指定服务器踢人
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok || cl == nil {
|
||||
// 目标服务器不在线,清理僵尸在线标记并视为成功
|
||||
_ = share.ShareManager.DeleteUserOnline(id)
|
||||
return nil
|
||||
}
|
||||
|
||||
resultCh := make(chan error, 1)
|
||||
go func() {
|
||||
resultCh <- cl.KickPerson(id) // 实现指定服务器踢人
|
||||
}()
|
||||
|
||||
select {
|
||||
case callErr := <-resultCh:
|
||||
if callErr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 调用失败后兜底:若已离线/切服/目标服不在线则视为成功
|
||||
useid2, err2 := share.ShareManager.GetUserOnline(id)
|
||||
if err2 != nil || useid2 == 0 || useid2 != useid1 {
|
||||
return nil
|
||||
}
|
||||
if cl2, ok2 := cool.GetClientOnly(useid2); !ok2 || cl2 == nil {
|
||||
_ = share.ShareManager.DeleteUserOnline(id)
|
||||
return nil
|
||||
}
|
||||
return callErr
|
||||
case <-time.After(3 * time.Second):
|
||||
// 防止异常场景下无限等待;超时不按成功处理
|
||||
useid2, err2 := share.ShareManager.GetUserOnline(id)
|
||||
if err2 != nil || useid2 == 0 || useid2 != useid1 {
|
||||
return nil
|
||||
}
|
||||
if cl2, ok2 := cool.GetClientOnly(useid2); !ok2 || cl2 == nil {
|
||||
_ = share.ShareManager.DeleteUserOnline(id)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("kick timeout, user still online: uid=%d server=%d", id, useid2)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveToLocalFile 兜底保存:将数据写入本地lose文件夹
|
||||
|
||||
Reference in New Issue
Block a user