Взаимодействие между несколькими .bat, мультиплеер на .bat

zhenshen

Вселенское Зло


Бат-файлы лишены возможности передавать по сети какую-нибудь полезную информацию друг другу.


Нет сокетов, ну и ладно, зато есть простая работа с именованными областями данных, т.е. файлами. Создание и чтение однострочных текстовых файлов вообще упрощено до предела. Достаточно выполнить echo Text>file.txt и file.txt будет содержать строку «Text». Прочитать строку из файла можно так: set /p var=<file.txt (после выполнения переменная var будет содержать строку «Text»).

Итак, можно передавать информацию между батниками через файлы. А если эти файлы будут находиться на расшаренных сетевых ресурсах, то между собой вполне могут общаться батники, запущенные на различных компьютерах.

Вот небольшой пример клиент-серверного приложения, такого себе rexec для бедных :)

файл rexec_server.bat:
set checkFolder=\\IMP5\PUBLIC_RW
:loop
@ping -n 2 127.0.0.1 > nul
@if not exist "%checkFolder%\!cmd" goto loop
@call "%checkFolder%\!command.bat" > "%checkFolder%\!result"
@del "%checkFolder%\!cmd"
@goto loop




файл rexec_client.bat:
@echo off
set checkFolder=\\IMP5\PUBLIC_RW

:mainloop
set /p c=^>
if "%c%"=="exit" exit
if "%c%"=="quit" exit
echo %c%>"%checkFolder%\!command.bat"
echo.>"%checkFolder%\!cmd"

:waitloop
ping -n 2 127.0.0.1 > nul
if exist "%checkFolder%\!cmd" goto waitloop
type "%checkFolder%\!result"
del "%checkFolder%\!result"
goto mainloop




Вот пример работы:



Делает оно следующее: rexec_server.bat ждёт от клиента командную строку и выполняет её на том компьютере, на котором он запущен. С клиентом он общается через расшаренную папку «\\IMP5\PUBLIC_RW» (разумеется, вам нужно будет заменить её на свою, если захотите поэкспериментировать). Чтобы клиент и сервер не пытались раньше времени прочитать файлы, которые сейчас для них пишутся, были предусмотрены файлы-флаги, которые служат только для того, чтобы обозначить, что информация уже сохранена полностью.

Ну и на закуску — мультиплеерная игра на .bat «крестики-нолики, три в ряд».
Здесь для сохранения целостности данных реализован механизм критических секций для блокирования попыток одновременного обращения к файлам.
Помните, что перед запуском нужно в строке «set FLAG_DIR=\\IMP5\SHARED_RW» указать свою расшаренную папку (кстати, через одну папку могут играться сразу несколько игр).

Файл xo.bat:
@echo off
cls
set FLAG_DIR=\\IMP5\SHARED_RW

call :create_chars

:restart
echo Please wait...
call :sys_utils init "%0"
set cmdPrefix=$p%__PID%

rem ------- connecting --------

call :sys_utils enter_critical_section
cls
call :sys_utils fetch_flag ready_to_play
if "%RESULT%"=="" (
call :sys_utils set_flag ready_to_play %cmdPrefix%
set symbol=X
set symbol2=O
set server=1
) else (
set cmdPrefix=%RESULT%
call :sys_utils set_flag %RESULT%_client_ready ---
set symbol=O
set symbol2=X
set server=0
)
call :sys_utils leave_critical_section

if "%server%"=="0" goto client1
call :wait_for_client
call :sys_utils set_flag %cmdPrefix%_server_ready_too ---
goto skipClient1
:client1
call :wait_for_server
if "%serverFailure%"=="1" goto restart
:skipClient1

rem ------- connected --------

call :clear_field
if "%server%"=="1" (set curMode=your_turn) else (set curMode=enemy_turn)

:main_game_loop
cls
call :check_win
call :render_field

if "%winSymbol%"=="%symbol%" goto you_win
if "%winSymbol%"=="%symbol2%" goto enemy_win

