Задача всего последующего текста, показать как я пытался написать быстрый сервер, в котором прототировать и вводить новые фичи будет так же просто, как и на клиенте.
Представьте себе диалог с вашим внутренним серверным программистом:
-Вот бы сделать особенность у персонажа, чтобы при каждом повышении уровня он бил всех монстров молнией
-Можно подумать..
-Но, если у игрока есть "шляпа судьбы", то он бы вызывал духа, который живет два хода
-Ээээ...
-Или, давай сделаем слепоту, чтобы игрок видел монстров не на своих местах, но остальные игроки видели мир как и прежде
*Ваш серверный программист с печалью в глазах смотрит в окно*
Начнем с небольшого отступления. ООП по-настоящему прекрасный концепт. Он особенно хорошо себя показывает в условиях одной среды: мы описали поведение объекта, спрятали "внутряк" и можем говорить ему что делать, не задумываясь над данными. Объект знает сам. Но, допустим, мы хотим отправить этот объект на другой сервис. Очевидно, что, что отправить поведение мы не можем и поэтому мы создаем еще один объект, кастрированную версию исходного, который передаем по сети. Это называется
mapping, или мапирование
У мапирования есть 4 существенные проблемы:
- Больше работы: нам нужно, как минимум, описать 2 метода: создание исходного из сетевых данных и наоборот
- Больше логики: это еще один слой, и многие наверное сталкивались с тем, как констукторы могут конфликтовать с маппингом. То есть объекты созданные естественным путем могут отличаться от тех, что мы создаем при сериализации
- Под все изменения протокола нужно подправлять маппинг
- Выделение памяти
Дальше снова пойдет речь о решении сетевых проблем, но сначала опишу задачу.
В "комнате" играют в игру X человек, в общем случае каждый получает свое сообщение. При этом большая часть данных за этими сообщениями общая. Но некоторые игроки будут получать информацию недоступную или отличную от других.
Из этих ограничений я сформулировал требования к интерфейсу создания сообщений (выше - приоритетнее):
- Писать наименьшие сообщения максимально быстро
- Понимать, было ли изменено сообщение, не писать пустое сообщение и не делать лишнюю сериализацию. Корректно перезаписывать дважды одну и ту же часть сообщени
- Избавиться от маппинга
- Обновлять часть сообщения. Например, у нас вложенная структура. Есть профиль пользователя, а в нем данные обо всех инвентарях и ресурсах. Обновляя ресуры, не отправлять весь профиль
- Это же касается игроков и монстров, которые в отсылаются в массиве. Хочется иметь возможность сессии X отправить изменения игрока Y. Это делается в том числе из соображений защиты. Любая информация, которая не предназначалась игроку, будет использована против вас
- Иногда я хочу работать с данными, но не отправлять изменения на клиент
- Возможность слать разные сообщения разным сессиям
- Я делаю кооперативное PVE и хочу иметь возможность быстро менять общие данные на личные данные игрока. Например, в процессе прототипирования может понадобиться иметь общий сундук на всех игроков, или уникальный сундук для каждого игрок
То есть, хотелось бы иметь возможность писать и общие сообщения и уникальные используя для общих сообщений общие объекты.
Всё серверное сообщение выглядит так