суббота, 23 июля 2011 г.

Разработка RoAOnline: этап 01


Ring of Asteroids Online набирает обороты! И вот уже можно плотненько рассказать непосредственно про сам этап разработки. Сегодня я закончил пилотные версии сервера и клиента. Я бы даже сказал очень пилотные)


В этом выпуске новостей:

  • модульность сервера и клиента;
  • алгоритмы и методы сетевого взаимодействия; 
  • что мне пришлось увидеть по результатам первого тестирования;


Загляну немножко в прошлое и замечу, что в первый этап разработки я так же включил наброски динамики полёта, которые продемонстрировал в этом посте.


Ну да в сторону формальности, начнём битву! анализ хода разработки.


Первое чем я занялся, как вы уже знаете, был набросок. Цель этого была общая картинка космоса с кораблём и конечно динамика корабля и звёзд. Всё это меня устроило, я сам себе дал согласие на развитие темы, т.е. я как бы являлся для себя и тем кто предлагает идею проекта с набросками и тем кто даёт на это инвестиции добро)

Затем, я начал формировать модульность сервера, т.е. другими словами продумывать ООП на серваке. Конечно всё начинается с MindManager’а бумажки, порисовав самое основное я открыл новый проект и начал уже непосредственно создавать модули и пустые классы. 
Тогда структура получилась более ясной:


Поясню немножко назначения модулей:


uCore – можно перефразировать как ServerEngine. Создаётся при запуске сервера, создаёт экземпляр TWorld и анализирует пакеты от клиентов.


uWorld – как не трудно догадаться содержит параметры локации и список всех загруженных кораблей. Так же обслуживает все корабли по прогрессу таймера из uCore.


uShip – класс и параметры корабля. В классе так же есть такие поля как id клиента.


uSocketManager – содержит класс TSocketManager, который занимается посылкой и приёмом дейтаграмм.


uGeneralPackageManager – содержит описание всех типов пакетов, которыми обмениваются клиент и сервер, а так же некоторые утилитные функции для пакетов.

Попридумывал список необходимых пакетов, он получился такой:

TPackageType = (
ptNone, // неопределённый пакет
ptPing, // пингование сервера для определения лага
ptServerError, // универсальный пакет ошибки с сервака на все случаи
ptLoginization, // логинизации
ptShipUpdate, // обновление параметров корабля
ptShipUnLoad, // перс вышелptKeyPress, // нажатие кнопки клиентомptKeyUnPress, // отжатие кнопки клиентомptChatMsg // чатовое сообщение
);
И типы данных под них.


Далее я начал долго и упорно капаться с UDP протоколом. В результате некоторых умозаключений выбрал организацию работы сокетов с помощью функции select.


Досье на функцию.
Название: select.
Модуль: WinSock.
Описание: Основное средство определения готовности сокета к немедленному выполнению какой-либо операции.
Прототип на Delphi:
function select(nfds: integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): longint;
Первый параметр оставлен только для совместимости со старыми версиями библы сокетов, в современных версиях он игнорируется. Следующие параметры содержат указатели на множества сокетов, которые проверяются. Последний параметр задаёт время ожидания – сколько времени функция будет ждать, пока наступит хоть одно событие.


Теперь, как я её использовал или алгоритм сетевого взаимодействия.


Для лучшего понимания приведу наглядную схему модулей клиента:
 

При подключении клиента, создаётся один сокет(в uSocketManager) для чтения и получения сообщения. Когда надо отправить пакет, то просто вызывается соответствующий метод TSocketManager'а, а вот с приёмом дейтаграммы чуть посложнее, мы вынуждены в каком-либо цикле постоянно вызывать функцию select с нулевым ожиданием и проверять оставила ли она в указанном множестве(в нашем случае это 1 сокет) сокет, и если да, то просто брать и читать из буфера сокета дейтаграмму а затем анализировать и производить соответствующие действия на клиенте. 
В роли цикла я выбрал прогресс каденсера в котором вызывал специальный метод в TSocketManager'е. И аналогично на сервере.


Ну и наконец, что же я увидел по результатам первого тестирования.


Скажу сразу, ожидания не оправдались: из-за постоянных вызовов функции select в каденсере, сервер грузил проц. Движение корабля было дёрганным и отклик затормаживался…


“- Доктор, скажите, а я буду ходить???...

- Да, но только под себя.”


Короче картина не из приятных)


Итоги

Честно говоря, чуть не сорвался сделать всё на своём любимом TCP(благо уже наработанный плацдарм). Но осадил горячку и стал подумывать о структуре UDP с помощью функций всё-таки второй версии библиотеки - WinSock2Api. 
Вообщем, это только начало)
 

2 комментария:

  1. Клиент-серверное взаимодействие для меня темный лес, но рискну предположить, что придется пускать select параллельным потоком. Хотя нет гарантий, что и это сильно поможет.

    Скрещиваю пальцы, чтобы у тебя все получилось.

    P.S. После долгого сидения на форуме гд.ру любой проект с суффиксом/префиксом Online вызывает у меня подозрение :)

    ОтветитьУдалить
  2. perfect daemon, с функцией select отдельная нить не принципиальна.
    Спасибо за поддержку.
    А насчёт подозрений к префиксу, то это точно только от геймдева))

    ОтветитьУдалить