Warcraft 3: еще о синхронизации

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


Синхронизация сетевых игр

Некоторые из контекста прошлой статьи не поняли, почему случайные числа на всех сторонах остаются одинаковыми, хотя и не пересылаются. Я всё же сказал причину:
Нужно всего-то записать ключ-зерно для генератора случайных чисел и все действия, предпринимаемые игроками.
Как и в большинстве игр с подобной сетевой инфраструктурой,  WC3 использует генератор псевдослучайных чисел. Грубо говоря, это алгоритм, который из представленного числа создает ряд чисел некоторой длины, при этом для одного и того же заданного ключа ряд будет одинаков. В этом ряду может быть много членов или мало — в зависимости от используемого алгоритма. Еще в 1997 году был придуман алгоритм получения ряда из 2^19000 чисел, но подобные монстры в играх не нужны.



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

Ключ-зерно для генерации ряда передается от хоста игрокам в самом начале загрузки карты. После этого все клиенты работают с одним рядом. Изменить его своими руками невозможно — только с помощью кода. При должной сноровке можно создать хак, который будет подсматривать в этот ряд и перед нужным моментом перебирать рандом до нужной позиции. Я даже знаю уникума, который запарился этим и даже добился некоторых успехов. Не идеально, но работает.


Не синхронизируемые вещи

Будучи людьми достаточно образованными, разработчики Blizzard понимали, что переносить на сетевое взаимодействие стоит только объекты, которые реально «работают» в игре. Синхронными должны быть все agent — агенты игры, которые могут быть интерактивными. Это:
ability, boolexpr, button, defeatcondition, dialog, effect, event, fogmodifier, force, gamecache, group, hashtable, leaderboard, location, multiboard, multiboarditem, player, quest, questitem, rect, region, sound, timer, timerdialog, trackable, trigger, triggercondition, widget (destructible + unit)
Все остальные сущности не требуют жесткой синхронизации и могут создаваться на компьютерах игроков локально, не пересылаясь по сети, без страха потерять синхронизацию:
aidifficulty, alliancetype, attacktype, blendmode, camerafield, camerasetup, damagetype, effecttype, eventid, fogstate, gamedifficulty, gamespeed, gamestate, gametype, image, itempool, itemtype, lightning, mapcontrol, mapdensity, mapflag, mapsetting, mapvisibility, pathingtype, placement, playercolor, playergameresult, playerscore, playerslotstate, playerstate, race, racepreference, raritycontrol, soundtype, startlocprio, terraindeformation, texmapflags, texttag, triggeraction, ubersplat, unitpool, unitstate, unittype, version, volumegroup, weapontype, weathereffect
Свечку не держал, т.к. большинство этих типов весьма уникальны и не используются в доте вообще, но все они должны быть асинхронными.

Это значит, что нельзя создать юнита только на одной машине и ожидать, что все игроки останутся в одной игровой стадии. Конечно, если с юнитом никто и никак не будет взаимодействовать, не будет оказывать влияния на игру, то — возможно. А вот текстовые надписи или эффекты молний можно создавать сколько душе угодно без вреда окружающим.

Всем известно, что юниты в игре проигрывают разные анимации в зависимости от условий. Это и уникальная анимация замаха при срабатывании  критического удара, и просто срабатывание рандомной анимации во время простоя, например, когда  тролль делает бэкфлип. Естественно, анимация ни на что не влияет, поэтому её синхронизировать тоже не нужно. Тем не менее, она работает на том же генераторе случайных чисел. Зато при выборе анимаций из списка, например, если их несколько на одно и то же действие, срабатывает уже реальный генератор случайных чисел. Например, в одном и том же реплее один и тот же юнит может проиграть анимацию №1, а при следующем просмотре проиграть №2. Этому есть простое объяснение — модель у клиента может быть любой, иметь 20 или 1 анимацию, поэтому выбор должен производиться чем-то еще, а не стандартным генератором.

Сброс зерна

В кампаниях  WC3 активно используются видеовставки на движке игры. Во время их работы важно добиться того, чтобы все юниты выполняли всегда одну и ту же анимацию. По сути, это гарантируется тем, что у большинства итак лишь одна анимация на действие. Но генератор случайных чисел всё равно нужно переставить так, чтобы у троллей не возникало желания делать бэкфлипы. Поэтому, по умолчанию, переход в формат театра сопровождается со сбросом генератора на зерно по ключу 0. Но это же можно заабузить!



С бородатых времен, когда мапхаки были молодые, а фрог только-только перешел от кодинга методом формочек на настоящий код, в доту была внедрена функция -ah. Не то что бы она оказалась проблемой для мапхаков — скорее, она даже позволила появиться псевдо-мапхаку в Dota Promode за счет подключения собственных локальных файлов. В дальнейшем, из-за возможности подобного абуза, функционал замены изображения был выпилен. Но больше интересно то, как реализована функция в целом.

Естественно, её изобрел вовсе не фрог. Система была разработана другими  WC3-кодерами для тех же целей. Суть была проста — юниты, которых игрок не видет по мнению игры, нужно делать полностью прозрачными и скрывать с карты. Благо, что в варкрафте есть функция, позволяющая изменить картинку-отображение юнита на миникарте. Подсунув пустое изображения, получится невидимый на миникарте юнит. Но в реплеях это было бы проблемой — ведь он всегда крутится от лица первого игрока. Поэтому нужно проверить, реальна ли игра, для чего и используется хак-абуз с переходом в режим театра и обратно. Побочным эффектом такого подхода является использование стандартной функции-обертки, которая, помимо прочего, и сбрасывает генератор псевдослучайных чисел на нулевой ряд.

Все рандомы с этого момента пойдут по уже известному ряду. На этом был основан абуз использования -random и -ah, чтобы получить нужного героя, не оплачивая 250 золотых. Он всегда будет одним и тем же, если никакое другое случайное событие не переведет указатель на следующее значение. Т.е. если кто-то напишет -roll — указатель сдвинется. Если где-то внутри игры пройдет триггер, который использует генерацию случайного числа — указатель сдвинется. Если кто-то кого-то ударит, указатель снова сдвинется из-за броска костей для подсчета атаки. Но техника -random -> ждать 5.9 секунд -> -ah будет всегда давать одного и того же героя без всякого риска, а при необходимости можно подобрать нужного спамом рандомных событий.

9 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.