Голосование

Как Вы обычно находите нужную информацию?

При помощи поисковых систем
Специализированные сайты
Экспертное мнение (форумы, LiveJournal, знакомые)
С помощью онлайн-справочников (Wikipedia, словари)
Печатная продукция (книги, журналы)



Посмотреть результаты
Другие опросы ...

 

Лента RSS, новости сайта Новости сайта
Лента RSS, новости форума Новости форума
  Bookmark and Share

Архив исходников

   
  Базы данных
  Графика & Мультимедиа
  Сети & Интернет
  Система
  Разное
   

Кнопки, Ссылки и Баннеры ...

 


Automatic translation


English German French Italian Spanish
Portuguese Greece Japan Chinese Korean


Ссылки и Баннеры


скрыть

 

Delphi Sources

Delphi Sources

СТАТЬИ

 

. : Написание игрового Интернет сервера : .

 

Введение
   Наверняка, Вы играли в такую игру как "Warcraft 3". И было бы просто прекрасно, если Вы играли по Интернету, ибо в этом случае Вы бы могли созерцать и испытать в действии то, что называется "Battle.net". В любом случае я поясню. Это некий "портал" благодаря которому игроки всего Интернета могут запросто найти работающие игровые сервера не выходя из игры. Что значительно облегчает им жизнь, т.к. отпадает необходимость заранее договариваться с соперниками при помощи чатов и подобных средств.
   То, о чем я буду говорить в этой статье, поможет Вам создать подобное для своей игрушки. Сам метод достаточно прост и почти не имеет отрицательных моментов. Из-за отсутствия информации по данной теме мне пришлось самому, методом проб и ошибок, писать подобный портал (далее "арена") для своего проекта TFK (
http://timeforkill.mirgames.ru).

Описание метода
   Итак, опишу то что нам понадобится для реализации:
    - Хостинг с поддержкой php;
    - Ваша игра с работоспособным сетевым кодом :).
   Первый пункт я надеюсь не вызывает больших проблем у начинающих "игрописателей", т.к. существует множество сайтов, предоставляющих бесплатный домен с поддержкой php. К примеру, для TFK он был предоставлен сайтом
http://mirgames.ru.
   А вот со вторым пунктом придется немного попариться, впрочем, это уже тема для отдельной статьи...
   Так как в данной статье я использовал PHP, то потребуется знание его основ. Впрочем, при желании, перевод на другой язык написания web страниц не составит большого труда.

   Итак, имеем в Интернете домен на котором размещен наш скрипт "арены". Есть игра-клиент, которой нужно узнать кол-во доступных серверов, и при необходимости создать свой.
   Что нам нужно от "арены"? Всего-навсего получить список серверов в виде "IP:Port IP:Port IP:Port..." и зарегистрировать новый.
   Как это будет происходить? Да очень просто! Посредством HTTP запросов.
   Так как нет идеальных решений, какие минусы у данного метода?
    - Серверы находящиеся за шлюзом не будут видны остальным клиентам, т.к. даже сама игра-сервер без понятия на каком external порту она висит.
    - При падении хостера (сайта) арена шлепнется вместе с ним! Но это относится уже к форс-мажорным обстоятельствам... ;).
   А какие же плюсы?
    - Относительная простота реализации;
    - Легко разместить такую арену в локальной сети;
    - Не требует восстановления после различных ЧП :).

Реализация
   В этом разделе описаны основные процедуры необходимые для воплощения нашей мечты в реальность. Работа с ареной делится на 2 части:
   1) Подача HTTP запросов и обработка ответов игрой;
   2) Обработка запроса скриптом на арене.

   Всего будет 2 вида запросов: view и ping.
   VIEW необходим для получения списка серверов. Будет выглядеть следующим образом:
   Запрос: http://host/?action=arena&mode=view.
   Ответ : 212.100.15.45:25666 192.10.38.212:25666.
   Т.е. в ответе мы видим, что на данный момент на арене находятся 2 сервера на портах 25666.

   PING для оповещения арены о том что сервер жив и удалять его из списка пока нет никакой необходимости.  Вы могли заметить то, что нет запроса на регистрацию сервера на арене, т.к. в качестве регистрации выступает постоянный "ping" посылаемый им. Сам же запрос "ping" следует посылать раз в несколько десятков секунд (20-40).
   Запрос: http://host/?action=arena&mode=ping&port=25666
   Ответ нам абсолютно не нужен :).

