Май 102013
 

Авторы: Arkshine & xPaw

Как вы знаете вывести в MOTD страничку, сформированную в вашем плагине довольно просто, а вот добиться двустороннего взаимодействия MOTD уже не такая тривиальная задача. В этой статье вы узнаете, как это сделать.
Зачем же это нужно? Например, для создания своего классного магазина или выбора класса игрока, применений масса.

Вот пример:
80_screenshots_2012-07-31_00001

Требования:

Для осуществления взаимодействия вам понадобятся:

Так же вы должны в какой-то мере владеть:

  • 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

По поводу неточностей или ошибок в переводе пишите в комментарии.

Извините, форма комментирования закрыта в данный момент.