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;
function select(nfds: integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): longint;
Первый параметр оставлен только для совместимости со старыми версиями библы сокетов, в современных версиях он игнорируется. Следующие параметры содержат указатели на множества сокетов, которые проверяются. Последний параметр задаёт время ожидания – сколько времени функция будет ждать, пока наступит хоть одно событие.
Теперь, как я её использовал или алгоритм сетевого взаимодействия.
Для лучшего понимания приведу наглядную схему модулей клиента:
При подключении клиента, создаётся один сокет(в uSocketManager) для чтения и получения сообщения. Когда надо отправить пакет, то просто вызывается соответствующий метод TSocketManager'а, а вот с приёмом дейтаграммы чуть посложнее, мы вынуждены в каком-либо цикле постоянно вызывать функцию select с нулевым ожиданием и проверять оставила ли она в указанном множестве(в нашем случае это 1 сокет) сокет, и если да, то просто брать и читать из буфера сокета дейтаграмму а затем анализировать и производить соответствующие действия на клиенте.
В роли цикла я выбрал прогресс каденсера в котором вызывал специальный метод в TSocketManager'е. И аналогично на сервере.
В роли цикла я выбрал прогресс каденсера в котором вызывал специальный метод в TSocketManager'е. И аналогично на сервере.
Ну и наконец, что же я увидел по результатам первого тестирования.
Скажу сразу, ожидания не оправдались: из-за постоянных вызовов функции select в каденсере, сервер грузил проц. Движение корабля было дёрганным и отклик затормаживался…
“- Доктор, скажите, а я буду ходить???...
- Да, но только под себя.”
Короче картина не из приятных)
Итоги
Честно говоря, чуть не сорвался сделать всё на своём любимом TCP(благо уже наработанный плацдарм). Но осадил горячку и стал подумывать о структуре UDP с помощью функций всё-таки второй версии библиотеки - WinSock2Api.
Вообщем, это только начало)


Клиент-серверное взаимодействие для меня темный лес, но рискну предположить, что придется пускать select параллельным потоком. Хотя нет гарантий, что и это сильно поможет.
ОтветитьУдалитьСкрещиваю пальцы, чтобы у тебя все получилось.
P.S. После долгого сидения на форуме гд.ру любой проект с суффиксом/префиксом Online вызывает у меня подозрение :)
perfect daemon, с функцией select отдельная нить не принципиальна.
ОтветитьУдалитьСпасибо за поддержку.
А насчёт подозрений к префиксу, то это точно только от геймдева))