if not "%curMode%"=="your_turn" goto skipTurn1
echo.
echo Your are "%symbol%"
echo.
set /p tmp=Please enter <nul
if "%f1%"=="1" set /p tmp=1, <nul
if "%f2%"=="2" set /p tmp=2, <nul
if "%f3%"=="3" set /p tmp=3, <nul
if "%f4%"=="4" set /p tmp=4, <nul
if "%f5%"=="5" set /p tmp=5, <nul
if "%f6%"=="6" set /p tmp=6, <nul
if "%f7%"=="7" set /p tmp=7, <nul
if "%f8%"=="8" set /p tmp=8, <nul
if "%f9%"=="9" set /p tmp=9, <nul
echo or 'q' for quit
echo.
set /p "tmp=Your turn: "

if "%tmp%"=="q" goto quit_game
if "%tmp%"=="Q" goto quit_game

call set varName=%%f%tmp%%%
if not "%varName%"=="%tmp%" goto main_game_loop
call set f%tmp%=%symbol%

call :sys_utils set_flag %cmdPrefix%_%symbol%_move f%tmp%
set curMode=enemy_turn
goto main_game_loop
:skipTurn1

if not "%curMode%"=="enemy_turn" goto skipTurn2
echo.
echo Waiting for another player
:wait_for_player0
set /p tmp=.<nul
ping -n 2 127.0.0.1 > nul
call :sys_utils fetch_flag %cmdPrefix%_%symbol2%_move
if "%RESULT%"=="" goto wait_for_player0

echo %RESULT%

if "%RESULT%"=="q" goto other_player_quit

call set %RESULT%=%symbol2%
set curMode=your_turn
goto main_game_loop
:skipTurn2

goto main_game_loop

:quit_game
call :sys_utils set_flag %cmdPrefix%_%symbol%_move q
exit

:eek:ther_player_quit
cls
echo Other player has left the game.
echo Press 'Enter' to search for another one.
pause > nul
goto restart

:you_win
echo.
echo You Win
echo.
echo Press 'Enter'
pause > nul
goto restart

:enemy_win
echo.
echo You Lose
echo.
echo Press 'Enter'
pause > nul
goto restart

:wait_for_client
echo Waiting for client
:wait_for_client1
set /p tmp=.<nul
ping -n 2 127.0.0.1 > nul
call :sys_utils fetch_flag %cmdPrefix%_client_ready
if "%RESULT%"=="" goto wait_for_client1
exit /b

:wait_for_server
echo Waiting for server
set serverFailure=0
set /a waitCnt=4
:wait_for_server1
set /a waitCnt-=1
if "%waitCnt%"=="0" (
set serverFailure=1
exit /b
)
set /p tmp=.<nul
ping -n 2 127.0.0.1 > nul
call :sys_utils fetch_flag %cmdPrefix%_server_ready_too
if "%RESULT%"=="" goto wait_for_server1
exit /b

:clear_field
set "f1=1"
set "f2=2"
set "f3=3"
set "f4=4"
set "f5=5"
set "f6=6"
set "f7=7"
set "f8=8"
set "f9=9"
exit /b


:create_chars

set "charX0= # # ^|"
set "charX1= # # ^|"
set "charX2= # ^|"
set "charX3= # # ^|"
set "charX4= # # ^|"
set "charX5=-------+"

set "charO0= ### ^|"
set "charO1= # # ^|"
set "charO2= # # ^|"
set "charO3= # # ^|"
set "charO4= ### ^|"
set "charO5=-------+"

set "charXW0=.#...#.^|"
set "charXW1=..#.#..^|"
set "charXW2=...#...^|"
set "charXW3=..#.#..^|"
set "charXW4=.#...#.^|"
set "charXW5=-------+"

set "charOW0=..###..^|"
set "charOW1=.#...#.^|"
set "charOW2=.#...#.^|"
set "charOW3=.#...#.^|"
set "charOW4=..###..^|"
set "charOW5=-------+"

for %%i in (1,2,3,4,5,6,7,8,9) do call :create_empty_char %%i

exit /b

:create_empty_char
set "char%10= ^|"
set "char%11= ^|"
set "char%12= %1 ^|"
set "char%13= ^|"
set "char%14= ^|"
set "char%15=-------+"
exit /b

