Найти тему
ZDG

Пирог #2. Реализация клиента и клиентского прокси

Вся подборка по рогаликам

Предыдущая часть:

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

Эти замечания важны, так как естественным образом выстраивается синхронный обмен.

Один ход игры состоит из следующих фаз:

  1. Клиент получает ответы с сервера
  2. Клиент рисует обновления
  3. Клиент ждёт команды
  4. Клиент отправляет команду на сервер

В то же время где-то вдали:

  1. Сервер получает запросы от клиента
  2. Сервер вносит изменения в состояние
  3. Сервер отправляет клиенту ответы

Это независимые друг от друга активности, так как происходят в разных местах, но они синхронизируются друг с другом за счёт ожидания данных.

Здесь можно увидеть ленту Мёбиуса или двухтактный двигатель: когда клиент заканчивает свой цикл отправкой send(request) внизу, сервер начинает свой цикл опросом get_requests() вверху, и наоборот.

Код клиента:

-2

Вышеописанная схема действий клиента реализована в методе update(), который будет вызываться в цикле.

В конструктор клиента передаётся proxy для него и клиент сразу же вызывает метод connect(). Клиентский прокси устанавливает соединение с серверным прокси, который сразу отправляет первый ответ, где находится GameState.

Этот ответ будет прочитан клиентом в update() с первым же вызовом proxy.get_responses(). То есть гарантируется, что при первом вызове нас уже будут ждать данные от сервера, GameState будет получено и цикл успешно запустится.

В методе render() клиент перебирает все полученные Response (важно то, что они могут прийти пачкой), и обрабатывает каждый из них в соответствии с типом. На данный момент обработки как таковой нет, и клиент просто печатает содержимое response.data (ему даже не не нужен GameState).

Код клиентского прокси (частичный):

-3

В методе connect() прокси открывает сетевой сокет до серверного прокси. Сразу после установки соединения серверный прокси вышлет Response.INIT. Ответ не читается здесь, но уже ждёт в буфере чтения сокета.

Метод send_request() отправляет данные серверному прокси. Он преобразует объекты Request от клиента в сырой текст для передачи, используя encode_request(). Текстовый запрос имеет простой формат "тип - пробел - данные".

-4

Метод get_responses() читает данные из сокета и преобразует их в понятные клиенту объекты Response с помощью метода decode_responses(). На данный момент все Response присылаются в виде строк, отделённых друг от друга символом новой строки ("\n"). А каждая строка имеет формат также "тип - пробел - данные".

-5

Когда прокси декодирует ответы сервера, его задача – обновить состояние game_state для клиента. Для каждого response вызывается метод update_state():

-6

Он проверяет: если тип ответа это INIT, то он перезаписывает все данные в game_state. А иначе... делает то же самое, так как другие варианты пока не предусмотрены.

Наконец, посмотрим на скрипт запуска клиента:

-7

Да, это вся главная программа, которая просто создаёт объекты клиента и прокси, а затем в цикле вызывает метод client.update().

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

Читайте дальше: