#!/bin/bash # ===================================================== # 一键开新区脚本 — 生成独立区服目录 # 用法: ./create_zone.sh <区号> [选项] # # 生成目录结构: # dev/server/ # ├── _launch_server.xml # 该区独立配置 # ├── start.sh # 启动 # ├── stop.sh # 停止 # ├── status.sh # 状态 # └── restart.sh # 重启 # ===================================================== set -e # ======================== 默认配置 ======================== SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BASE_OUTPUT="/data/CzServer/_output.server" DLL="$BASE_OUTPUT/OpenCards.Server.DotNetCore.dll" # 数据库密码(从现有配置读取) REDIS_PASSWORD="tmKjD1ENs3HfZ7adzLJO!" MYSQL_CONN="server=127.0.0.1;User ID=root;Password=CmASfW98lOKbFieqEQox;database=orm" NAMESERVER_ENDPOINT="127.0.0.1:17000" FIGHT_POST_URL="http://127.0.0.1:8088/fight/reqstartbattle" # 服务器公网IP SERVER_PUBLIC_IP="${SERVER_PUBLIC_IP:-127.0.0.1}" # ======================== 颜色 ======================== RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_step() { echo -e "${CYAN}[STEP]${NC} $1"; } # ======================== 参数 ======================== ZONE_ID="" ZONE_NAME="" CONNECTOR_PORT="" RPC_PORT="" REDIS_DB="" SERVER_ID="" REALM_ID="1" while [[ $# -gt 0 ]]; do case "$1" in --port) CONNECTOR_PORT="$2"; shift 2 ;; --rpc-port) RPC_PORT="$2"; shift 2 ;; --name) ZONE_NAME="$2"; shift 2 ;; --ip) SERVER_PUBLIC_IP="$2"; shift 2 ;; --redis-db) REDIS_DB="$2"; shift 2 ;; --realm) REALM_ID="$2"; shift 2 ;; --nameserver) NAMESERVER_ENDPOINT="$2"; shift 2 ;; -h|--help) echo "用法: $0 <区号> [选项]" echo "" echo "参数:" echo " 区号 第几区 (1, 2, 3...)" echo "" echo "选项:" echo " --port <端口> 客户端连接端口 (默认: 19820+区号)" echo " --rpc-port <端口> RPC端口 (默认: rpc=17019+区号)" echo " --name <名称> 区服显示名称 (默认: 测试服N区)" echo " --ip 服务器公网IP (用于serverlist)" echo " --redis-db <编号> Redis DB (默认: 同区号)" echo " --realm 大区ID (默认: 1)" echo " --nameserver <地址> NameServer地址 (默认: 127.0.0.1:17000)" echo "" echo "示例:" echo " $0 1 # 创建1区 (端口19821)" echo " $0 2 # 创建2区 (端口19822)" echo " $0 3 --port 19823 --name 正式3区" exit 0 ;; *) ZONE_ID="$1"; shift ;; esac done if [ -z "$ZONE_ID" ]; then log_error "请指定区号,如: $0 2" exit 1 fi # 自动计算默认值 SERVER_ID="${ZONE_ID}" REDIS_DB="${REDIS_DB:-${ZONE_ID}}" CONNECTOR_PORT="${CONNECTOR_PORT:-$((19820 + ZONE_ID))}" RPC_PORT="${RPC_PORT:-$((17019 + ZONE_ID))}" ZONE_NAME="${ZONE_NAME:-测试服${ZONE_ID}区}" # ======================== 预检查 ======================== log_step "检查环境..." ZONE_DIR="${SCRIPT_DIR}/server${ZONE_ID}" GAME_NODE="GameNode${ZONE_ID}" if [ -d "$ZONE_DIR" ]; then log_warn "目录 ${ZONE_DIR} 已存在" read -p "是否覆盖? (y/N): " confirm if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then log_info "已取消" exit 0 fi fi check_port() { local port=$1 (ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null) | grep -q ":${port} " && return 0 return 1 } if check_port "$CONNECTOR_PORT"; then log_warn "端口 ${CONNECTOR_PORT} 已被占用,请检查" fi if check_port "$RPC_PORT"; then log_warn "RPC端口 ${RPC_PORT} 已被占用,请检查" fi # ======================== 创建目录 ======================== log_step "创建目录: ${ZONE_DIR}" mkdir -p "${ZONE_DIR}" # ======================== 生成 _launch_server.xml ======================== log_step "生成 _launch_server.xml ..." cat > "${ZONE_DIR}/_launch_server.xml" << XEOF dev ${REALM_ID} 0.0.0 ../../../data/ServerData/ https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=d9d262b8-17c8-4297-8b9f-bb1b7f72ac89 true https://zqtfcn.oss-cn-shanghai.aliyuncs.com/apollo_dev/server/playback/ ${NAMESERVER_ENDPOINT} 15000 OpenCards.Server.Core.Serializer <${GAME_NODE}> 127.0.0.1,password=${REDIS_PASSWORD},allowAdmin=true,syncTimeout=30000,responseTimeout=30000,connectRetry=1000,connectTimeout=10000;db=${REDIS_DB} ${MYSQL_CONN} 127.0.0.1 GameNode ${GAME_NODE} 127.0.0.1:${RPC_PORT} ${NAMESERVER_ENDPOINT} 5000 30000 60000 OpenCards.Server.Core.Serializer OpenCards.Service.Center.CenterService OpenCards.Server.Account.AccountServer OpenCards.Server.Connector.ConnectorService OpenCards.Server.Arena.ArenaManagerService OpenCards.Server.Arena.ArenaValorService OpenCards.Server.Arena.ArenaHighendService OpenCards.Server.Connector.SessionService OpenCards.Server.Logic.LogicService OpenCards.Server.Logic.LogicManagerService OpenCards.Service.StageRank.StageRankService OpenCards.Server.Arena.ArenaPinnacleGameService LogicManagerService_${ZONE_ID} LogicManagerService ${FIGHT_POST_URL} ${SERVER_ID} CenterService_${ZONE_ID} CenterService ${SERVER_ID} StageRankService_${ZONE_ID} StageRankService ${SERVER_ID} ArenaPinnacleGameService_${ZONE_ID} ArenaPinnacleGameService ${SERVER_ID} ArenaManagerService_${ZONE_ID} ArenaManagerService ${SERVER_ID} ArenaHighendService_${ZONE_ID} ArenaHighendService ConnectorService_${ZONE_ID} ConnectorService ${SERVER_ID} 0.0.0.0 ${CONNECTOR_PORT} OpenCards.Core.Serializer true 30000 16384 16384 300000 XEOF # ======================== 生成管理脚本 ======================== log_step "生成管理脚本..." # --- 公共工具函数 --- write_script_header() { local file=$1 cat > "$file" << 'SHEOF' #!/bin/bash SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BASE_OUTPUT="/data/CzServer/_output.server" DLL="$BASE_OUTPUT/OpenCards.Server.DotNetCore.dll" LOG_DIR="$BASE_OUTPUT/logfile" mkdir -p "$LOG_DIR" GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; NC='\033[0m' SHEOF } # --- start.sh --- write_script_header "${ZONE_DIR}/start.sh" cat >> "${ZONE_DIR}/start.sh" << XEOF # ${ZONE_NAME} 启动脚本 PID=\$(pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" 2>/dev/null) if [ -n "\$PID" ]; then echo -e "\${YELLOW}[SKIP] ${ZONE_NAME} 已在运行 (PID: \$PID)\${NC}" exit 0 fi echo -e "\${GREEN}[START] ${ZONE_NAME} (端口:${CONNECTOR_PORT}, RPC:${RPC_PORT}, Redis:db=${REDIS_DB}) ...\${NC}" nohup dotnet "\$DLL" "\$SCRIPT_DIR/_launch_server.xml" ${GAME_NODE} global.RealmID=${REALM_ID} global.ServerID=${SERVER_ID} \ >> "\$LOG_DIR/server${ZONE_ID}.log" 2>&1 & sleep 3 PID=\$(pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" 2>/dev/null) if [ -n "\$PID" ]; then echo -e "\${GREEN}[OK] ${ZONE_NAME} 启动成功 (PID: \$PID)\${NC}" else echo -e "\${RED}[FAIL] ${ZONE_NAME} 启动失败,查看日志:\${NC}" tail -20 "\$LOG_DIR/server${ZONE_ID}.log" exit 1 fi XEOF # --- stop.sh --- write_script_header "${ZONE_DIR}/stop.sh" cat >> "${ZONE_DIR}/stop.sh" << XEOF # ${ZONE_NAME} 停止脚本 PID=\$(pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" 2>/dev/null) if [ -n "\$PID" ]; then echo -e "\${YELLOW}[STOP] ${ZONE_NAME} (PID: \$PID)\${NC}" kill \$PID sleep 2 if pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" > /dev/null 2>&1; then echo -e "\${RED}[KILL] 强制终止...\${NC}" kill -9 \$PID 2>/dev/null fi echo -e "\${GREEN}[OK] ${ZONE_NAME} 已停止\${NC}" else echo -e "\${YELLOW}[SKIP] ${ZONE_NAME} 未在运行\${NC}" fi XEOF # --- status.sh --- write_script_header "${ZONE_DIR}/status.sh" cat >> "${ZONE_DIR}/status.sh" << XEOF # ${ZONE_NAME} 状态查看 PID=\$(pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" 2>/dev/null) if [ -n "\$PID" ]; then echo -e "\${GREEN}[UP] ${ZONE_NAME} 运行中" echo " PID: \$PID" echo " 端口: ${CONNECTOR_PORT}" echo " RPC: ${RPC_PORT}" echo " 日志: \$LOG_DIR/server${ZONE_ID}.log\${NC}" else echo -e "\${RED}[DOWN] ${ZONE_NAME} 未运行 (端口:${CONNECTOR_PORT})\${NC}" fi XEOF # --- restart.sh --- write_script_header "${ZONE_DIR}/restart.sh" cat >> "${ZONE_DIR}/restart.sh" << XEOF # ${ZONE_NAME} 重启脚本 bash "\$SCRIPT_DIR/stop.sh" sleep 2 bash "\$SCRIPT_DIR/start.sh" XEOF chmod +x "${ZONE_DIR}/start.sh" "${ZONE_DIR}/stop.sh" "${ZONE_DIR}/status.sh" "${ZONE_DIR}/restart.sh" # ======================== 输出 serverlist 条目 ======================== cat << XEOF ${GREEN}============================================ 请将以下条目添加到 OSS serverlist.json: ============================================${NC} { "id": ${ZONE_ID}, "index": $((ZONE_ID - 1)), "name": "${ZONE_NAME}", "address": "${SERVER_PUBLIC_IP}:${CONNECTOR_PORT}", "state": 1, "is_open": true, "capacity": 2000, "serverid": ${SERVER_ID}, "groupid": ${REALM_ID}, "note": "测试服" } ${GREEN}============================================${NC} XEOF # ======================== 完成 ======================== echo "" echo -e "${GREEN}============================================${NC}" echo -e "${GREEN} ${ZONE_NAME} 创建完成!${NC}" echo -e "${GREEN}============================================${NC}" echo "" echo -e " 目录: ${CYAN}${ZONE_DIR}${NC}" echo -e " 区号: ${CYAN}${SERVER_ID}${NC}" echo -e " 端口: ${CYAN}${CONNECTOR_PORT}${NC}" echo -e " RPC端口: ${CYAN}${RPC_PORT}${NC}" echo -e " Redis: ${CYAN}db=${REDIS_DB}${NC}" echo "" echo -e " 管理:" echo -e " ${CYAN}bash ${ZONE_DIR}/start.sh${NC} 启动" echo -e " ${CYAN}bash ${ZONE_DIR}/stop.sh${NC} 停止" echo -e " ${CYAN}bash ${ZONE_DIR}/status.sh${NC} 状态" echo -e " ${CYAN}bash ${ZONE_DIR}/restart.sh${NC} 重启" echo -e " ${CYAN}tail -f ${BASE_OUTPUT}/logfile/server${ZONE_ID}.log${NC} 日志" echo "" echo -e " ${YELLOW}⚠ 别忘了:${NC}" echo -e " 1. 更新 OSS serverlist.json(见上方)" echo -e " 2. 防火墙放行端口 ${CONNECTOR_PORT} 和 ${RPC_PORT}" echo ""