Голосование

Сколько Вам лет?

10 или меньше
10-12
12-14
14-16
16-18
18-20
20-25
25-30
Более 30



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

 

Лента 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-2016 "DS"

E-mail: Отправить письмо


ВКонтакте   Twitter   Facebook