Мониторинг Counter-Strike

Елена Бадло, Сергей Бадло  (г. Запорожье)

Часто задают вопросы: «Как мониторить CS (в народе — контру)? К примеру, имеется игровой локальный или Интернет-сервер. Вопрос: как достать из него информацию о текущей игре? Как узнать о текущей карте, игроках, но не средствами PHP?». Сегодня мы покажем вам, что ничего сложного в этом нет, а для этого создадим тестовый запросчик-монитор к серверам Counter-Strike.

Предпосылки реализации…

Прежде всего, скачаем и запустим любую программу для перехвата и анализа трафика Интернета и локальной сети, к примеру, тот же CommView http://www.tamos.ru/download/main/. Такие программы, называемые снифферами, собирают информацию о данных, проходящих через модем (dial-up) или сетевую карту (будь-то Ethernet или Wi-Fi) и декодируют анализируемые данные, что позволяет исследовать даже отдельные пакеты. C помощью сниффера мы отследим реакцию на запросы при отправке типичных команд ‘яяяяTSource Engine Query’ и ‘яяяяinfostring’ к игровому серверу (см. протокол обмена https://developer.valvesoftware.com/wiki/Server_queries):

Packet ID: 36 TTL: 128

Packet size: 53 bytes type: UDP

Source IP : 10.4.145.120: 28941

Destination IP : 10.4.134.251: 34665

————————- Packet dump ——————————

000000 45 00 00 35 E4 BA 00 00 | 80 11 29 82 0A 04 91 78 E..5……)….x

000010 0A 04 86 FB 0D 71 69 87 | 00 21 9D 7E FF FF FF FF …..qi..!.~….

000020 54 53 6F 75 72 63 65 20 | 45 6E 67 69 6E 65 20 51 TSource Engine Q

Пример простого сниффера с исходниками и без ограничений приложен к ресурсам в конце статьи.

Как видим, используется обмен по протоколу UDP. Вот с ним-то и будем работать: можно через сокеты* (чаще WinSock) <http://msdn.microsoft.com/ru-ru/library/dd335942.aspx, можно и через разного рода готовые обертки, облегчающие труд программиста. Нам больше импонирует модуль TNMUdp из пакета FastNet (это сервер-клиент в одном флаконе). Он когда-то шел в стандартной поставке Delphi6, в последующих же версиях его убрали. Но это не страшно, к тестовому проекту он приложен в полном виде.

—————————————————

* UDP-сокеты не требуют установления логических соединений и обычно применяются для широковещательной и многоадресной (multicast) связи. Как известно, в UDP нет средств надежной доставки сообщений и контроля правильного порядка пакетов, поэтому за обнаружение потери пакетов, устранение таких проблем и упорядочение пакетов отвечает приложение-получатель.

Давай уже практику!

— Скажет нетерпеливый читатель. И то ведь правда. Запустим любимую среду компиляции TDL (для некоммерческих разработок) и набросаем для визуальности тестовый проект запросчика-монитора :

Среда компиляции. Окно тестового проекта

Перенесем на форму проекта: один таймер TTimer (для автопосылки запросов серверу), два TCombobox (первый — для выбора IP адреса игрового сервера, второй — для выбора строки командного запроса), окно отображения ответа сервера TMemo и кнопку TSpeedButton.  Модуль (класс) TNMUDP устанавливать не будем. Достаточно подключить его динамически, делается это просто (создаем класс через Create() и заполняем все необходимые свойства):

// подключаем класс без установки компонента-

 udp:= TNMUDP.Create(nil);

 // назначаем порт и хост для работы

 udp.LocalPort  := 27015;

 udp.RemoteHost := ‘localhost’;

 udp.RemotePort := 27015;

 udp.ReportLevel:= 1;

 // назначаем событие приема ответа

 udp.OnDataReceived:= udpDataReceived

Теперь сформируем запрос к игровому серверу в потоке. Для этого воспользуемся методом SendStream() класса TNMUDP. Реализация подобного подхода представлена в листинге 1:

ЛИСТИНГ-1

<pre>{ ФОРМИРОВАНИЕ ЗАПРОСА К СЕРВЕРУ }

procedure Tform1.send(ip,mes: string);

var msg: TStringStream;

begin

 try

  msg:= TStringStream.Create(»);

  msg.Position:= 0;

  msg.WriteString(mes);

  UDP.RemoteHost:= ip;

  UDP.SendStream(msg);

  msg.Free

 except end

end;

Далее немного кода для расшифровки ответа с сервера контры :

procedure TForm1.SpeedButton1Click(Sender: TObject);

begin

 memo1.Clear;

 send(server.Text, sz.Text)

end;