Реализация на стороне игры
   Соответственно нам теперь необходимо знать как отправить HTTP запрос и получить на него ответ. Все проще чем может показаться. Приведу всего одну процедуру использующую возможности WinSock:
 

 function Arena(const mode: string; get: boolean): string;
 const
  host = 'host.ru';
  port = 25666;
 var
  wData : WSADATA;
  addr  : sockaddr_in;
  sock  : integer;
  error : integer;
  buf   : array [0..1023] of Char;
  str   : string;
  phe   : PHostEnt;
 begin
 //Инициализация сокета
 Result := '';
 WSAStartup($0101, wData);
 phe := gethostbyname(PChar(string(host)));
 if phe = nil then
  begin
  WSACleanup;
  Exit;
  end;

 sock := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

 if sock = INVALID_SOCKET then
  begin
  WSACleanup;
  Exit;
  end;

 addr.sin_family := AF_INET;
 addr.sin_port   := htons(80);
 addr.sin_addr   := PInAddr(phe.h_addr_list^)^;

 error := connect(sock, addr, sizeof(addr));
 if error = SOCKET_ERROR then
  begin
  closesocket(sock);
  WSACleanup;
  Exit;
  end;

 // Составляем строку запроса
 str := 'GET http://' + host + '/?action=arena&mode=' + mode;
 if mode = 'ping'
 then str := str + '&port=' + IntToStr(port);
 str := str + ' HTTP/1.0'#13#10#13#10;

 // отправляем
 send(sock, str[1], Length(str), 0);

 // Если нужен ответ то принимаем
 if get then
  begin
  ZeroMemory(@buf, 1024);
  error := recv(sock, buf, 1024, 0);
  while error > 0 do
   begin
   Result := Result + Copy(buf, 0, error);
   error  := recv(sock, buf, 1024, 0);
   end;
  end;
 // Закрываем сокет – завершаем работу с сетью
 closesocket(sock);
 WSACleanup;
    
 // Вырезаем из ответа то что нам нужно, т.е. отрезаем
 // HTTP заголовки
 if get and Result <> ''
 then Result:=Copy(Result, pos(#13#10#13#10, Result)+4,
                                              Length(Result));
 end;


   В функцию передается всего 2 параметра mode и get.
   Первый является именем запроса, а второй означает нужен ли нам результат обработки запроса. Соответственно вызов этой функции для наших двух запросов будет выглядеть следующим образом:
 

 Str := Arena('view', true); // для получения списка серверов
 Arena('ping', false);       // сообщить арене что наш сервер
                             // живее всех живых


   При вызове этой функции игра на некоторое время может подвиснуть. Для того, чтобы избежать сего безобразия можно воспользоваться потоками. Функция работающая в потоке практически никак не будет влиять на деятельность игры, но возникает риск некорректного доступа к общим ресурсам для игры и потока.
   Приведу пример:
 

 procedure Arena_PingThread;
 begin
  Arena('ping', false);
 end;
    
 procedure Arena_Ping;
 var
  id : DWORD;
 begin
  CreateThread(nil, 128, @Arena_PingThread, nil, 0, id);
 end;


   После получения списка серверов запросом "view" игра должна разослать им запросы о их текущем состоянии (карта, игроки и т.д.) В этот момент отбрасываются "умершие" сервера, ибо ответа от них не придет.

Реализация на стороне интернет сервера
   Итак, с игрой разобрались, теперь осталось написать скрипт!
   В запросах мы посылаем ключевое слово "action=arena" благодаря чему помимо арены на данном домене может висеть полноценный сайт.
   Для того, чтобы определить адресуется ли данный запрос арене, в index.php необходимо (желательно в самом начале) написать следующее:
 

 if ($action == 'arena')
 {
  include 'arena.php';
  die();
 }


   Это означает, что в случае того, когда захотят "пообщаться" с ареной, будет запущен скрипт арены для обработки запроса и дальнейшее выполнение скрипта index.php прекратится.

 А вот и сам код arena.php:
 

 <?php
  //В этом файле будет храниться список активных серверов
  $list_file = 'db/arena_list.txt';
  // Узнаем IP адрес отправителя запроса
  $ip = $_SERVER['REMOTE_ADDR'];
  // Читаем номер порта из запроса
  $port = intval($_REQUEST['port']);
  // Это от хитрых кулхацкеров ;)
  if (!($port >= 1024 && $port <= 65500))
   $port = 25666;
  // Читаем файл-список
  $lst = file($list_file);
  // В переменной $time теперь хранится текущее время
  $time = time();
  $j = -1;
  $i = 0;
  // Удаляем “мертвецов” и попутно ищем адрес отправителя
  // в этом списке
  while ($i < count($lst)) {
   $lst[$i] = trim($lst[$i]);
   list($l_ip, $l_port, $l_time) = explode(":", $lst[$i]);
   // Если время с предыдущего пинга превысило 45 секунд
   // – его явно уже нет
   if ($l_time < ($time - 45)) {
    for ($t = $i; $t < count($lst) - 1; $t++)
     $lst[$t] = $lst[$t + 1];
    unset($lst[count($lst) - 1]);
    continue;
   }
   if ($l_ip == $ip) $j = $i;
   $i++;
  }

  // Обработка запроса
  switch ($mode) {
   case 'view':
    for ($i = 0; $i < Count($lst); $i++) {
     // Вывод очередного IP:Port из списка
     list($l_ip, $l_port, $l_time) = explode(":", $lst[$i]);
     echo $l_ip.':'.$l_port.' ';
    }
    break;
   case 'ping':
    if ($j == -1)
     // Если пингуется впервые, значит новый сервер - добавляем
     array_push($lst, $ip.':'.$port.':'.$time);
    else {
     // Обновляем информацию для сервера
     // Заметьте, что при смене порта на сервере
     // на арене он тоже изменится
     list($l_ip, $l_port, $l_time) = explode(":", $lst[$j]);
     $lst[$j] = $l_ip.':'.$port.':'.$time;
     }
    break;
  }

  // Обновляем список серверов в файле-списке
  $f = fopen($list_file, "a+");
  flock($f, LOCK_EX);
  ftruncate($f, 0);
  for ($i = 0; $i < count($lst); $i++)
   fwrite($f, $lst[$i]."\n");
  fflush($f);
  flock($f, LOCK_UN);
  fclose($f);
 ?>


   Файл со списком серверов должен находиться в "db/arena_list.txt" с атрибутами разрешающими его изменение.
   Вот собственно и все! Дальше дело стоит за Вашей фантазией...
   Если заметите какие-либо ошибки или недоработки данной реализации - буду рад Вас выслушать.

Удачи!

 

Дата: 11.01.2006, Автор: XProger.






Назад

   

 















































































































































































 

© 2004-2017 "DS"

Отправить письмо / Реклама


ВКонтакте   Facebook   Twitter