:check_win
set winSymbol=.
call :check3 1 2 3
call :check3 4 5 6
call :check3 7 8 9
call :check3 1 4 7
call :check3 2 5 8
call :check3 3 6 9
call :check3 1 5 9
call :check3 3 5 7
exit /b

:check3
call set tmp1=%%f%1%%%%f%2%%%%f%3%%
call set tmp2=%%f%1%%%%f%1%%%%f%1%%
if "%tmp1%"=="%tmp2%" (
call set "winSymbol=%%f%1%%"
call set "f%1=%%f%1%%W"
call set "f%2=%%f%2%%W"
call set "f%3=%%f%3%%W"
)
exit /b

:render_field
call :render_line %f1% %f2% %f3%
call :render_line %f4% %f5% %f6%
call :render_line %f7% %f8% %f9%
exit /b

:render_line
call echo %%char%10%%%%char%20%%%%char%30%%
call echo %%char%11%%%%char%21%%%%char%31%%
call echo %%char%12%%%%char%22%%%%char%32%%
call echo %%char%13%%%%char%23%%%%char%33%%
call echo %%char%14%%%%char%24%%%%char%34%%
call echo %%char%15%%%%char%25%%%%char%35%%
exit /b


rem ----------------- atom --------------
:sys_utils
goto %1
exit /b

:init
if "%FLAG_DIR%"=="" set FLAG_DIR=.
set UNIQ_BAT_ID=%COMPUTERNAME%_%USERNAME%_%~n2
set /A CRITICAL_SECTION_LEVEL=0

call :enter_critical_section
call :test_flag --null-- $pid$
set /A __PID=%RESULT%
if [%__PID%]==[0] set /A __PID=10000000
if [%__PID%]==[] set /A __PID=10000000
if [%__PID%]==[99999999] set /A __PID=10000000
set /A __PID=__PID+1
call :set_flag --null-- $pid$ %__PID%
call :leave_critical_section

set UNIQ_BAT_ID=%UNIQ_BAT_ID%_pid%__PID%
exit /b

:set_flag
call :enter_critical_section
echo %3>"%FLAG_DIR%\%2"
call :leave_critical_section
exit /b

:remove_flag
call :enter_critical_section
if exist "%FLAG_DIR%\%2" del "%FLAG_DIR%\%2"
call :leave_critical_section
exit /b

:fetch_flag
call :enter_critical_section
set "RESULT="
if exist "%FLAG_DIR%\%2" (
set /p RESULT=<"%FLAG_DIR%\%2"
del "%FLAG_DIR%\%2"
)
call :leave_critical_section
exit /b

:test_flag
call :enter_critical_section
set "RESULT="
if exist "%FLAG_DIR%\%2" set /p RESULT=<"%FLAG_DIR%\%2"
call :leave_critical_section
exit /b

:enter_critical_section
set /A CRITICAL_SECTION_LEVEL=CRITICAL_SECTION_LEVEL+1
if not [%CRITICAL_SECTION_LEVEL%]==[1] exit /b

set /A __WAIT_CNT=500

:wait_enter_critical_section
set /A __WAIT_CNT=__WAIT_CNT-1
if [%__WAIT_CNT%]==[0] del "%FLAG_DIR%\$cs$_*"
if exist "%FLAG_DIR%\$cs$_*" goto wait_enter_critical_section

echo.>"%FLAG_DIR%\$cs$_%UNIQ_BAT_ID%"
set /A __CNT=0
for %%i in ( "%FLAG_DIR%\$cs$_*" ) do set /a __CNT=__CNT+1
if [%__CNT%]==[1] goto all_right_1
del "%FLAG_DIR%\$cs$_%UNIQ_BAT_ID%"
goto wait_enter_critical_section
:all_right_1
exit /b

:leave_critical_section
set /A CRITICAL_SECTION_LEVEL=CRITICAL_SECTION_LEVEL-1
if [%CRITICAL_SECTION_LEVEL%]==[0] del "%FLAG_DIR%\$cs$_%UNIQ_BAT_ID%"
exit /b

:EOF
 

Сверху