Рассмотрим процедуру приема и парсинга данных из ответа сервера. Для того, чтобы принять данные воспользуемся методом ReadStream() класса TNMUDP, после чего просто прочитаем полученный поток в стринговый класс TStringStream (что позволит проводить нам анализ над привычной строкой). В ответе содержится информация о том: установлен-ли пароль на сервере, количество игроков и ботов, наименование текущей карты (игры). «Чем искать?», — спросите вы. Да уже известными вам функциями POS() и POSEX() (вспомните статью «Мобильные технологии. ICQ автобот» из выпуска #154 «Магии ПК» http://www.magic-pc.spb.ru/journal/201112/19/01.php?journal=last). Реализация подобного подхода представлена в листинге 2:

ЛИСТИНГ-2

{ АНАЛИЗ ДАННЫХ ОТ ИГРОВОГО СЕРВЕРА }

procedure TForm1.udpDataReceived(Sender: TComponent;

  NumberBytes: Integer; FromIP: String; Port: Integer);

var s ,password,countplayers : string;

    msg: TStringStream;

begin

 //декодирование потока

 msg:= TStringStream.Create(»);

 msg.Position:= 0;

 UDP.ReadStream(msg);

 s:= msg.DataString;

 msg.Free;

 

 label1.caption:= ‘Ответ: ‘ + s;

 

 if pos(‘яяяяinfostringresponse’,s) = 1 then begin

   if copy(s,pos(‘password’,s)+10,pos(‘os’,s)-pos(‘password’,s)-10) = ‘0’ then

    password:=’No’

   else password:=’Yes’;

 

   if pos(‘bots’,s) <> 0 then

    countplayers:=copy(s,pos(‘players’,s)+9,pos(‘proxytarget’,s)-pos(‘players’,s)-9)+’

            (‘+copy(s,pos(‘bots’,s)+6,pos(‘gamedir’,s)-pos(‘bots’,s)-6)+’)

            / ‘+copy(s,pos(‘max’,s)+5,pos(‘bots’,s)-pos(‘max’,s)-5)

   else countplayers:=copy(s,pos(‘players’,s)+9,pos(‘proxytarget’,s)-pos(‘players’,s)-9)+’ (—)

   / ‘+copy(s,pos(‘max’,s)+5,pos(‘gamedir’,s)-pos(‘max’,s)-5);

   memo1.Lines.Add(‘Game:  Counter-Strike’);

   memo1.Lines.Add(‘from:    ‘+FromIP+’:’+inttostr(Port));

   memo1.Lines.Add(‘Mod (Protocol):   ‘+copy(s,pos(‘description’,s)+13,pos(‘hostname’,s)

   -pos(‘description’,s)-13)+’ (‘+copy(s,34,2)+’)’);

   memo1.Lines.Add(‘Hostname:    ‘+copy(s,pos(‘hostname’,s)+10,pos(‘map’,s)-pos(‘hostname’,s)-10));

   memo1.Lines.Add(‘Map:         ‘+copy(s,pos(‘map’,s)+5,pos(‘type’,s)-pos(‘map’,s)-5));

memo1.Lines.Add(‘Password:    ‘+password);

   memo1.Lines.Add(‘Players (Bots) / MaxPlayers: ‘+countplayers);

   memo1.Lines.Add(»);

   if pos(‘яяяяI0’,s) = 1 then memo1.Lines.Add(copy(s,7, posex(‘|’,s,7)-7));

 if pos(‘яяяя’,s) = 1 then  memo1.Lines.add(s);<a href=»picture6big.jpg» target=»_blank»>

   exit;

  end;

end;

 

<img src=»picture7.jpg» width=»254″ height=»406″ border=»0″ align=»left» hspace=»10″ alt=»»>

Вот, в принципе, и все. Нам остается осуществить компиляцию проекта, жмакнув по клавише <F9>. Для пробы  запустим локальный CS-сервер:

Ок? Теперь протестируем наш запросчик-монитор на известных игровых серверах

Принимаем награды

 Заключение

Удачной игры! Полное описание протокола обмена для серверов CS вы найдете на ресурсе https://developer.valvesoftware.com/wiki/Server_queries.

Тестовые скрипты (на PHP), исходный код и компиляция тестового запросчика-монитора (файл csmn.zip) доступны на сайте автора [4].

Ресурсы

  1. Исходники и компиляция бесплатного сниффера пакетов http://raxp.radioliga.com/cnt/s.php?p=snp.zip
  2. UDPSock. Класс для работы по UDP http://raxp.radioliga.com/cnt/s.php?p=udps.zip
  3. IDE TurboDelphi-Lite (для некоммерческих разработок) http://www.andyaska.com/?act=download&mode=get&id=34
  4. Исходники и компиляция тестового проекта запросчика-монитора http://raxp.radioliga.com/cnt/s.php?p=csmn.zip

 

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *