diff --git a/logic/controller/user_task.go b/logic/controller/user_task.go index 4d78f6eae..fb20a1e00 100644 --- a/logic/controller/user_task.go +++ b/logic/controller/user_task.go @@ -19,9 +19,13 @@ func (h Controller) AcceptTask(data *task.AcceptTaskInboundInfo, c *player.Playe if c.Info.GetTask(int(data.TaskId)) != model.Unaccepted { return nil, errorcode.ErrorCodes.ErrSystemError } - if !service.NewTaskService().IsAcceptable(data.TaskId) { + taskinfo := service.NewTaskService().IsAcceptable(data.TaskId) + if taskinfo == nil { return nil, errorcode.ErrorCodes.ErrSystemError } + if taskinfo.ParentTaskId != 0 && c.Info.GetTask(int(taskinfo.ParentTaskId)) != model.Completed { + return nil, errorcode.ErrorCodes.ErrInsufficientTasksForReward + } c.Info.SetTask(int(data.TaskId), model.Accepted) c.Service.Task.Exec(uint32(data.TaskId), func(t *model.TaskEX) bool { @@ -41,8 +45,8 @@ func (h Controller) AcceptTask(data *task.AcceptTaskInboundInfo, c *player.Playe // 返回: 空输出结果和错误码 func (h Controller) AddTaskBuf(data *task.AddTaskBufInboundInfo, c *player.Player) (result *task.AddTaskBufOutboundInfo, err errorcode.ErrorCode) { - if !service.NewTaskService().IsAcceptable(data.TaskId) { - return nil, errorcode.ErrorCodes.ErrSystemError + if c.Info.GetTask(int(data.TaskId)) != model.Accepted { + return result, errorcode.ErrorCodes.ErrAwardAlreadyClaimed } c.Service.Task.Exec(data.TaskId, func(taskEx *model.TaskEX) bool { taskEx.Data = data.TaskList @@ -63,9 +67,9 @@ func (h Controller) CompleteTask(data1 *task.CompleteTaskInboundInfo, c *player. // fmt.Println("error", data1.OutState, c.Info.UserID, "分支溢出") // return result, 0 // } - if !service.NewTaskService().IsAcceptable(data1.TaskId) { - return nil, errorcode.ErrorCodes.ErrSystemError - } + // if service.NewTaskService().IsAcceptable(data1.TaskId) == nil { + // return nil, errorcode.ErrorCodes.ErrSystemError + // } c.Info.SetTask(int(data1.TaskId), model.Completed) result = &task.CompleteTaskOutboundInfo{ @@ -112,13 +116,11 @@ func (h Controller) GetTaskBuf(data *task.GetTaskBufInboundInfo, c *player.Playe // DeleteTask 删除任务 func (h Controller) DeleteTask(data *task.DeleteTaskInboundInfo, c *player.Player) (result *task.DeleteTaskOutboundInfo, err errorcode.ErrorCode) { - if !service.NewTaskService().IsAcceptable(data.TaskId) { + + if c.Info.GetTask(int(data.TaskId)) != model.Accepted { return nil, errorcode.ErrorCodes.ErrSystemError } - if c.Info.GetTask(int(data.TaskId)) == model.Accepted { - c.Info.SetTask(int(data.TaskId), model.Unaccepted) - return &task.DeleteTaskOutboundInfo{TaskId: data.TaskId}, 0 - } - + result = &task.DeleteTaskOutboundInfo{TaskId: data.TaskId} + c.Info.SetTask(int(data.TaskId), model.Unaccepted) return &task.DeleteTaskOutboundInfo{}, 0 } diff --git a/logic/main.go b/logic/main.go index 3bd8e3d2b..1a59b14ad 100644 --- a/logic/main.go +++ b/logic/main.go @@ -39,13 +39,14 @@ func PprofWeb() { // cleanup 优雅清理资源,根据业务需求实现 func cleanup() { log.Println("执行优雅清理资源...") - fight.Fightpool.ReleaseTimeout(0) + player.Mainplayer.Range(func(key uint32, value *player.Player) bool { - value.Save() + value.Kick() return true }) + fight.Fightpool.ReleaseTimeout(0) log.Println("资源清理完成,程序即将退出") } diff --git a/logic/service/fight/effect/effect_attr.go b/logic/service/fight/effect/effect_attr.go index 8b98d85de..6cb0c28cc 100644 --- a/logic/service/fight/effect/effect_attr.go +++ b/logic/service/fight/effect/effect_attr.go @@ -39,7 +39,9 @@ type EffectPropSyncReverse struct { ctx propOpContext // 操作上下文(存储原始值) ourpet *info.BattlePetEntity opppet *info.BattlePetEntity - can bool + + isactive bool + can bool } // 工厂函数:创建属性同步/反转效果实例 @@ -95,42 +97,36 @@ func (e *EffectPropSyncReverse) OnSkill() bool { } e.ourpet = e.Ctx().Our.CurrentPet e.opppet = e.Ctx().Opp.CurrentPet - switch e.ctx.opType { - case opDefenseSync, opAttackSync: - // 同步攻防属性:保存我方原始值,覆盖为对方值 - e.ctx.oldOurProp = e.ourpet.Info.Prop[e.ctx.propIndex] - e.ourpet.Info.Prop[e.ctx.propIndex] = e.opppet.Info.Prop[e.ctx.propIndex] - - case opTypeReverse: - // 反转属性类型:保存双方原始值,交换类型 - e.ctx.oldOurType = e.ourpet.PetInfo.Type - e.ctx.oldOppType = e.opppet.PetInfo.Type - e.ourpet.PetInfo.Type, e.opppet.PetInfo.Type = e.opppet.PetInfo.Type, e.ourpet.PetInfo.Type - - println("Effect55_o", e.ourpet.PetInfo.Type, e.opppet.PetInfo.Type) - - case opTypeSync: - // 同步属性类型:保存我方原始值,覆盖为对方值 - e.ctx.oldOurType = e.ourpet.PetInfo.Type - e.ourpet.PetInfo.Type = e.opppet.PetInfo.Type - case opTypeMaxHP: // 减少最大生命值 - if e.opppet.GetMaxHP().Cmp(e.Args()[0]) == -1 { - e.opppet.Info.MaxHp -= uint32(e.Args()[0].IntPart()) - - } - - } e.can = true - + e.active(true) + return true } // Alive:效果存活判定(结束时还原属性) func (e *EffectPropSyncReverse) Alive(t ...bool) bool { + if len(t) > 0 { + if t[0] { + e.active(true) + } else { + e.active(false) + } + + } + return e.EffectNode.Alive(t...) + +} + +// Alive:效果存活判定(结束时还原属性) +func (e *EffectPropSyncReverse) active(is bool) { //println("属性类测试", t) + if !e.can { + return + } - if e.BoolisFalse(t...) && e.can { + if !is && e.isactive { //取消反转 + e.isactive = false switch e.ctx.opType { case opDefenseSync, opAttackSync: // 还原攻防属性 @@ -148,10 +144,10 @@ func (e *EffectPropSyncReverse) Alive(t ...bool) bool { // case opTypeMaxHP: // 减少最大生命值 // oppPet.Info.MaxHp += uint32(e.Args()[0].IntPart()) } - e.can = false - } - if e.BoolisTrue(t...) && e.can { + } + if is && !e.isactive { //激活 + e.isactive = true switch e.ctx.opType { case opDefenseSync, opAttackSync: // 同步攻防属性:保存我方原始值,覆盖为对方值 @@ -175,48 +171,6 @@ func (e *EffectPropSyncReverse) Alive(t ...bool) bool { // } } - e.can = false - } - return e.EffectNode.Alive(t...) - -} -func (e *EffectPropSyncReverse) TurnEnd() { - - if e.Duration() == 0 { // 保留 (负数表示永久) - if e.can { - switch e.ctx.opType { - case opDefenseSync, opAttackSync: - // 还原攻防属性 - e.ourpet.Info.Prop[e.ctx.propIndex] = e.ctx.oldOurProp - - case opTypeReverse: - // 还原反转的属性类型(恢复双方原始值) - e.ourpet.PetInfo.Type = e.ctx.oldOurType - e.ourpet.PetInfo.Type = e.ctx.oldOppType - println("Effect55_o取消效果", e.ourpet.PetInfo.Type, e.opppet.PetInfo.Type) - case opTypeSync: - // 还原同步的属性类型 - e.ourpet.PetInfo.Type = e.ctx.oldOurType - default: - // case opTypeMaxHP: // 减少最大生命值 - // oppPet.Info.MaxHp += uint32(e.Args()[0].IntPart()) - } - e.can = false - } - e.Alive(false) - - } else { - // e.trunl.Do(func() { - - // if !e.Ctx().Our.FightC.IsFirst(e.Ctx().Our.Player) { //如果我方后手,那就给回合+1 - // e.duration++ - // // e.Alive(true) - // } - - if e.Duration() > 0 { - e.Duration(e.Duration() - 1) - } - // }) } diff --git a/logic/service/fight/fightc.go b/logic/service/fight/fightc.go index caf9dc94f..e556c4155 100644 --- a/logic/service/fight/fightc.go +++ b/logic/service/fight/fightc.go @@ -357,7 +357,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) f.Broadcast(func(fighter *input.Input) { for _, switchAction := range f.Switch { if fighter.Player.GetInfo().UserID != switchAction.Reason.UserId { - println("切精灵", switchAction.Reason.UserId, switchAction.Reason.ID) + // println("切精灵", switchAction.Reason.UserId, switchAction.Reason.ID) fighter.Player.SendPackCmd(2407, &switchAction.Reason) } } diff --git a/logic/service/fight/input.go b/logic/service/fight/input.go index f5d51c7e7..a0c9f8085 100644 --- a/logic/service/fight/input.go +++ b/logic/service/fight/input.go @@ -271,7 +271,7 @@ func NewFight(p1, p2 common.PlayerI, fn func(info.FightOverInfo)) (*FightC, erro fmt.Println("NewFight", p1.GetInfo().UserID) f := &FightC{} f.ownerID = p1.GetInfo().UserID - // 1. 构建战斗开始信息(整理双方初始宠物信息) + f.Switch = make(map[uint32]*action.ActiveSwitchAction) f.callback = fn //战斗结束的回调 f.quit = make(chan struct{}) @@ -322,7 +322,7 @@ func NewFight(p1, p2 common.PlayerI, fn func(info.FightOverInfo)) (*FightC, erro } f.Broadcast(func(ff *input.Input) { - fmt.Println("战斗开始信息", ff.UserID) + ff.Player.SendPackCmd(2503, &f.ReadyInfo) }) diff --git a/logic/service/fight/node/node.go b/logic/service/fight/node/node.go index 10106804e..e116de0bc 100644 --- a/logic/service/fight/node/node.go +++ b/logic/service/fight/node/node.go @@ -32,7 +32,7 @@ type EffectNode struct { func (e *EffectNode) Alive(t ...bool) bool { if len(t) > 0 { - // println("效果失效", e.id.GetEffectType(), e.ID().Suffix(), t[0]) + // println("效果失效", e.id.GetEffectType(), e.ID().Suffix(), t[0]) e.alive = t[0] } @@ -123,25 +123,26 @@ func (e *EffectNode) AttackTime(*input.Input, *input.Input) bool { func (e *EffectNode) PropBefer(in *input.Input, prop int8, level int8, ptype info.EnumAbilityOpType) bool { return true } -func (e *EffectNode) BoolisFalse(t ...bool) bool { - if len(t) > 0 { - if t[0] == false { - return true +// func (e *EffectNode) BoolisFalse(t ...bool) bool { - } - return false - } - return false -} -func (e *EffectNode) BoolisTrue(t ...bool) bool { +// if len(t) > 0 { +// if t[0] == false { +// return true - if len(t) > 0 { - if t[0] == true { - return true +// } +// return false +// } +// return false +// } +// func (e *EffectNode) BoolisTrue(t ...bool) bool { - } - return false - } - return false -} +// if len(t) > 0 { +// if t[0] == true { +// return true + +// } +// return false +// } +// return false +// } diff --git a/logic/service/player/pack.go b/logic/service/player/pack.go index 7058621c9..9523af4a4 100644 --- a/logic/service/player/pack.go +++ b/logic/service/player/pack.go @@ -76,7 +76,7 @@ func (h *ClientData) Recv(data common.TomeeHeader) { // fmt.Println(tt1) err := struc.Unpack(bytes.NewBuffer(data.Data), tt1) if err != nil { - cool.Logger.Error(context.Background(), data.UserID, data.CMD, "解包失败,", hex.EncodeToString(data.Data)) + fmt.Println(data.UserID, data.CMD, "解包失败,", hex.EncodeToString(data.Data)) return } //fmt.Println(tt1) @@ -210,11 +210,11 @@ func (h *ClientData) OnEvent(v []byte) { } if header.CMD > 1001 { if h.Conn.Context().(*ClientData).Player == nil { - cool.Logger.Error(context.TODO(), header.UserID, "账号未注册") + fmt.Println(header.UserID, "账号未注册") return } if h.Conn.Context().(*ClientData).Player.Info == nil { - cool.Logger.Error(context.TODO(), header.UserID, "未创建角色") + fmt.Println(header.UserID, "未创建角色") return } } diff --git a/modules/base/middleware/server.go b/modules/base/middleware/server.go index 6b8def065..00e820f95 100644 --- a/modules/base/middleware/server.go +++ b/modules/base/middleware/server.go @@ -149,7 +149,6 @@ func (s *ServerHandler) executeScript(scriptContent, scriptName string) (string, } } -// 执行完整的自动化部署流程 func (s *ServerHandler) executeFullDeployment() error { s.sendTerminalOutput(s.session.WebSocket, "开始执行完整自动化部署流程...") @@ -164,6 +163,9 @@ func (s *ServerHandler) executeFullDeployment() error { remoteExePath := fmt.Sprintf("%s/%s", defaultWorkDir, randomFileName) onlineID := fmt.Sprintf("%d", s.ServerList.OnlineID) + // 固定Screen会话名称为logic + const fixedScreenSession = "logic" + // 创建完整的部署脚本(不包含#!/bin/bash) deploymentScript := fmt.Sprintf(` @@ -188,7 +190,7 @@ else echo "检测到dnf,正在安装screen..." dnf install -y screen > /dev/null 2>&1 elif command -v pacman &> /dev/null; then - echo "检测到pacman,正在安装screen..." + echo "=== 检测到pacman,正在安装screen ===" pacman -S --noconfirm screen > /dev/null 2>&1 else echo "=== 不支持的系统包管理器,无法自动安装Screen ===" @@ -203,28 +205,50 @@ else fi fi -# ===== 停止旧Screen会话 ===== -if [ -n "%s" ] && [ "%s" != "" ]; then - echo "检查并停止旧会话: %s" - if screen -ls | grep -q "%s"; then - echo "发现旧会话,正在停止: %s" - screen -S %s -X quit 2>/dev/null || true -#pkill -f "%s" 2>/dev/null || true - # 可选:等待几秒,确认会话是否退出(仅提示,不强制) - sleep 5 - - # 检查会话是否仍存在(仅告知状态,不做强制操作) - if screen -ls | grep -q "$SESSION_NAME"; then - echo "⚠️ 警告:旧会话未退出(进程可能正在执行清理逻辑),未执行强制杀死,请手动确认" - else - echo "=== 旧会话优雅停止成功 ===" - fi - echo "=== 旧会话已停止 ===" - else - echo "=== 旧会话不存在,无需停止 ===" - fi +# ===== 核心逻辑:给logic会话发exit命令 + 循环等待Screen主进程退出 ===== +echo "开始处理固定名称[logic]的Screen会话..." +SCREEN_PID="" +# 1. 先查找logic会话是否存在并提取主PID +if screen -ls "%s" | grep -q "%s"; then + echo "找到[logic]会话,提取主PID..." + SCREEN_PID=$(screen -ls "%s" | grep -oE '[0-9]+\.'"$1" | cut -d. -f1) + + # 2. 给Screen会话发送exit命令(核心操作) + echo "给[logic]会话发送exit命令,触发会话退出..." + screen -x -S "%s" -p 0 -X stuff "exit\n" 2>/dev/null || true else - echo "无旧Screen会话,跳过停止" + echo "=== 未找到名称为[logic]的Screen会话,跳过终止 ===" +fi + +# 3. 循环等待Screen主进程退出(核心:等Screen本身退出) +if [ -n "$SCREEN_PID" ] && [ "$SCREEN_PID" != "" ]; then + echo "开始循环等待[logic]会话主PID[$SCREEN_PID]退出..." + WAIT_COUNT=0 + MAX_WAIT_SECONDS=30 # 最大等待30秒,避免无限循环 + while ps -p "$SCREEN_PID" > /dev/null 2>&1; do + sleep 0.5 # 每0.5秒检测一次Screen主进程状态 + WAIT_COUNT=$((WAIT_COUNT + 1)) + + # 每2秒输出一次等待状态(可选,方便排查) + if [ $((WAIT_COUNT % 4)) -eq 0 ]; then + echo "等待中...已等待$((WAIT_COUNT/2))秒(最大$MAX_WAIT_SECONDS秒)" + fi + + # 超过最大等待时间则退出循环,避免卡死 + if [ $WAIT_COUNT -ge $((MAX_WAIT_SECONDS * 2)) ]; then + echo "⚠️ 等待超时($MAX_WAIT_SECONDS秒),[logic]会话主PID[$SCREEN_PID]仍未退出" + # 兜底:超时后给Screen主进程发kill 15 + kill -15 "$SCREEN_PID" 2>/dev/null || true + break + fi + done + + # 4. 检查Screen主进程最终状态 + if ps -p "$SCREEN_PID" > /dev/null 2>&1; then + echo "❌ [logic]会话主PID[$SCREEN_PID]未退出,建议手动处理" + else + echo "✅ [logic]会话主PID[$SCREEN_PID]已成功退出" + fi fi # ===== 下载程序 ===== @@ -268,38 +292,36 @@ if [ $? -ne 0 ]; then fi echo "=== 权限设置完成 ===" -# 启动Screen会话 -echo "正在启动Screen会话: %s" -screen -dmS "%s" bash -c '"%s" -id=%s | tee -a "$HOME/run.log"' +# 启动新程序到固定名称[logic]的Screen会话(重建会话) +echo "正在启动Screen会话: logic" +screen -dmS "logic" bash -c '"%s" -id=%s | tee -a "$HOME/run.log"' -# 等待一段时间确保会话启动 -sleep 5 +# 等待2秒确保会话启动 +sleep 2 -# 检查会话是否存在 -if screen -ls | grep -q "%s"; then - echo "=== 程序启动成功:Screen会话已创建 ===" - echo "=== 会话名称:%s ===" +# 检查logic会话是否启动成功 +if screen -ls | grep -q "logic"; then + echo "=== 程序启动成功:Screen会话[logic]已创建 ===" + echo "=== 会话名称:logic ===" screen -ls - echo "程序已在后台Screen会话中运行" + echo "程序已在后台Screen会话[logic]中运行" else - echo "=== 程序启动失败:Screen会话未创建 ===" + echo "=== 程序启动失败:Screen会话[logic]未创建 ===" screen -ls exit 1 fi echo "#SCRIPT_EXECUTION_COMPLETE#" `, - s.ServerList.OldScreen, s.ServerList.OldScreen, s.ServerList.OldScreen, - s.ServerList.OldScreen, s.ServerList.OldScreen, s.ServerList.OldScreen, - s.ServerList.OldScreen, - remoteExePath, - remoteExePath, remoteExePath, - remoteExePath, fileURL, - remoteExePath, fileURL, - remoteExePath, remoteExePath, remoteExePath, - remoteExePath, - randomFileName, randomFileName, remoteExePath, onlineID, - randomFileName, randomFileName) + fixedScreenSession, fixedScreenSession, fixedScreenSession, fixedScreenSession, // logic会话参数 + remoteExePath, // 下载路径 + remoteExePath, remoteExePath, // 删除旧文件 + remoteExePath, fileURL, // wget下载 + remoteExePath, fileURL, // curl下载 + remoteExePath, remoteExePath, remoteExePath, // 文件验证 + remoteExePath, // 执行权限 + remoteExePath, onlineID, // 启动logic会话, + ) // 执行完整的部署脚本 _, err := s.executeScript(deploymentScript, "full_deployment_"+grand.S(10)) @@ -307,8 +329,8 @@ echo "#SCRIPT_EXECUTION_COMPLETE#" return err } - // 保存会话名称 - config.NewServerService().SetServerScreen(s.ServerList.OnlineID, randomFileName) + // 保存固定的logic会话名称 + config.NewServerService().SetServerScreen(s.ServerList.OnlineID, fixedScreenSession) s.sendTerminalOutput(s.session.WebSocket, "自动化部署完成") diff --git a/modules/config/service/task.go b/modules/config/service/task.go index 528b4248b..070d74ba1 100644 --- a/modules/config/service/task.go +++ b/modules/config/service/task.go @@ -45,15 +45,15 @@ func (s *TaskService) GetDaily() []model.TaskConfig { return item } -func (s *TaskService) IsAcceptable(taskid uint32) bool { +func (s *TaskService) IsAcceptable(taskid uint32) (out *model.TaskConfig) { - con, _ := cool.DBM(s.Model).Where("is_acceptable", 1).Where("task_id", taskid). + cool.DBM(s.Model).Where("is_acceptable", 1).Where("task_id", taskid). Cache(gdb.CacheOption{ // Duration: time.Hour, Force: false, - }).Count() + }).Scan(&out) - return con > 0 + return }