Организация данных при реализации "тумана войны"
От: johny5 Новая Зеландия
Дата: 22.06.07 02:31
Оценка: 5 (1)
Пишу сюда, потому что проблема и решение C++ specific. Если не прав, переместите куда либо ещё (только не в игровой форум плз!!).

Краткая информация. Пишу космическую онлайн стратегию. Клиент-Серверная модель.

Игра начинается так: собираются несколько игроков, генерят себе игровую партию, играют. Игровая партия длиться в среднем 1,2 месяца. Потом выявляется победитель и начинается новая партия. С точки зрения софта, игровая партия это набор данных обо всей игровой вселенной.
Обсчёт игровой партии это продвижение модели на 1 квант времени вперёд.
В игровой партии есть флоты.
Флот состоит из списка кораблей.
Корабль ссылается на инфу о корабельном дизайне.
Корабельный дизайн в свою очередь ссылается на корпус и список корабельных компонентов.


Хочу обсудить проблемы, которые возникают при попытке реализовать концепцию "туман войны" и прочих факторов, которые ведут к разному представлению игровой партии на клиенте и сервере.


Существующие проблемы модели
1. Поддержка передачи вычислимой инфы без передачи аргументов для вычислений
2. Явное указание аттрибутов, которые видны игроку в данный момент
3. Поддержка "истории" аттрибутов для конкретного игрока
4. Поддержка хранения различных аттрибутов в данных о флоте, которые специфичны только серверу или только клиенту и предназначены для сериализации на винт но не предназначены для передачи по сети.


  1. В глобальной игровой партии количество кораблей во флоте можно легко получить просто спросив ships.size().

    Теперь вступает в роль туман войны.
    Игрок в игровом мире видит не всё, но было бы всё просто если бы видимость делилась на вижу/не вижу. Она делится на более плавные градиенты, в частности, возможно такое, что игрок будет видить вражеский флот и количество его кораблей но не видеть из каких кораблей он состоит.
    Это приводит к тому, что в ships я не могу передать ничего, потому что в контейнере лежат конкретные описания кораблей, а просто заполнять всё это мусором лишь бы только ради размера ну как то будет странно. Кроме того, для некоторых случаев, например коэффициент невидимости флота будет явно виден вражьим игроком, хотя в модели он задаётся длинным путём через ships -> дизайн_корабля -> корабельные_модули -> модули_прозрачности да ещё и по отношении к общей массе флота. В последнем случае всё это "эмулировать" будет очень тяжело.

  2. Серверу нужно как то указывать, какие аттрибуты из переданного класса игрок видит в данный момент а какие нет. Логикой выбора "видимых аттрибутов" по идее занимается сервер.
    Но я не могу выкинуть из класса аттрибуты, которые на самом деле клиент не "видит", т.е. не имеет информации о них. Идея пустоты заполнять '-1'-ами ломается при попытке заполнить ими какойнть вектор с классами, причём когда пустой вектор это тоже валидное состояние. Использовать boost::optional для каждого мембера это имхо кощунство, кроме того это не лечит от проблемы №3

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

  4. Во время обсчёта партии возникает необходимость сохранять временные данные в модели, которые для клиента совершенно не важны. Например количество выделенных уже ресурсов на выполнение какой то команды флота, очень нужная штука для "откатов" если команда будет вдруг отменена. Но это количество ресурсов игроку не нужны. максимум — ему нужно будет передавать процент выполнения команды.
    Такие данные также необходимо сериализовать на винт, потому что при перезапуске сервера, всё должно работать в точности так же как было до останова.




Для решения вышеобозначенных проблем, я пробовал 2 выхода из этой ситуации

  1. Разбил параметры флота на первичные и вычислимые, вычислимые я стараюсь пересчитывать каждый раз, когда что то меняется в первичных данных флота либо в окружении флота, т.е. в данных партии. Естественно я делаю всё это ручками. И естественно, при передаче по сети я всё время имею проблемы "не забыть пересчитать" (из-за сети я не могу сделать callback привязки, потому что в момент передачи сам флот оказывается как бы "отвязан" от партии и поэтому всё равно всё приходится делать ручками".
    И первичные и вычислимые являются сериализуемыми(!). Естественно вылазит гемор с постоянной проверкой целостности модели после чтения с винта, передачи по сети (защита от юных хакеров), потому что для своего флота игрок будет видеть как ships.size() так и ship_num и они должны быть всегда равными.


  2. Я сделал в модели понятие EnemyFleet. Из за этого у меня класс GameParty разбился на 2 класса, ServerGameParty и ClientGameParty. Из за этого же, во многих местах клиенского кода у меня повылазило большое дублирование кода, например выдача информации о флоте, когда изменяется лишь способ получения с дополнительными проверками аттрибута "видимости", т.е. что на самом деле из полученной информации игроку сейчас видно.
    Кроме того, усложнился код проекта вообще, многие классы, которые могли работать с GameParty, теперь вынуждены поддерживать оба класса.
    Сервер получил код конвертации данных из ServerGameParty в ClientGameParty и обратно, в общем телега начала еле проворачиваться.


Для 3й и 4й проблемы я сделал над-структуры, которые ссылаются на данные партии.



Но ни то ни другое решение простотой и безопасностью не блещут. Я чуствую что все эти проблемы как то можно решить одним способом, потому что проблемы все близки.


Какие решения в данном случае могли бы быть, что вы посоветуете? Как эти проблемы решаются в реальных игровых проектах?

Спасибо.



22.06.07 12:15: Перенесено модератором из 'C/C++. Прикладные вопросы' — Хитрик Денис
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.