From 53be2cb7768d6547701a389ee69b9b805b9f77ab Mon Sep 17 00:00:00 2001 From: 1 <1@72wo.cn> Date: Fri, 23 Jan 2026 14:59:15 +0000 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/cool/global.go | 3 +- common/utils/tcpping.go | 5 +- logic/controller/Controller.go | 12 +- logic/service/player/pack.go | 2 +- logic/service/player/player.go | 6 +- logic/service/player/server.go | 1 + logic/service/space/space.go | 3 +- modules/base/middleware/server.go | 218 +++++++++++++++--------------- 8 files changed, 129 insertions(+), 121 deletions(-) diff --git a/common/cool/global.go b/common/cool/global.go index 1c1a19860..3a1dad412 100644 --- a/common/cool/global.go +++ b/common/cool/global.go @@ -25,9 +25,8 @@ type Cmd struct { //Res reflect.Value //返回体 } +var CmdCache = make(map[uint32]Cmd, 0) var ( - CmdCache = &utils.SyncMap[uint32, Cmd]{} //命令缓存 - Logger = glog.New() Cron = cronex.New() //时间轮 diff --git a/common/utils/tcpping.go b/common/utils/tcpping.go index ac1c268cd..958cf51a5 100644 --- a/common/utils/tcpping.go +++ b/common/utils/tcpping.go @@ -8,7 +8,10 @@ import ( func TcpPing(address string) (n int64, err error) { s := time.Now() tcpConn, err := net.Dial("tcp", address) - n = time.Now().Sub(s).Nanoseconds() + n = time.Now().Sub(s).Milliseconds() + if n == 0 { + n = 1 + } if err != nil { return } diff --git a/logic/controller/Controller.go b/logic/controller/Controller.go index 93c14096f..6b0b9f047 100644 --- a/logic/controller/Controller.go +++ b/logic/controller/Controller.go @@ -47,7 +47,7 @@ func ParseCmd[T any](data []byte) T { func Init(isGame bool) { // 获取控制器实例的反射值 controllerValue := reflect.ValueOf(Maincontroller) - + // 获取控制器类型 controllerType := controllerValue.Type() @@ -86,11 +86,11 @@ func Init(isGame bool) { Req: methodValue.Type().In(0).Elem(), // Res: , // TODO 待实现对不同用户初始化方法以取消全局cmdcache } - - _, exists := cool.CmdCache.LoadOrStore(cmd, cmdInfo) - if exists { // 方法已存在 - glog.Error(context.Background(), "命令处理方法已存在,跳过注册", cmd, method.Name) - } + + cool.CmdCache[cmd] = cmdInfo + // if exists { // 方法已存在 + // glog.Error(context.Background(), "命令处理方法已存在,跳过注册", cmd, method.Name) + // } } } } diff --git a/logic/service/player/pack.go b/logic/service/player/pack.go index 9523af4a4..30ea806ef 100644 --- a/logic/service/player/pack.go +++ b/logic/service/player/pack.go @@ -58,7 +58,7 @@ func (h *ClientData) Recv(data common.TomeeHeader) { } }() - cmdlister, ok := cool.CmdCache.Load(data.CMD) + cmdlister, ok := cool.CmdCache[data.CMD] if !ok { glog.Debug(context.Background(), data.UserID, data.CMD, "cmd未注册") diff --git a/logic/service/player/player.go b/logic/service/player/player.go index 62a1f5444..159314fe2 100644 --- a/logic/service/player/player.go +++ b/logic/service/player/player.go @@ -3,7 +3,6 @@ package player import ( "blazing/common/data" "blazing/common/socket/errorcode" - "blazing/common/utils" "blazing/cool" "blazing/logic/service/common" "blazing/logic/service/fight/info" @@ -20,6 +19,7 @@ import ( "github.com/antlabs/timer" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" + csmap "github.com/mhmtszr/concurrent-swiss-map" "github.com/panjf2000/gnet/v2" ) @@ -34,7 +34,7 @@ func CountPlayer() int { } // Mainplayer 全局玩家数据存储映射 -var Mainplayer = &utils.SyncMap[uint32, *Player]{} +var Mainplayer = csmap.New[uint32, *Player]() type OgrePetInfo struct { Id uint32 @@ -226,7 +226,7 @@ func (player1 *Player) Kick() { //取成功,否则创建 //player1.Save() //先保存数据再返回 head := common.NewTomeeHeader(1001, player1.Info.UserID) - head.Result = uint32(errorcode.ErrorCodes.ErrAccountLoggedInElsewhere) + head.Result = uint32(errorcode.ErrorCodes.ErrXinPlanSleepMode) //实际上这里有个问题,会造成重复保存问题 player1.SendPack(head.Pack(nil)) diff --git a/logic/service/player/server.go b/logic/service/player/server.go index 918250590..4faa05a06 100644 --- a/logic/service/player/server.go +++ b/logic/service/player/server.go @@ -38,6 +38,7 @@ func KickPlayer(userid uint32) error { //踢出玩家 } + //return player return nil } diff --git a/logic/service/space/space.go b/logic/service/space/space.go index 13d6141c1..37ec7cf0f 100644 --- a/logic/service/space/space.go +++ b/logic/service/space/space.go @@ -2,7 +2,6 @@ package space import ( "blazing/common/data/xmlres" - "blazing/common/utils" "blazing/cool" "blazing/logic/service/common" @@ -139,4 +138,4 @@ func GetSpace(id uint32) *Space { return ret } -var planetmap = &utils.SyncMap[uint32, *Space]{} //玩家数据 +var planetmap = csmap.New[uint32, *Space]() diff --git a/modules/base/middleware/server.go b/modules/base/middleware/server.go index 3b064792d..0e1530a64 100644 --- a/modules/base/middleware/server.go +++ b/modules/base/middleware/server.go @@ -196,29 +196,58 @@ else command -v screen || { echo "❌ Screen安装失败"; exit 1; } fi +#!/bin/bash # ===== 优雅终止logic会话(先等内部程序退出 → 再退screen)===== echo "===== 优雅终止logic会话 =====" SCREEN_PID="" -# 你实际使用的screen名称(从日志看是logic) -SCREEN_NAME="%s{screen_name}" +# 替换为你实际的screen名称(示例:logic) +SCREEN_NAME="%s{screen_name}" +# 日志文件路径(可根据需要调整) +LOG_FILE="./screen_logic_exit.log" # 调试开关(如需详细日志,取消set -x注释) set -o pipefail export PS4='[DEBUG] ${BASH_SOURCE}:${LINENO} - ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' # set -x -# 定义:检查PID是否存活的函数 +# ========== 核心函数:基于kill -0的等待进程退出函数 ========== +# 等待进程结束(带60秒超时,用kill -0检测存活,输出进度点) +wait_for_process_exit() { + local pidKilled=$1 + local begin=$(date +%s) # 记录开始时间(秒) + local end + local timeout=60 # 最大等待时间(秒) + + # 循环检测进程是否存活 + while kill -0 $pidKilled > /dev/null 2>&1; do + echo -n "." # 输出进度点,直观显示等待过程 + sleep 1; + + # 检查是否超时 + end=$(date +%s) + if [ $((end - begin)) -gt $timeout ]; then + echo -e "\n⚠️ 等待进程$pidKilled退出超时(已等${timeout}秒)" + break; + fi + done + + # 最终状态提示 + if ! kill -0 $pidKilled > /dev/null 2>&1; then + echo -e "\n✅ 进程$pidKilled已退出" + fi +} + +# 定义:检查PID是否存活的函数(复用kill -0逻辑) pid_is_alive() { local pid=$1 - # 仅检查PID是否存在,不发送信号(最安全的方式) - if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then + if [ -n "$pid" ] && kill -0 "$pid" > /dev/null 2>&1; then return 0 # PID存活 else return 1 # PID不存在 fi } -# 定义安全的进程检测函数(带超时防卡) +# 定义:获取screen会话下的所有子进程PID get_inner_procs() { local screen_pid=$1 # 5秒超时,避免pstree卡住 @@ -230,113 +259,90 @@ get_inner_procs() { echo "$procs" } -# 1. 检查logic会话是否存在 -if screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; then - echo "找到$SCREEN_NAME会话,提取主PID..." - SCREEN_PID=$(screen -ls "$SCREEN_NAME" | grep -oE '[0-9]+\.'"$SCREEN_NAME" | head -1 | cut -d. -f1) - - if [ -z "$SCREEN_PID" ]; then - echo "⚠️ 提取$SCREEN_NAME会话PID失败" - else - echo "✅ 提取到$SCREEN_NAME会话主PID:$SCREEN_PID" - - # ========== 步骤1:给screen内所有子进程发优雅退出信号 ========== - echo "给$SCREEN_NAME内所有程序发送优雅退出信号(SIGTERM)..." - INNER_ALL_PROCS=$(get_inner_procs "$SCREEN_PID") - - if [ -n "$INNER_ALL_PROCS" ]; then - echo "📌 检测到screen内进程列表:$INNER_ALL_PROCS" - for pid in $INNER_ALL_PROCS; do - # 发送信号前检查子进程是否存活 - if pid_is_alive "$pid"; then - if kill -15 "$pid" 2>/dev/null; then - echo "✅ 已给进程$pid发送SIGTERM信号" - else - echo "⚠️ 进程$pid发送信号失败" - fi - else - echo "ℹ️ 进程$pid已不存在,跳过发送信号" - fi - done - else - echo "ℹ️ 未检测到$SCREEN_NAME内的子进程" - fi +# 第一步:提取screen主PID并校验 +SCREEN_PID=$(screen -ls "$SCREEN_NAME" | grep -oE '[0-9]+\.'"$SCREEN_NAME" | head -1 | cut -d. -f1) +if [ -z "$SCREEN_PID" ]; then + echo "ℹ️ 未找到$SCREEN_NAME会话对应的PID,跳过终止流程" + # 去掉exit 1,直接跳过后续逻辑,不影响其他脚本执行 +else + # ========== 仅当找到PID时,才执行以下终止流程 ========== + echo "✅ 找到$SCREEN_NAME,主PID:$SCREEN_PID" - # 兜底:给screen内Shell发送exit指令 - echo "给screen内Shell发送exit指令(兜底)..." - screen -S "$SCREEN_NAME" -p 0 -X stuff $'exit\n' 2>/dev/null - sleep 1 - - # ========== 步骤2:循环等待内部程序退出(防卡优化) ========== - echo "开始循环等待$SCREEN_NAME内部所有程序退出(最大60秒)..." - WAIT_COUNT=0 - MAX_WAIT_SECONDS=60 - INNER_PROC_EXIST=true - - while [ "$INNER_PROC_EXIST" = true ] && [ $WAIT_COUNT -lt $MAX_WAIT_SECONDS ]; do - INNER_PROCS=$(get_inner_procs "$SCREEN_PID") - - # 强制退出条件:即使进程检测失败也不卡住 - if [ -z "$INNER_PROCS" ]; then - INNER_PROC_EXIST=false - echo "✅ $SCREEN_NAME内部所有程序已退出(或进程检测完成)" - else - sleep 1 - WAIT_COUNT=$((WAIT_COUNT + 1)) - - # 每5秒输出状态 - if [ $((WAIT_COUNT % 5)) -eq 0 ]; then - ELAPSED=$WAIT_COUNT - echo "⏳ 等待中...残留进程:$INNER_PROCS(已等$ELAPSED秒,剩余$((MAX_WAIT_SECONDS - ELAPSED))秒)" + # 第二步:导出【退出前】的实时日志(终端+文件双输出) + echo -e "\n===== 【退出前】$SCREEN_NAME 内程序实时log =====" + screen -S "$SCREEN_NAME" -p 0 -X hardcopy -h "$LOG_FILE" 2>/dev/null + cat "$LOG_FILE" + + # 第三步:给所有子进程发SIGTERM信号,并等待进程退出 + echo -e "\n===== 开始给所有子进程发优雅退出信号(SIGTERM) =====" + INNER_PROCS=$(get_inner_procs "$SCREEN_PID") + if [ -z "$INNER_PROCS" ]; then + echo "ℹ 未检测到$SCREEN_NAME下的子进程" + else + echo "待处理子进程:$INNER_PROCS" + for pid in $INNER_PROCS; do + if pid_is_alive "$pid"; then + echo -n "📌 终止进程$pid并等待退出:" + kill -15 "$pid" # 发送SIGTERM优雅退出信号 + if [ $? -eq 0 ]; then + wait_for_process_exit "$pid" # 调用等待函数 + else + echo "❌ 进程$pid:发送SIGTERM失败" fi + else + echo "ℹ 进程$pid:已不存在,跳过" fi done - - # 超时提示 - if [ "$INNER_PROC_EXIST" = true ]; then - echo "⚠️ 等待超时(60秒),$SCREEN_NAME内部程序仍未退出" - echo "📌 残留进程PID:$INNER_PROCS" - fi - - # ========== 步骤3:退出screen会话(核心修复:避免kill卡住) ========== - if [ "$INNER_PROC_EXIST" = false ]; then - echo "内部程序已退出,开始退出$SCREEN_NAME会话..." - # 第一步:尝试正常退出screen会话 - if screen -S "$SCREEN_NAME" -X quit 2>/dev/null; then - echo "✅ $SCREEN_NAME会话已通过screen -X quit退出" - else - echo "ℹ️ screen -X quit执行失败(会话可能已消失),检查PID是否存活..." - # 第二步:仅当PID存活时,才执行kill,且加超时 - if pid_is_alive "$SCREEN_PID"; then - echo "📌 PID $SCREEN_PID 仍存活,尝试kill终止(5秒超时)..." - timeout 5 kill -15 "$SCREEN_PID" 2>/dev/null - if [ $? -eq 0 ]; then - echo "✅ 已给screen主进程$SCREEN_PID发送SIGTERM信号" - else - echo "⚠️ kill $SCREEN_PID 失败(超时/进程不存在)" - fi - else - echo "ℹ️ PID $SCREEN_PID 已不存在,跳过kill操作" - fi - fi - sleep 2 - else - echo "⚠️ 内部程序未完全退出,跳过退出screen会话" - fi - - # 最终验证 - if screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; then - echo "❌ $SCREEN_NAME会话最终仍未退出" - else - echo "✅ $SCREEN_NAME会话已完全退出" - fi fi -else - echo "=== 未找到$SCREEN_NAME会话,跳过终止 ===" + + # 验证子进程是否全部退出 + echo -e "\n===== 验证子进程退出状态 =====" + REMAIN_PROCS=$(get_inner_procs "$SCREEN_PID") + INNER_PROC_EXIST=true + if [ -z "$REMAIN_PROCS" ]; then + INNER_PROC_EXIST=false + echo "✅ $SCREEN_NAME内部所有程序已退出" + else + echo "⚠️ 仍有残留进程:$REMAIN_PROCS" + fi + + # 退出screen会话 + if [ "$INNER_PROC_EXIST" = false ]; then + echo "内部程序已退出,开始退出$SCREEN_NAME会话..." + # 尝试正常退出screen会话 + if screen -S "$SCREEN_NAME" -X quit 2>/dev/null; then + echo "✅ $SCREEN_NAME会话已通过screen -X quit退出" + else + echo "ℹ️ screen -X quit执行失败,检查PID是否存活..." + # 仅当PID存活时,执行kill + if pid_is_alive "$SCREEN_PID"; then + echo "📌 PID $SCREEN_PID 仍存活,尝试kill终止..." + timeout 5 kill -15 "$SCREEN_PID" 2>/dev/null + if [ $? -eq 0 ]; then + echo "✅ 已给screen主进程$SCREEN_PID发送SIGTERM信号" + else + echo "⚠️ kill $SCREEN_PID 失败" + fi + else + echo "ℹ️ PID $SCREEN_PID 已不存在,跳过kill操作" + fi + fi + sleep 2 + else + echo "⚠️ 内部程序未完全退出,跳过退出screen会话" + fi + + # 最终验证 + echo -e "\n===== 最终验证 =====" + if screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; then + echo "❌ $SCREEN_NAME会话最终仍未退出" + else + echo "✅ $SCREEN_NAME会话已完全退出" + fi fi -# 关闭调试 -# set +x +# 后续脚本可以从这里继续执行,不受上述逻辑影响 +echo -e "\n===== 终止logic会话流程结束,继续执行后续脚本 =====" # ===== 准备下载目录 ===== echo "创建工作目录:%s{work_dir}"