@echo off rem ------------------------------ rem huangyongxing@yeah.net rem 项目编译及启停控制工具 rem ------------------------------ cd %~dp0/../ set DIR_ERL=%cd% cd ../ set DIR_MAIN=%cd% rem 注意设置SRV_ID、HOST、PORT, rem 一般HOST用自己的IP,SRV_ID推荐使用自己的工号数字【保证唯一】 rem 同时,需要在下方指定对应版本的erl安装路径 set SRV_ID=1 set HOST= set COOKIE=mrzj_%SRV_ID% set PORT=9110 set NODE_NAME_PREFIX=mrzj_dev_ set NODE_NAME=%NODE_NAME_PREFIX%%SRV_ID%@%HOST% set CENTER_NODE_NAME=mrzj_dev_center@%HOST% set CENTER_COOKIE=mrzj_dev_center set CENTER_PORT=9111 title 明日战纪源码管理(%NODE_NAME%, %PORT%) rem 协议生成工具 set PROTO_TOOL=%DIR_MAIN%\doc\proto_auto\ProtoBuilder.exe if not exist %PROTO_TOOL% set PROTO_TOOL= rem 指定erl路径,会自动计算对应的werl和escript路径 rem 使用otp 18.3示例 rem set ERL=D:\Program Files\erl7.3\bin\erl.exe rem 使用otp 19.3示例 rem set ERL=D:\Program Files\erl8.3\bin\erl rem 使用otp 23.3示例 set ERL=D:\Program Files\erl-23.3.4.4\bin\erl rem 使用环境变量中的erl rem set ERL=erl rem 设置游戏服使用的config,便于在本地配置多个服测试 set GS_CONFIG=gsrv.config rem 客户端路径 set CLIENT=%DIR_MAIN%\code\u3d\app\localTest\ClientApp.exe if "%HOST%"=="" ( call :fun_error HOST未配置 goto :EOF ) rem 客户端目录 set CLIENT_DIR=%DIR_MAIN%\code rem 服务端目录 set SERV_DIR=%DIR_ERL% rem svn程序目录(视实际安装目录而异) set SVN_EXE=C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe rem 调用初始化脚本执行环境的接口 call :fun_init_calc rem dialyzer相关配置 rem plt file set PLTFILE=%DIR_ERL%\script\.dialyzer_plt_v%OTP_VER% rem dialyzer 分析日志输出文件名前缀 set DIALYZER_OUTFILE_PREFIX=dialyzer_v%OTP_VER% echo. rem --------- 开始主循环: 等待指令->输入指令->执行指令->等待指令 --------- :fun_main echo erts version: %ERTS_VER% echo otp version: %OTP_VER% echo werl.exe : %WERL% echo server root : %DIR_ERL% set inp= echo --------------------------------------------------------------------------- echo make^|m 编译 ^| dialyzer^|dlz 运行dialyzer静态分析 echo mmm 快速编译(powershell) ^| mm 编译与版本库差异文件 echo start 启动游戏服及跨服 ^| stop 关闭游戏服及跨服 echo restart 重启服务器及跨服 ^| hot 热更服务器及跨服 echo hot_master 热更游戏服 ^| hot_center 热更跨服 echo master 启动游戏服 ^| stop_master 关闭游戏服 echo center 启动跨服服务器 ^| stop_center 关闭跨服服务器 echo werl 开启一个werl窗口 ^| kill^|k 强行结束所有werl.exe echo dir 打开server目录 ^| clean_logs^|cl 清理本地游戏服日志 echo client^|c 开户PC版客户端 ^| attr 重新生成attr_conv模块 if "%PROTO_TOOL%" NEQ "" echo pt 协议生成工具 ^| su ^| cu svn更新 服务端^|客户端 echo clear^|cls 清屏 ^| quit^|q 退出 echo --------------------------------------------------------------------------- set /p inp=请输入指令: cd %DIR_ERL% goto fun_routing :fun_routing if "%inp%"=="make" call :fun_make if "%inp%"=="m" call :fun_make if "%inp%"=="mm" call :fun_mm if "%inp%"=="mmm" call :fun_mmm if "%inp%"=="start" call :fun_start if "%inp%"=="stop" call :fun_stop if "%inp%"=="master" call :fun_start_master if "%inp%"=="stop_master" call :fun_stop_master if "%inp%"=="hot" call :fun_hot if "%inp%"=="hot_master" call :fun_hot_master if "%inp%"=="hot_center" call :fun_hot_center if "%inp%"=="restart" call :fun_restart if "%inp%"=="center" call :fun_start_center if "%inp%"=="stop_center" call :fun_stop_center if "%inp%"=="kill" call :fun_kill if "%inp%"=="k" call :fun_kill if "%inp%"=="clean_logs" call :fun_clean_logs if "%inp%"=="cl" call :fun_clean_logs if "%inp%"=="werl" call :fun_start_werl if "%inp%"=="client" call :fun_start_client if "%inp%"=="c" call :fun_start_client if "%inp%"=="pt" call :fun_proto_tool if "%inp%"=="attr" call :fun_create_attr_conv if "%inp%"=="dir" call :fun_dir_server if "%inp%"=="su" call :server_svn_update if "%inp%"=="cu" call :client_svn_update if "%inp%"=="dlz" call :fun_run_dialyzer if "%inp%"=="dialyzer" call :fun_run_dialyzer if "%inp%"=="cls" cls if "%inp%"=="clear" cls if "%inp%"=="quit" goto :EOF if "%inp%"=="q" goto :EOF echo. goto fun_main rem --------- 后面为相关指令操作接口封装 --------- rem 初始化计算脚本一些工具路径和设置一些全局变量 rem 包括以下变量值: rem ERL、WERL、ESCRIPT、ERTS_VER、ERTS_VER_MAIN rem ERL_OPT_NORMAL rem ERL_OPT_S_SINGLE :fun_init_calc rem 如果%erl%等于erl说明有设置环境变量,理论上escript也找得到 if "%ERL%"=="erl" set envTrue=1 if "%ERL%"=="erl.exe" set envTrue=1 setlocal enabledelayedexpansion if "!envTrue!"=="1" ( for /f "delims=" %%i in ('where erl') do (set ERL=%%i) ) rem 给文件补上后缀,便于判断是否存在 if "!ERL:~-4!" NEQ ".exe" set ERL=!ERL!.exe if NOT EXIST !ERL! ( echo "文件不存在"!ERL! goto :EOF ) ELSE ( set ERL_BIN_ROOT=!ERL:erl.exe=! set ESCRIPT=!ERL_BIN_ROOT!escript.exe set WERL=!ERL_BIN_ROOT!werl.exe set DIALYZER=!ERL_BIN_ROOT!dialyzer.exe rem 避免自动计算的路径用'\',手写的可能用'/',所以两类都先尝试去除 set ERL_INST_TMP=!ERL_BIN_ROOT:bin\=! set ERL_INST=!ERL_INST_TMP:bin/=! ) rem 取得erts version call :fun_calc_erts_ver ERTS_VER ERTS_VER_MAIN "!ERL!" rem call :fun_calc_otp_ver OTP_VER "!ERL!" "!ERL_INST!" call :fun_calc_otp_ver_simple OTP_VER "!ERL_INST!" endlocal & ( rem 如果是作为全局变量,需要在这里set(否则仍是局部变量) rem set ERL_BIN_ROOT=%ERL_BIN_ROOT% set ERL=%ERL% set WERL=%WERL% set ESCRIPT=%ESCRIPT% set DIALYZER=%DIALYZER% set ERTS_VER=%ERTS_VER% set ERTS_VER_MAIN=%ERTS_VER_MAIN% set OTP_VER=%OTP_VER% set ERL_INST=%ERL_INST% rem echo erlang root: %ERL_BIN_ROOT% echo erl.exe : %ERL% echo werl.exe : %WERL% echo escript.exe : %ESCRIPT% rem 设置一些执行参数 if %ERTS_VER_MAIN% LEQ 8 ( set ERL_OPT_NORMAL=+P 204800 +K true +A 100 +spp true -smp enable -hidden +sbwt none set ERL_OPT_S_SINGLE=+P 204800 +K true +A 100 +spp true -smp disable -hidden +sbwt none ) ELSE ( set ERL_OPT_NORMAL=+P 204800 +K true +A 100 +spp true +S 0 -hidden +sbwt none set ERL_OPT_S_SINGLE=+P 204800 +K true +A 100 +spp true +S 1 -hidden +sbwt none ) ) & goto :EOF :fun_make cd %DIR_ERL% set startTime=%time% call :get_localtime startTimeStr echo [%startTimeStr%] start make echo. rem xcopy /d 只更新比目标新的源文件 xcopy "dep\ebin\*.beam" "ebin\" /h /r /k /q /y /d rem 判断编译参数,决定真实使用的文件(目前windows版都使用release版,需要其他版本的手工拷贝文件) setlocal enabledelayedexpansion set needCopy=FALSE if NOT EXIST "ebin\game_logger.beam" ( SET needCopy=TRUE ) ELSE ( rem SET fileCmpRet= rem FOR /F %%i IN ('comp /M dep\ebin\debug\game_logger.beam ebin\game_logger.beam') DO set fileCmpRet=%%i rem if "!fileCmpRet!"=="文件的大小不同。" SET needCopy=TRUE call :calc_md5 "dep\ebin\release\game_logger.beam" "fMD51" call :calc_md5 "ebin\game_logger.beam" "fMD52" if "!fMD51!" NEQ "!fMD52!" SET needCopy=TRUE ) if "!needCopy!"=="TRUE" xcopy "dep\ebin\release\*.beam" "ebin\" /e /h /r /k /q /y endlocal rem 在编译、热更等简单操作中可直接使用halt(0)直接退出erlangVM,比c:q()优雅中止服务的速度更快 rem set makeOptions=[{d, 'DEV_SERVER'}], rem 通常可增加的选项 {d,'DEV_SERVER'}, {d,'CP_VER',"TW"}, ... set makeOptions=[] "%ERL%" -noinput -pa ./ebin -eval "mmake:all(2, %makeOptions%),halt(0)" call :calc_time_diff "%startTime%" "%time%" "runTime" call :get_localtime EndTimeStr echo. echo cost time: %runTime% echo. echo [%EndTimeStr%] finish make echo "%EndTimeStr%" > %DIR_ERL%/script/timefile goto :EOF :fun_mm cd %DIR_ERL% set startTime=%time% call :get_localtime startTimeStr echo [%startTimeStr%] start mm echo. svn status "./src" > "./svn_status.log" cd %DIR_ERL% "%ERL%" -noinput -pa ./ebin -eval "make_file:make_file(),halt(0)" call :calc_time_diff "%startTime%" "%time%" "runTime" call :get_localtime EndTimeStr echo. echo cost time: %runTime% echo. echo [%EndTimeStr%] finish make goto :EOF :fun_start call :fun_start_center & call :fun_start_master goto :EOF :fun_stop call :fun_stop_master & call :fun_stop_center goto :EOF :fun_mmm cd %DIR_ERL% set startTime=%time% call :get_localtime startTimeStr echo [%startTimeStr%] start mmm echo. cd script echo %cd% PowerShell.exe -file mm.ps1 call :calc_time_diff "%startTime%" "%time%" "runTime" call :get_localtime EndTimeStr echo. echo cost time: %runTime% echo. echo [%EndTimeStr%] finish make goto :EOF :fun_start_master cd %DIR_ERL%\config rem start cmd /C ""%ERL%" %ERL_OPT_NORMAL% -name %NODE_NAME% -setcookie %COOKIE% -boot start_sasl -config %GS_CONFIG% -pa ../ebin -s main start -extra %HOST% %PORT% & pause" start "" "%WERL%" %ERL_OPT_NORMAL% -name %NODE_NAME% -setcookie %COOKIE% -boot start_sasl -config %GS_CONFIG% -pa ../ebin -s main start -extra %HOST% %PORT% goto :EOF :fun_stop_master cd %DIR_ERL%\config set STOP_NODE_NAME=%NODE_NAME_PREFIX%%SRV_ID%_stop_%RANDOM%@%HOST% "%ERL%" -hidden -name %STOP_NODE_NAME% -setcookie %COOKIE% -pa ../ebin -s main stop_server -extra %NODE_NAME% rem start "" "%WERL%" -hidden -name %STOP_NODE_NAME% -setcookie %COOKIE% -pa ../ebin -s main stop_server -extra %NODE_NAME% goto :EOF :fun_hot call :fun_hot_master & call :fun_hot_center goto :EOF :fun_hot_master cd %DIR_ERL%\config set hotNodeName=%NODE_NAME_PREFIX%%SRV_ID%_hot_%RANDOM%@%HOST% "%ERL%" -hidden -name %hotNodeName% -setcookie %COOKIE% -pa ../ebin -eval "rpc:call('%NODE_NAME%', u, u, []), halt(0)" rem start "" "%WERL%" -hidden -name %hotNodeName% -setcookie %COOKIE% -pa ../ebin -eval "rpc:call('%NODE_NAME%', u, u, []), halt(0)" goto :EOF :fun_hot_center cd %DIR_ERL%\config set hotNodeName=%NODE_NAME_PREFIX%center_hot_%RANDOM%@%HOST% "%ERL%" -hidden -name %hotNodeName% -setcookie %CENTER_COOKIE% -pa ../ebin -eval "rpc:call('%CENTER_NODE_NAME%', u, u, []), halt(0)" rem start "" "%WERL%" -hidden -name %hotNodeName% -setcookie %CENTER_COOKIE% -pa ../ebin -eval "rpc:call('%CENTER_NODE_NAME%', u, u, []), halt(0)" goto :EOF :fun_restart call :fun_stop choice /t 2 /d y /n >nul call :fun_start goto :EOF :fun_start_center cd %DIR_ERL%\config rem start cmd /C ""%ERL%" %ERL_OPT_NORMAL% -name %CENTER_NODE_NAME% -setcookie %CENTER_COOKIE% -boot start_sasl -config cls.config -pa ../ebin -s main start -extra %HOST% %CENTER_PORT% & pause" start "" "%WERL%" %ERL_OPT_NORMAL% -name %CENTER_NODE_NAME% -setcookie %CENTER_COOKIE% -boot start_sasl -config cls.config -pa ../ebin -s main start -extra %HOST% %CENTER_PORT% goto :EOF :fun_stop_center cd %DIR_ERL%\config set STOP_NODE_NAME=center_stop_%RANDOM%@%HOST% "%ERL%" -hidden -name %STOP_NODE_NAME% -setcookie %CENTER_COOKIE% -pa ../ebin -s main stop_server -extra %CENTER_NODE_NAME% rem start "" "%WERL%" -hidden -name %STOP_NODE_NAME% -setcookie %CENTER_COOKIE% -pa ../ebin -s main stop_server -extra %CENTER_NODE_NAME% goto :EOF rem 强制关闭werl进程(在异常崩溃的情况下,windows中会残留werl进程) :fun_kill taskkill /F /IM werl.exe goto :EOF rem 清理本地游戏服日志 :fun_clean_logs cd %DIR_ERL%\logs DEL /Q *.* goto :EOF rem 随机节点名的werl :fun_start_werl cd %DIR_ERL%\config if %time:~0,2% leq 9 (set hour=0%time:~1,1%) else (set hour=%time:~0,2%) set randomNode=werl_%hour%%time:~3,2%%time:~6,2%_%RANDOM%@%HOST% if not exist gsrv.config ( start "" "%WERL%" -hidden -name %randomNode% -setcookie %COOKIE% -pa ../ebin ) else ( start "" "%WERL%" -hidden -name %randomNode% -setcookie %COOKIE% -pa ../ebin -config gsrv.config ) goto :EOF rem dialyzer静态分析工具 初始化持久查询表文件plt file :fun_init_plt echo [init] build plt file "%DIALYZER%" --build_plt --apps erts kernel stdlib sasl compiler mnesia crypto inets ssl public_key eunit tools --output_plt "%PLTFILE%" goto :EOF rem dialyzer静态分析工具 执行分析 :fun_analyse_code call :get_datetime_str dt set outfile=%DIR_ERL%\script\%DIALYZER_OUTFILE_PREFIX%_%dt%.txt set erlLib=%ERL_INST%\lib set serverDir=%DIR_ERL% echo 正在执行dialyzer分析 echo 输出分析日志文件:%outfile% rem "D:/Program Files/erl-23.2.5/bin/dialyzer.exe" -I server/include --src server/src -r server/src -pa "D:/Program Files/erl-23.2.5/lib" --plt ./.dialyzer_plt_mrzj > dialyzer_%dt%.txt "%DIALYZER%" -I %serverDir%\include --src %serverDir%\src -r %serverDir%\src -pa "%erlLib%" --plt "%PLTFILE%" > "%outfile%" goto :EOF rem dialyzer静态分析工具调用 :fun_run_dialyzer if NOT EXIST "%PLTFILE%" call :fun_init_plt call :fun_analyse_code goto :EOF rem 协议生成工具 :fun_proto_tool rem 需要开启变量延迟,否则else括号块首次执行时变量解析有问题 setlocal enabledelayedexpansion if "!PROTO_TOOL!"=="" ( call :fun_error 未配置协议生成工具 ) else ( for /f "delims=" %%t in ('echo !PROTO_TOOL!') do (set dirProtoTool=%%~dpt) echo --------------------------------- echo change to directory: [!dirProtoTool!] cd !dirProtoTool! "!PROTO_TOOL!" ) endlocal goto :EOF rem 开户客户端 :fun_start_client if NOT EXIST %CLIENT% ( call :fun_error 没有找到客户端可执行文件[%CLIENT%] goto :EOF ) setlocal enabledelayedexpansion for /f "delims=" %%t in ('echo !CLIENT!') do (set dirClient=%%~dpt) rem 客户端从某个版本开始,似乎用了绝对路径来查找资源,必须在exe所在目录启动才行 cd /d "!dirClient!" start "" cmd /C "%CLIENT%" endlocal goto :EOF rem 生成attr_conv模块 :fun_create_attr_conv if NOT EXIST src\tool\attr_tool.erl ( echo "未找到attr_tool.erl文件" ) ELSE ( "%ESCRIPT%" src\tool\attr_tool.erl ) goto :EOF rem 打开服务端工作目录 %DIR_ERL% :fun_dir_server start "" explorer "%DIR_ERL%" goto :EOF rem 服务端更新 :server_svn_update start "" "%SVN_EXE%" /command:update /path:"%SERV_DIR%" /closeonend:0 goto :EOF rem 客户端更新 :client_svn_update start "" "%SVN_EXE%" /command:update /path:"%CLIENT_DIR%" /closeonend:0 goto :EOF rem node name split rem call :node_name_split "%node%" "nodePrefix" "nodeHost" :node_name_split set input=%~1 set sep=@ set nPre= set nHost= setlocal enabledelayedexpansion for /F "delims=%sep% tokens=1-2" %%a in ("%input%") do ( set nPre=%%a set nHost=%%b ) endlocal & set "%~2=%nPre%" & set "%~3=%nHost%" & goto :EOF rem 计算两个时间差值 rem 调用示例: rem set startTime=%time% rem echo do something rem call :calc_time_diff "%startTime%" "%time%" "runTime" rem echo cost time %runTime% :calc_time_diff setlocal set /a n=0 for /f "tokens=1-8 delims=.: " %%a in ("%~1:%~2") do ( set /a n-=1%%a*3600000+1%%b*60000+1%%c*1000+1%%d*10 set /a n+=1%%e*3600000+1%%f*60000+1%%g*1000+1%%h*10 ) set /a s=n/3600000,n=n%%3600000,f=n/60000,n=n%%60000,m=n/1000,n=n%%1000 set "ok=%s% 小时 %f% 分钟 %m% 秒 %n% 毫秒" endlocal & set "%~3=%ok%" & goto :EOF rem 获取本地时间 :get_localtime setlocal if %time:~0,2% leq 9 (set hour=0%time:~1,1%) else (set hour=%time:~0,2%) set lt=%date:~0,4%-%date:~5,2%-%date:~8,2% %hour%:%time:~3,2%:%time:~6,2% endlocal & set "%~1=%lt%" & goto :EOF rem 获取日期 rem call :get_datetime_str var :get_datetime_str setlocal if %time:~0,2% leq 9 (set hour=0%time:~1,1%) else (set hour=%time:~0,2%) set dt=%date:~0,4%%date:~5,2%%date:~8,2%_%hour%%time:~3,2%%time:~6,2% endlocal & set "%~1=%dt%" & goto :EOF rem 计算文件md5 rem call :calc_md5 "filePath" "fMD5" rem echo %fMD5% :calc_md5 setlocal FOR /F %%i IN ('certutil -hashfile "%~1" MD5 ^| findstr /V 哈希 ^| findstr /V 完成') DO set fMD5=%%i endlocal & set "%~2=%fMD5%" & goto :EOF rem 计算erts版本 rem call :fun_calc_erts_ver "ERTS_VER" "ERTS_VER_MAIN" "%ERL%" :fun_calc_erts_ver rem 注意,pattern最后面本身有空格需要保留(bat语法奇葩,两侧加引号又会出问题) rem 但是为防止不小心格式化代码时去掉了空格, rem 所以改为无空格,并在替换后,多进行一次替换空格操作 set pattern=Erlang (SMP,ASYNC_THREADS) (BEAM) emulator version set replace= set erlbin=%3 setlocal enabledelayedexpansion for /f "usebackq delims=" %%i in (`"%erlbin%" +V 2^>^&1`) do (set allstr=%%i) set "ertsVer=!allstr:%pattern%=%replace%!" set "ertsVer=!ertsVer: =!" for /f "usebackq delims=." %%i in (`echo !ertsVer!`) do ( if defined ertsVerMain goto break_calc_erts_ver set ertsVerMain=%%i ) :break_calc_erts_ver endlocal & set "%~1=%ertsVer%" & set "%~2=%ertsVerMain%" & goto :EOF rem 查找otp version【调用erl获取主版本号,再定位版本号文件】 rem call :fun_calc_otp_ver OTP_VER "%ERL%" "%ERL_INST%" :fun_calc_otp_ver rem erlang:system_info(otp_release) 在OTP 17之后,只能获取主版本号 rem Win和Linux通行的方案是通过 os:find_executable("erl") 找到erl.exe, rem 然后读取这个文件 ../../release/MainOTPVerion/OTP_VERSION rem 在Win上可以简化为安装路径下的 releases/MainOTPVerion/OTP_VERSION set erlbin=%2 set erlInst=%3 setlocal enabledelayedexpansion for /f "usebackq delims=" %%i in (`"%erlbin% -noshell -eval ""io:fwrite(""""^~s"""",[erlang:system_info(otp_release)]),halt(0)"""`) do ( set otpMainVer=%%i ) set releaseFile=%erlInst%\releases\%otpMainVer%\OTP_VERSION for /f "usebackq delims=" %%i in (`type %releaseFile%`) do ( set otpVer=%%i ) endlocal & set "%~1=%otpVer%" & goto :EOF rem 查找otp version【不调用erl获取主版本号,依赖文件系统下仅存一个版本号目录来定位文件】 :fun_calc_otp_ver_simple OTP_VER "%ERL_INST%" set erlInst=%2 set releaseDir=%erlInst%\releases setlocal enabledelayedexpansion cd /d %releaseDir% for /f "usebackq delims=" %%i in (`dir /AD /B`) do ( set releaseFile=%releaseDir%\%%i\OTP_VERSION ) for /f "usebackq delims=" %%i in (`type %releaseFile%`) do ( set otpVer=%%i ) endlocal & set "%~1=%otpVer%" & goto :EOF :fun_error echo --------------------- if "%1"=="" ( echo 出错了! ) ELSE ( echo %1 ) echo --------------------- echo. pause goto :EOF