create_zone.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. #!/bin/bash
  2. # =====================================================
  3. # 一键开新区脚本 — 生成独立区服目录
  4. # 用法: ./create_zone.sh <区号> [选项]
  5. #
  6. # 生成目录结构:
  7. # dev/server<N>/
  8. # ├── _launch_server.xml # 该区独立配置
  9. # ├── start.sh # 启动
  10. # ├── stop.sh # 停止
  11. # ├── status.sh # 状态
  12. # └── restart.sh # 重启
  13. # =====================================================
  14. set -e
  15. # ======================== 默认配置 ========================
  16. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  17. BASE_OUTPUT="/data/CzServer/_output.server"
  18. DLL="$BASE_OUTPUT/OpenCards.Server.DotNetCore.dll"
  19. # 数据库密码(从现有配置读取)
  20. REDIS_PASSWORD="tmKjD1ENs3HfZ7adzLJO!"
  21. MYSQL_CONN="server=127.0.0.1;User ID=root;Password=CmASfW98lOKbFieqEQox;database=orm"
  22. NAMESERVER_ENDPOINT="127.0.0.1:17000"
  23. FIGHT_POST_URL="http://127.0.0.1:8088/fight/reqstartbattle"
  24. # 服务器公网IP
  25. SERVER_PUBLIC_IP="${SERVER_PUBLIC_IP:-127.0.0.1}"
  26. # ======================== 颜色 ========================
  27. RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
  28. log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
  29. log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
  30. log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
  31. log_step() { echo -e "${CYAN}[STEP]${NC} $1"; }
  32. # ======================== 参数 ========================
  33. ZONE_ID=""
  34. ZONE_NAME=""
  35. CONNECTOR_PORT=""
  36. RPC_PORT=""
  37. REDIS_DB=""
  38. SERVER_ID=""
  39. REALM_ID="1"
  40. while [[ $# -gt 0 ]]; do
  41. case "$1" in
  42. --port) CONNECTOR_PORT="$2"; shift 2 ;;
  43. --rpc-port) RPC_PORT="$2"; shift 2 ;;
  44. --name) ZONE_NAME="$2"; shift 2 ;;
  45. --ip) SERVER_PUBLIC_IP="$2"; shift 2 ;;
  46. --redis-db) REDIS_DB="$2"; shift 2 ;;
  47. --realm) REALM_ID="$2"; shift 2 ;;
  48. --nameserver) NAMESERVER_ENDPOINT="$2"; shift 2 ;;
  49. -h|--help)
  50. echo "用法: $0 <区号> [选项]"
  51. echo ""
  52. echo "参数:"
  53. echo " 区号 第几区 (1, 2, 3...)"
  54. echo ""
  55. echo "选项:"
  56. echo " --port <端口> 客户端连接端口 (默认: 19820+区号)"
  57. echo " --rpc-port <端口> RPC端口 (默认: rpc=17019+区号)"
  58. echo " --name <名称> 区服显示名称 (默认: 测试服N区)"
  59. echo " --ip <IP地址> 服务器公网IP (用于serverlist)"
  60. echo " --redis-db <编号> Redis DB (默认: 同区号)"
  61. echo " --realm <ID> 大区ID (默认: 1)"
  62. echo " --nameserver <地址> NameServer地址 (默认: 127.0.0.1:17000)"
  63. echo ""
  64. echo "示例:"
  65. echo " $0 1 # 创建1区 (端口19821)"
  66. echo " $0 2 # 创建2区 (端口19822)"
  67. echo " $0 3 --port 19823 --name 正式3区"
  68. exit 0
  69. ;;
  70. *) ZONE_ID="$1"; shift ;;
  71. esac
  72. done
  73. if [ -z "$ZONE_ID" ]; then
  74. log_error "请指定区号,如: $0 2"
  75. exit 1
  76. fi
  77. # 自动计算默认值
  78. SERVER_ID="${ZONE_ID}"
  79. REDIS_DB="${REDIS_DB:-${ZONE_ID}}"
  80. CONNECTOR_PORT="${CONNECTOR_PORT:-$((19820 + ZONE_ID))}"
  81. RPC_PORT="${RPC_PORT:-$((17019 + ZONE_ID))}"
  82. ZONE_NAME="${ZONE_NAME:-测试服${ZONE_ID}区}"
  83. # ======================== 预检查 ========================
  84. log_step "检查环境..."
  85. ZONE_DIR="${SCRIPT_DIR}/server${ZONE_ID}"
  86. GAME_NODE="GameNode${ZONE_ID}"
  87. if [ -d "$ZONE_DIR" ]; then
  88. log_warn "目录 ${ZONE_DIR} 已存在"
  89. read -p "是否覆盖? (y/N): " confirm
  90. if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
  91. log_info "已取消"
  92. exit 0
  93. fi
  94. fi
  95. check_port() {
  96. local port=$1
  97. (ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null) | grep -q ":${port} " && return 0
  98. return 1
  99. }
  100. if check_port "$CONNECTOR_PORT"; then
  101. log_warn "端口 ${CONNECTOR_PORT} 已被占用,请检查"
  102. fi
  103. if check_port "$RPC_PORT"; then
  104. log_warn "RPC端口 ${RPC_PORT} 已被占用,请检查"
  105. fi
  106. # ======================== 创建目录 ========================
  107. log_step "创建目录: ${ZONE_DIR}"
  108. mkdir -p "${ZONE_DIR}"
  109. # ======================== 生成 _launch_server.xml ========================
  110. log_step "生成 _launch_server.xml ..."
  111. cat > "${ZONE_DIR}/_launch_server.xml" << XEOF
  112. <?xml version="1.0" encoding="utf-8"?>
  113. <doc>
  114. <!-- ================================================================ -->
  115. <!-- ${ZONE_NAME} 配置 (由 create_zone.sh 生成) -->
  116. <!-- 区号: ${ZONE_ID} 端口: ${CONNECTOR_PORT} RPC: ${RPC_PORT} -->
  117. <!-- Redis: db=${REDIS_DB} 生成时间: $(date '+%Y-%m-%d %H:%M:%S') -->
  118. <!-- ================================================================ -->
  119. <define name="GameNodeDefine" param1="" param2=""/>
  120. <GlobalConfig>
  121. <Env>dev</Env>
  122. <Realm>${REALM_ID}</Realm>
  123. <Mysql></Mysql>
  124. <AcceptClientVersion>0.0.0</AcceptClientVersion>
  125. <TemplateRoot>../../../data/ServerData/</TemplateRoot>
  126. <NoticeErrorUrl>https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=d9d262b8-17c8-4297-8b9f-bb1b7f72ac89</NoticeErrorUrl>
  127. <DebugPay>true</DebugPay>
  128. <BattleRecordURLPrefix>https://zqtfcn.oss-cn-shanghai.aliyuncs.com/apollo_dev/server/playback/</BattleRecordURLPrefix>
  129. </GlobalConfig>
  130. <NameServer>
  131. <RpcConfig>
  132. <LocalEndPoint>${NAMESERVER_ENDPOINT}</LocalEndPoint>
  133. <NetworkTimeoutMS>15000</NetworkTimeoutMS>
  134. <RpcCodec>OpenCards.Server.Core.Serializer</RpcCodec>
  135. </RpcConfig>
  136. </NameServer>
  137. <ServiceNodes>
  138. <${GAME_NODE}>
  139. <Redis>127.0.0.1,password=${REDIS_PASSWORD},allowAdmin=true,syncTimeout=30000,responseTimeout=30000,connectRetry=1000,connectTimeout=10000;db=${REDIS_DB}</Redis>
  140. <Mysql>${MYSQL_CONN}</Mysql>
  141. <Ip>127.0.0.1</Ip>
  142. <RpcConfig>
  143. <LocalNodeType>GameNode</LocalNodeType>
  144. <LocalNodeName>${GAME_NODE}</LocalNodeName>
  145. <LocalEndPoint>127.0.0.1:${RPC_PORT}</LocalEndPoint>
  146. <NameServerEndPoint>${NAMESERVER_ENDPOINT}</NameServerEndPoint>
  147. <RequestTickTimeMS>5000</RequestTickTimeMS>
  148. <NetworkTimeoutMS>30000</NetworkTimeoutMS>
  149. <DefaultTaskExecuteTimeout>60000</DefaultTaskExecuteTimeout>
  150. <RpcCodec>OpenCards.Server.Core.Serializer</RpcCodec>
  151. <AcceptTypeMappings>
  152. <CenterService>OpenCards.Service.Center.CenterService</CenterService>
  153. <AccountServer>OpenCards.Server.Account.AccountServer</AccountServer>
  154. <ConnectorService>OpenCards.Server.Connector.ConnectorService</ConnectorService>
  155. <ArenaManagerService>OpenCards.Server.Arena.ArenaManagerService</ArenaManagerService>
  156. <ArenaValorService>OpenCards.Server.Arena.ArenaValorService</ArenaValorService>
  157. <ArenaHighendService>OpenCards.Server.Arena.ArenaHighendService</ArenaHighendService>
  158. <SessionService>OpenCards.Server.Connector.SessionService</SessionService>
  159. <LogicService>OpenCards.Server.Logic.LogicService</LogicService>
  160. <LogicManagerService>OpenCards.Server.Logic.LogicManagerService</LogicManagerService>
  161. <StageRankService>OpenCards.Service.StageRank.StageRankService</StageRankService>
  162. <ArenaPinnacleGameService>OpenCards.Server.Arena.ArenaPinnacleGameService</ArenaPinnacleGameService>
  163. </AcceptTypeMappings>
  164. </RpcConfig>
  165. <StartService>
  166. <LogicManagerService>
  167. <ServiceName>LogicManagerService_${ZONE_ID}</ServiceName>
  168. <ServiceType>LogicManagerService</ServiceType>
  169. <Config>
  170. <FightPostURL>${FIGHT_POST_URL}</FightPostURL>
  171. <serverID>${SERVER_ID}</serverID>
  172. </Config>
  173. </LogicManagerService>
  174. <CenterService>
  175. <ServiceName>CenterService_${ZONE_ID}</ServiceName>
  176. <ServiceType>CenterService</ServiceType>
  177. <Config>
  178. <serverID>${SERVER_ID}</serverID>
  179. </Config>
  180. </CenterService>
  181. <StageRankService>
  182. <ServiceName>StageRankService_${ZONE_ID}</ServiceName>
  183. <ServiceType>StageRankService</ServiceType>
  184. <Config>
  185. <serverID>${SERVER_ID}</serverID>
  186. </Config>
  187. </StageRankService>
  188. <ArenaPinnacleGameService>
  189. <ServiceName>ArenaPinnacleGameService_${ZONE_ID}</ServiceName>
  190. <ServiceType>ArenaPinnacleGameService</ServiceType>
  191. <Config>
  192. <ServerID>${SERVER_ID}</ServerID>
  193. </Config>
  194. </ArenaPinnacleGameService>
  195. <ArenaManagerService>
  196. <ServiceName>ArenaManagerService_${ZONE_ID}</ServiceName>
  197. <ServiceType>ArenaManagerService</ServiceType>
  198. <Config>
  199. <ServerID>${SERVER_ID}</ServerID>
  200. </Config>
  201. </ArenaManagerService>
  202. <ArenaHighendService>
  203. <ServiceName>ArenaHighendService_${ZONE_ID}</ServiceName>
  204. <ServiceType>ArenaHighendService</ServiceType>
  205. <Config></Config>
  206. </ArenaHighendService>
  207. <ConnectorService>
  208. <ServiceName>ConnectorService_${ZONE_ID}</ServiceName>
  209. <ServiceType>ConnectorService</ServiceType>
  210. <Config>
  211. <ServerId>${SERVER_ID}</ServerId>
  212. <Host>0.0.0.0</Host>
  213. <Port>${CONNECTOR_PORT}</Port>
  214. <NetCodec>OpenCards.Core.Serializer</NetCodec>
  215. <KeepAlive>true</KeepAlive>
  216. <KeepAliveInterval>30000</KeepAliveInterval>
  217. <RecvBufferSize>16384</RecvBufferSize>
  218. <SendBufferSize>16384</SendBufferSize>
  219. <MaxConnections>300000</MaxConnections>
  220. </Config>
  221. </ConnectorService>
  222. </StartService>
  223. </${GAME_NODE}>
  224. </ServiceNodes>
  225. </doc>
  226. XEOF
  227. # ======================== 生成管理脚本 ========================
  228. log_step "生成管理脚本..."
  229. # --- 公共工具函数 ---
  230. write_script_header() {
  231. local file=$1
  232. cat > "$file" << 'SHEOF'
  233. #!/bin/bash
  234. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  235. BASE_OUTPUT="/data/CzServer/_output.server"
  236. DLL="$BASE_OUTPUT/OpenCards.Server.DotNetCore.dll"
  237. LOG_DIR="$BASE_OUTPUT/logfile"
  238. mkdir -p "$LOG_DIR"
  239. GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; NC='\033[0m'
  240. SHEOF
  241. }
  242. # --- start.sh ---
  243. write_script_header "${ZONE_DIR}/start.sh"
  244. cat >> "${ZONE_DIR}/start.sh" << XEOF
  245. # ${ZONE_NAME} 启动脚本
  246. PID=\$(pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" 2>/dev/null)
  247. if [ -n "\$PID" ]; then
  248. echo -e "\${YELLOW}[SKIP] ${ZONE_NAME} 已在运行 (PID: \$PID)\${NC}"
  249. exit 0
  250. fi
  251. echo -e "\${GREEN}[START] ${ZONE_NAME} (端口:${CONNECTOR_PORT}, RPC:${RPC_PORT}, Redis:db=${REDIS_DB}) ...\${NC}"
  252. nohup dotnet "\$DLL" "\$SCRIPT_DIR/_launch_server.xml" ${GAME_NODE} global.RealmID=${REALM_ID} global.ServerID=${SERVER_ID} \
  253. >> "\$LOG_DIR/server${ZONE_ID}.log" 2>&1 &
  254. sleep 3
  255. PID=\$(pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" 2>/dev/null)
  256. if [ -n "\$PID" ]; then
  257. echo -e "\${GREEN}[OK] ${ZONE_NAME} 启动成功 (PID: \$PID)\${NC}"
  258. else
  259. echo -e "\${RED}[FAIL] ${ZONE_NAME} 启动失败,查看日志:\${NC}"
  260. tail -20 "\$LOG_DIR/server${ZONE_ID}.log"
  261. exit 1
  262. fi
  263. XEOF
  264. # --- stop.sh ---
  265. write_script_header "${ZONE_DIR}/stop.sh"
  266. cat >> "${ZONE_DIR}/stop.sh" << XEOF
  267. # ${ZONE_NAME} 停止脚本
  268. PID=\$(pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" 2>/dev/null)
  269. if [ -n "\$PID" ]; then
  270. echo -e "\${YELLOW}[STOP] ${ZONE_NAME} (PID: \$PID)\${NC}"
  271. kill \$PID
  272. sleep 2
  273. if pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" > /dev/null 2>&1; then
  274. echo -e "\${RED}[KILL] 强制终止...\${NC}"
  275. kill -9 \$PID 2>/dev/null
  276. fi
  277. echo -e "\${GREEN}[OK] ${ZONE_NAME} 已停止\${NC}"
  278. else
  279. echo -e "\${YELLOW}[SKIP] ${ZONE_NAME} 未在运行\${NC}"
  280. fi
  281. XEOF
  282. # --- status.sh ---
  283. write_script_header "${ZONE_DIR}/status.sh"
  284. cat >> "${ZONE_DIR}/status.sh" << XEOF
  285. # ${ZONE_NAME} 状态查看
  286. PID=\$(pgrep -f "server${ZONE_ID}/_launch_server.xml ${GAME_NODE}" 2>/dev/null)
  287. if [ -n "\$PID" ]; then
  288. echo -e "\${GREEN}[UP] ${ZONE_NAME} 运行中"
  289. echo " PID: \$PID"
  290. echo " 端口: ${CONNECTOR_PORT}"
  291. echo " RPC: ${RPC_PORT}"
  292. echo " 日志: \$LOG_DIR/server${ZONE_ID}.log\${NC}"
  293. else
  294. echo -e "\${RED}[DOWN] ${ZONE_NAME} 未运行 (端口:${CONNECTOR_PORT})\${NC}"
  295. fi
  296. XEOF
  297. # --- restart.sh ---
  298. write_script_header "${ZONE_DIR}/restart.sh"
  299. cat >> "${ZONE_DIR}/restart.sh" << XEOF
  300. # ${ZONE_NAME} 重启脚本
  301. bash "\$SCRIPT_DIR/stop.sh"
  302. sleep 2
  303. bash "\$SCRIPT_DIR/start.sh"
  304. XEOF
  305. chmod +x "${ZONE_DIR}/start.sh" "${ZONE_DIR}/stop.sh" "${ZONE_DIR}/status.sh" "${ZONE_DIR}/restart.sh"
  306. # ======================== 输出 serverlist 条目 ========================
  307. cat << XEOF
  308. ${GREEN}============================================
  309. 请将以下条目添加到 OSS serverlist.json:
  310. ============================================${NC}
  311. {
  312. "id": ${ZONE_ID},
  313. "index": $((ZONE_ID - 1)),
  314. "name": "${ZONE_NAME}",
  315. "address": "${SERVER_PUBLIC_IP}:${CONNECTOR_PORT}",
  316. "state": 1,
  317. "is_open": true,
  318. "capacity": 2000,
  319. "serverid": ${SERVER_ID},
  320. "groupid": ${REALM_ID},
  321. "note": "测试服"
  322. }
  323. ${GREEN}============================================${NC}
  324. XEOF
  325. # ======================== 完成 ========================
  326. echo ""
  327. echo -e "${GREEN}============================================${NC}"
  328. echo -e "${GREEN} ${ZONE_NAME} 创建完成!${NC}"
  329. echo -e "${GREEN}============================================${NC}"
  330. echo ""
  331. echo -e " 目录: ${CYAN}${ZONE_DIR}${NC}"
  332. echo -e " 区号: ${CYAN}${SERVER_ID}${NC}"
  333. echo -e " 端口: ${CYAN}${CONNECTOR_PORT}${NC}"
  334. echo -e " RPC端口: ${CYAN}${RPC_PORT}${NC}"
  335. echo -e " Redis: ${CYAN}db=${REDIS_DB}${NC}"
  336. echo ""
  337. echo -e " 管理:"
  338. echo -e " ${CYAN}bash ${ZONE_DIR}/start.sh${NC} 启动"
  339. echo -e " ${CYAN}bash ${ZONE_DIR}/stop.sh${NC} 停止"
  340. echo -e " ${CYAN}bash ${ZONE_DIR}/status.sh${NC} 状态"
  341. echo -e " ${CYAN}bash ${ZONE_DIR}/restart.sh${NC} 重启"
  342. echo -e " ${CYAN}tail -f ${BASE_OUTPUT}/logfile/server${ZONE_ID}.log${NC} 日志"
  343. echo ""
  344. echo -e " ${YELLOW}⚠ 别忘了:${NC}"
  345. echo -e " 1. 更新 OSS serverlist.json(见上方)"
  346. echo -e " 2. 防火墙放行端口 ${CONNECTOR_PORT} 和 ${RPC_PORT}"
  347. echo ""