Авторы: Arkshine & xPaw
Как вы знаете вывести в MOTD страничку, сформированную в вашем плагине довольно просто, а вот добиться двустороннего взаимодействия MOTD уже не такая тривиальная задача. В этой статье вы узнаете, как это сделать.
Зачем же это нужно? Например, для создания своего классного магазина или выбора класса игрока, применений масса.
Требования:
Для осуществления взаимодействия вам понадобятся:
- SourceQuery.class.php
- Вебсайт
Так же вы должны в какой-то мере владеть:
- PAWN
- PHP
- HTML/CSS (желательно)
Приступаем!
В этой статье будет описано, то, как сделать MOTD для выбора класса игрока.
План статьи:
- Как это работает?
- PHP код
- AMXX плагин
- Итоги и Скриншоты
Как это работает?
Мы будем показывать игроку меню в MOTD, в котором он сможет куда-либо нажать.
Когда игрок сделает действие (нажмёт на ссылку), вебсайт отошлёт всю нужную информацию на сервер (hlds), и затем плагин обработает эту информацию.
Для реализации мы регистрируем серверную команду через register_concmd.
Например register_concmd(“php_result”, “callback”).
При выполнении команды php_result через RCON мы передаём ей некоторые параметры, которые затем читаем в функции callback.
В итоге получается такая схема взаимодействия:
Сервер -> Вебсайт -> Сервер
PHP код
Скрипт получает следующую информацию:
- Id игрока
- IP игрока
- Текущий класс игрока
Итак, начинаем:
<html><head><title>Class Chooser</title></head><body> <?php $s_index = $_GET[ 'index' ]; $s_ip = $_GET[ 'ip' ]; $s_class = $_GET[ 'class' ]; $ip = $_SERVER[ 'REMOTE_ADDR' ]; /* Если полученный IP с сервера не совпадает с реальным IP игрока, то прекращаем работу скрипта. */ if( $ip != $s_ip ) { die( ); } ?> </body></html>
После этой проверки, мы можем показать игроку меню:
<html><head><title>Class Chooser</title></head><body> <?php $s_index = $_GET[ 'index' ]; $s_ip = $_GET[ 'ip' ]; $s_class = $_GET[ 'class' ]; $ip = $_SERVER[ 'REMOTE_ADDR' ]; if( $ip != $s_ip ) { die( ); } else { // Создаём массив из доступных классов $classesName = array( "M4A1 + USP", "Ak47 + GLOCK", "FAMAS + DEAGLE", "GALIL + DEAGLE" ); // Теперь в цикле создаём элементы меню for( $x = 0; $x < count( $classesName ); $x ++ ) { Echo "<a href="". $_SERVER[ 'PHP_SELF' ] ."?item=". ( $x + 1 ) ."&index=". $s_index ."&class=". $s_class ."&ip=". $s_ip ."">". $classesName[ $x ] ."</a><br/><br/>"; } } ?> </body></html>
Теперь мы должны отослать серверу информацию о том какой класс выбрал игрок.
Но мы отошлём команду, только в том случае, если текущий класс игрока отличается от того, который игрок выбрал.
Теперь пришло время подключить SourceQuery.class.php в скрипт.
Окончательный вариант кода: (в коде есть дефайны, которые нужно поменять)
<html><head><title>Class Chooser</title></head><body> <?php define( 'TIME_OUT', 3 ); define( 'SERV_PORT', port_of_your_server ); // Сменить на порт сервера define( 'SERV_IP', 'ip_of_your_server' ); // Сменить на ip сервера define( 'SERV_RCON', 'rcon_of_your_server' ); // Сменить на RCON сервер // Не забудьте залить класс SourceQuery на FTP require 'SourceQuery.class.php'; $s_index = $_GET[ 'index' ]; $s_ip = $_GET[ 'ip' ]; $s_class = $_GET[ 'class' ]; $item = $_GET[ 'item' ]; $ip = $_SERVER[ 'REMOTE_ADDR' ]; if( $ip != $s_ip ) { die( ); } else if( $item ) // Если был выбран пункт меню, то отсылаем команду ... { if( $class != $item ) // ... продолжаем только если текущий класс игрока не совпадает с выбранным. { $query = new SourceQuery( ); try { $query -> Connect( SERV_IP, SERV_PORT, TIME_OUT, SourceQuery :: GOLDSOURCE ); // Подключяемся к серверу $query -> SetRconPassword( SERV_RCON ); // Задаём RCON $query -> Rcon( 'php_results '. $s_index .' '. $item.' '. $ip ); // Отсылаем команду } catch( SQueryException $e ) { echo $e -> getMessage( ); } $query -> Disconnect( ); // Завершаем соединение } else // ... выбранный класс равен текущему классу игрока, редирект на страницу выбора класса (по сути на эту же станицу) { header( 'Location: '. $_SERVER[ 'PHP_SELF' ] .'?index='. $s_index .'&class='. $s_class .'&ip='. $s_ip .'' ); } } else { $classesName = array( "M4A1 + USP", "Ak47 + GLOCK", "FAMAS + DEAGLE", "GALIL + DEAGLE" ); for( $x = 0; $x < count( $classesName ); $x ++ ) { Echo "<a href="". $_SERVER[ 'PHP_SELF' ] ."?item=". ( $x + 1 ) ."&index=". $s_index ."&class=". $s_class ."&ip=". $s_ip ."">". $classesName[ $x ] ."</a><br/><br/>"; } } ?> </body></html>
На этом с PHP всё.
AMXX плагин
Для начала получим информацию отосланную с вебсайта (через RCON)
Мы использовали команду php_result.
#include < amxmodx > new MaxServerSlots; public plugin_init( ) { register_plugin( "AMXX/PHP MOTD", "1.0.0", "Bboy Grun" ); register_concmd( "php_results", "OnResultShop" ); MaxServerSlots = get_maxplayers( ); } public OnResultShop( const idServer ) { if( !idServer ) // idServer должен быть равен 0 (нельзя допустить чтоб игрок мог использовать эту команду) { new arguments[ 25 ]; if( read_args( arguments, charsmax( arguments ) ) ) // Получаем аргументы, если их нет, то прекращаем работу { new ipPHP[ 16 ], idStr[ 3 ], itemStr[ 3 ]; // Парсим 3 аргумента, если их не 3, то прекращаем работу if( parse( arguments, idStr, charsmax( idStr ), itemStr, charsmax( itemStr ), ipPHP, charsmax( ipPHP ) ) == 3 ) { new const player = str_to_num( idStr ); // Проверка на то, что в player содержится id игрока if( 1 <= player <= MaxServerSlots ) { new ip[ 16 ]; get_user_ip( player, ip, charsmax( ip ), 1 ); // Как и в PHP, мы должны сверить IP адреса if( equal( ip, ipPHP ) ) { // Можем продолжать } } } } } }
Отлично, мы получили всю информацию, которая нужна.
Теперь завершим плагин, добавим следующую функциональность:
- Выдача оружия игроку при респавне
- Показ MOTD, если у игрока нет класса
Так же сразу будем выдавать оружие игроку при выборе класса, если до этого игрок был без класса.
#include < amxmodx > #include < fun > #include < cstrike > #include < hamsandwich > const MaxClients = 32; new PlayerClass[ MaxClients + 1 ]; new PlayerNextClass[ MaxClients + 1 ]; new MaxServerSlots; // Путь до скрипта new const URL[ ] = "http://my-website.com/index.php"; public plugin_init( ) { register_plugin( "AMXX/PHP MOTD", "1.0.0", "Bboy Grun" ); RegisterHam( Ham_Spawn, "player", "OnPlayerSpawn", .Post = true ); register_clcmd( "say /class", "OnChooseClass" ); register_concmd( "php_results", "OnResultShop" ); MaxServerSlots = get_maxplayers( ); } public OnPlayerSpawn( const player ) { if( is_user_alive( player ) ) { if( !PlayerClass[ player ] ) { OnChooseClass( player ); } else { new const nextClass = PlayerNextClass[ player ]; if( nextClass ) { PlayerClass[ player ] = nextClass; PlayerNextClass[ player ] = 0; } give_weapons( player, nextClass ); } } } public client_disconnect( client ) { PlayerNextClass[ client ] = 0; PlayerClass[ client ] = 0; } public OnChooseClass( const player ) { new infos[ 128 ], ip[ 16 ]; get_user_ip( player, ip, charsmax( ip ), 1 ); formatex( infos, charsmax( infos ), "%s?index=%d&ip=%s&class=%d", URL, player, ip, PlayerClass[ player ] ); show_motd( player, infos ); } public OnResultShop( const idServer ) { if( !idServer ) { new arguments[ 25 ]; if( read_args( arguments, charsmax( arguments ) ) ) { new ipPHP[ 16 ], idStr[ 3 ], itemStr[ 3 ]; if( parse( arguments, idStr, charsmax( idStr ), itemStr, charsmax( itemStr ), ipPHP, charsmax( ipPHP ) ) == 3 ) { new const player = str_to_num( idStr ); if( 1 <= player <= MaxServerSlots ) { new ip[ 16 ]; get_user_ip( player, ip, charsmax( ip ), 1 ); if( equal( ip, ipPHP ) ) { new const item = str_to_num( itemStr ); new const currentClass = PlayerClass[ player ]; if( currentClass ) { if( currentClass != item ) { PlayerNextClass[ player ] = item; } } else { PlayerClass[ player ] = item; if( is_user_alive( player ) ) { give_weapons( player, item ); } } } } } } } } give_weapons( const player, const item ) { #define give_items(%1,%2,%3,%4) ( give_item( %1, %2 ), cs_set_user_bpammo( %1, %3, %4 ) ) strip_user_weapons( player ); give_item( player, "weapon_knife" ); switch( item ) { case 1: { give_items( player, "weapon_m4a1", CSW_M4A1, 90 ); give_items( player, "weapon_usp", CSW_USP, 100 ); } case 2: { give_items( player, "weapon_ak47", CSW_AK47, 90 ); give_items( player, "weapon_glock18", CSW_GLOCK18, 120 ); } case 3: { give_items( player, "weapon_famas", CSW_FAMAS, 90 ); give_items( player, "weapon_deagle", CSW_DEAGLE, 35 ); } case 4: { give_items( player, "weapon_galil", CSW_GALIL, 90 ); give_items( player, "weapon_deagle", CSW_DEAGLE, 35 ); } } }
В заключение
Единственное, что можно ещё сказать, не делайте изображения больше чем 600х350 в MOTD.
Картинка с таким размером будет приемлемо выглядеть на всех разрешениях экрана.
Теперь вы можете создать свой магазин или что угодно, со своим дизайном и прочим.
SourceQuery.class.php
Перевод: Polarhigh
Источник: http://forums.alliedmods.net/showthread.php?t=198774
По поводу неточностей или ошибок в переводе пишите в комментарии.
Sorry, the comment form is closed at this time.