11.09.2009
Привет, класс
Меня зовут CCP GingerDude и меня обязали научать вас всех о новых изменениях, которые я сделал на нашем имитационном движке Destiny, так как это имеет отношение к давней и неуловимой проблеме, известной как "десинк" (или десинхронизация).
Теперь, если вы рассядетесь по местам, снимете наушники и выключите своё проклятое барахло, с которым вы постоянно возитесь, мы сможем начать.
Так, чё такое Destiny?
Destiny, как уже было указано выше, – имитационный движок EVE. Если задаться вопросом о его назначении, то это кусок кода, который даёт возможность вашему кораблю двигаться, поворачиваться, сталкиваться, совершать варп-прыжки и маскироваться. Хотя, это не всё что он делает. Короче говоря, он поддерживает состояние всех кораблей и объектов в космосе. Он запущен как на сервере, так и на каждом клиенте, а основная идея состоит в том, что, при любом развитии событий, на стороне клиента любого игрока всё должно происходить точно так же, как и на стороне сервера. Принимая во внимание детерминированную природу компьютеров, получается, что один и тот же код, с одними и теми же данными, должен приводить к одним и тем же результатам. Единственное различие между клиентской и серверной стороной состоит в том, что сервер обрабатывает каждый сценарий с объектами в нём для данной системы, тогда как клиентская часть обрабатывает только те объекты, которые он видит и которые относятся с той сцене, в которой игрок находится. Другими словами, клиент имеет дело только с частью того, с чем взаимодействует сервер.
...и этот десинк, о котором вы говорите, это... что?
Очень хороший вопрос. На протяжении нескольких лет термин "десинк" применялся к различным, не связанным друг с другом проблемам, большинство которых даже не относятся к такому классу ошибок. Можно выделить следующие из них:
- Лаг – это не десинк
Когда вы неожиданно взрываетесь, хотя верили, что у вас всё ещё оставалось немного структуры, или прыгаете в сцену и сразу же подвергаетесь безжалостному убийству, ещё до того, как вы даже просто заметите врага в овервью, вы сталкиваетесь только с запаздыванием или утерей сетевых пакетов, или они приходят к вам в неправильном порядке. Это неотъемлемая часть работы каналов передачи данных, и на это мы повлиять никак не можем, кроме как попытаться компенсировать потерю, что мы обычно и делаем. - Расфазировка – это не десинк
Ваш щит и капаситор показывают неверные значения, например, когда вы получаете повреждения брони, хотя щит до сих пор отображается как целый; или наоборот, когда вы не получаете повреждения брони, хотя щит уже кончился. Примерно так же происходит и с капаситором. Всё это – расфазировка, которая случается из-за того, что таймер на вашем компьютере выдаёт немного различные скорости. Ваш клиент пересинхронизируется, когда вы входите в игру, вылетаете со станции или прыгаете. Больше всего шансов поймать расфазировку – долго находиться в одном и том же месте. Это обычно не сильно заметно, но случается. В прошлом у нас бывали моменты, когда таймер на сервере или прокси-системе Tranquility немного отклонялся в сторону, показывая похожие симптомы, но мы это исправили. - Сдвиг времени – это не десинк
Симуляции клиента и сервера могут идти немного не в ногу, то есть ваш клиент может на пару мгновений отставать или опережать сервер. Это нормально и происходит, по большей части, из-за разных нагрузок на разных машинах. Всё это полностью ожидаемо и корректно обрабатывается. Вы можете, например, заметить это в виде "эффекта резиновой нити", когда ваш корабль дёргается туда-сюда после варп-прыжка.
Тогда что такое десинк?
Это ситуация, когда сервер и клиент не сходятся во мнении относительно того, где должен находиться объект в определённый момент времени.
Если вы были внимательны с самого начала, то вы вспомните, что компьютеры – вещь детерминированная, так что, если мы пользуемся одинаковыми программами, с одинаковыми наборами данных на обоих концах, то такая ситуация не должна появляться вообще.
Как это получается?
Симуляция физики – это просто дифференциальное уравнение, суть которого заключается в том, чтобы получить положение объекта в определённый момент времени и вычислить его положение в следующий момент времени. Но если результат вычисления направления или ускорения будет хотя бы немного разниться на сервере и клиенте, это приведёт в итоге к большим различиям в положении объекта на этих компьютерах. Самые умные среди вас уже вероятно заметили, что в момент, когда вы заметили десинк, вы уже пропустили причину его появления.
Во первых, десинк сложно обнаружить. Много кораблей перемещаются на высоких скоростях, появляются, идут, врезаясь друг в друга, и так далее. И, совершенно неожиданно, один клиент получает сообщение, что он не может нацелиться на корабль, который по его показаниям находится на расстоянии 5 километров, так как, по мнению сервера, он находится вне диапазона прицеливания. Использование девсплоитов помогает обнаружить десинк, но, как уже объяснялось выше, мы можем обнаружить проблему только через некоторое время после того, как случилось что-то, что её вызвало.
Почему сейчас?
Вы имеете ввиду что-нибудь вроде "почему эта проблема существует так долго"? Позвольте мне вас уверить, это не из-за того, что не было ни каких попыток с ней справиться. Многими способами, на протяжении нескольких лет мы пытались воспроизвести ситуацию, которая вызывала проблему, но до недавнего времени удача нам не улыбалась. Из багрепортов мы знали, что десинк появляется главным образом там, где плотно взаимодействует туева хуча кораблей. В CCP, конечно же, знали, что такая проблема существует, даже сталкивались с ней. Однако, нам нужна была возможность воспроизвести эту ошибку именно в тот момент, когда нам это нужно, чтобы попробовать провести отладку. Попытка читать код, отслеживая пути его выполнения, поддерживая зависимости переменных, с отладчиком или без – это экстремально тяжёлая задача. Поиски моментов, когда в протекании процессов пути начинают расходиться, а отношения различаться – ещё тяжелее. Выдвигать идеи и гипотезы относительно того, что же могло произойти, кодировать тестовые ситуации и анализировать логи после их реализации – это нелепое, разочаровывающее, граничащие с дикостью и, вообще, – то ещё наказание, по моему мнению.
А что же изменилось?
Итак, некоторые из вас возможно помнят время, когда можно было эффективно скрамблить капитальный корабль, просто периодически врезаясь в него шаттлом или фрегатом. Я не хочу вдаваться в детали этой проблемы, кроме того момента, что её решение гарантировало, что два столкнувшихся объекта разойдутся в определённый период времени. У всего этого были побочные эффекты, некоторые– вполне ожидаемые, некоторые – нет. Ожидаемый побочный эффект состоял в том, что корабли "ударятся" гораздо раньше, чем войдут в соприкосновение. Неожиданный побочный эффект состоял в том, что CCP Habakuk (который в тот момент был багхантером в волонтёрской команде) нашёл возможность воспроизвести десинк. Всё, что мы должны были сделать – затолкать всеми правдами и неправдами в карго 10 или больше материнских кораблей, а затем одновременно их вытолкнуть в космос, посмотреть, как они разлетаются, а затем использовать панель разработчика, чтобы сравнить состояние клиентской и серверной сторон. Практически всегда после такого столкновения они впадали в состояние десинка.
Ага, и в чём же конкретно была проблема?
Теперь, когда мы знали, что десинк был так или иначе связан с моментом, когда поверхности кораблей так или иначе пересекались, мы начали копать. Когда мы связались с автором Destiny,– CCP LeKjart, – он сказал, что для проверки симуляции на сервере и на клиенте для проверки столкновений использовались разные наборы шаров. Однако, структура данных, которую мы использовали, гарантировала вполне определённую итеративную последовательность... или нет? Снова перечитывая документацию, на этот раз вооружившись адвокатскими очками, мы всё-таки нашли ляп. Гарантировалась корректная обработка для идентичных одноразмерных наборов, но не для их подмножеств. Например, набор чисел 5, 3, 4 и 6 обрабатывался в определённой последовательности, вне зависимости от того, в каком порядке эти числа поступали на обработку, но набор 5, 3 и 4 не обязательно обрабатывался точно так же, как и его надмножество. Те из вас, кто ещё не уснул, помнят меня, объясняющего различия между вычислениями на стороне сервера и на стороне клиента, в самом начале лекции.
Это так же объясняет то, почему нам так сложно было воспроизвести эту проблему раньше. Во-первых, порядок не имеет значения, когда в столкновение вовлечены только два объекта, а десинк может появиться только для одного объекта и в половине случаев при взаимодействии трёх шаров в одном и том же столкновении. Во-вторых, различия, до и после введения исправления ситуации с бампанием титана, лежат в области скорости объекта, отбрасываемого в сторону. Только после усложнения сцены столкновения стало возможным получить нужный нам эффект, особенно, когда в неё вовлечено много объектов, так как в определённый промежуток времени они совершат значительное количество сильных ударов друг о друга.
Небольшие модификации наборов двигающихся объектов определили нужный нам порядок, и мы снова начали тестировать. На сей раз десинк был намного меньше, но всё ещё оставался. Поиск других пятен, с использованием таких же или похожих наборов данных, где порядок мог бы иметь значение, выявил ещё два случая. После внесения соответствующих исправлений мы снова всё проверили. Тишина и спокойствие, десинк ушёл.
Это было действительно так просто?
Не глупите, конечно же нет!
Каждый раз, когда кто-нибудь из бедных программистов, вроде меня, начинает расслабляться после исправления какого-нибудь бага, встревают злые, противные, ужасные QA-подобные (Quality Assurance – контроль качества, или отдел контроля качества, тестеры - прим. переводчика) люди. Тестирование последнего фикса показало, что десинк всё ещё случается, иногда. Всё, что нужно сделать для его появления – выгрузить из карго в космос около 50 титанов, а затем войти в игру на этом месте, или варпнуться туда же после перезагрузки сервера. На Tranquility такое случается постоянно...
В тот момент, когда до запуска Apocrypha 1.5. оставалось всего несколько дней, не имело значения то, как быстро я найду ошибку и исправлю её, так как в этот патч было уже поздно что-нибудь включать дополнительно. Поэтому мы решили сделать частичное решение проблемы. Конечно – не идеально, но лучше, чем ничего.
Следующие несколько дней стали для меня бедой и проклятием. Поскольку шаги воспроизведения подразумевали неоднократную перезагрузку сервера, я должен был кодировать, компилировать и перезагружать всё, всякий раз, как только мне нужно было что-нибудь проверить. Я нашёл ещё одно пятно, где пространственное положение объекта могло быть различным на клиенте и сервере, в течение короткого промежутка времени, когда сервер имел информацию об объекте в пространстве, а клиент – нет (для примера – ситуация с заклоченным кораблём). Это была гипотетическая ситуация, но я её всё равно обнаружил. Но проблема оставалась, а мои волосы продолжали редеть.
После того, как прошло слишком много времени, я прекратил попытки быть сообразительным и стал просто остроумным. С того момента, когда у меня появилась возможность воспроизводить нужную ситуацию при участии только одного клиента, за один-два шага симуляции, я замусорил код чрезвычайно детальной регистрацией каждого вычисления и каждой переменной. После этого мне стало очень тяжело заходить в игру, а каждый шаг требовал нескольких секунд для выполнения, но это не имело значения. Огромный log-файл был сформирован даже прежде, чем на экран вывелся первый кадр изображения. Затем я сел и написал анализатор, который помещает строки из файла, полученного с сервера, напротив таких же строк клиентской части игры, переупорядочил строки согласно хронологии событий и убрал всякую не относящуюся к делу информацию серверной стороны.
Как только всё было готово, я избавился от идентичных для обоих сторон строк. Мне сразу бросилось в глаза странное несоответствие: ускорение,вычисляемое в самом начале столкновения, на клиентской стороне радикально отличалось от того, что происходило на сервере. Оценка формулы для вычисления скорости отскока показала ошибку. Помните, когда я говорил, что симуляция использует для вычисления следующего состояния объекта только его текущее состояние? Что же, здесь, фактически, было единственное место, в котором для определения вашей скорости в настоящий момент использовалась ваша скорость в предыдущий момент.
Помните, что клиент и сервер выполняют тот же самый код. Когда объект добавлен в пространство, в котором клиент уже находится, сервер и клиент используют переменные с одинаковыми значениями. Если зайти в сцену, которую сервер уже загрузил, он уже вычислил значение скорости и поместил её во временную переменную. И когда у клиентской стороны нет такого временного значения, в качестве предыдущего значения скорости ошибочно используется текущее, и если шар в первое мгновение своего существования окажется среди сталкивающихся объектов, что получится? Десинк.
Быстрый тест, после того, как я добавил такие данные к информации, отправляемой клиенту сервером, показал, что десинк теперь полностью устранён.
Хорошо, получается, что проблема устранена, так?
Верно, почти что. На этот момент мы написали патч для Apocrypha 1.5 и объявили, что полностью устранили проблему. Так и есть. Но я сделал ещё не всё.
Во-первых, я окинул тяжёлым взглядом то, как мы реализовали столкновение кораблей после исправления ошибки с бампом титанов. Конечно, я был рад тому, что это помогло выявить десинк, но меня не удовлетворило распределение сил при отскакивании кораблей от места столкновения. Слишком высокой получалась скорость для больших кораблей и слишком низкой – для малых. После нескольких регулировок я получил более приемлемый результат: большие корабли теперь реагируют на столкновение намного спокойней , чем раньше, и титаны теперь просто сметают маленькие корабли со своего пути. Они теперь теряют только небольшую часть своей скорости, тогда как лёгкие корабли теперь вылетают из области столкновения как воздушные шарики из воды. Я так же протестировал новые способы вычисления скорости возврата, без использования её предыдущих значений. Всё работало хорошо. В тот день я был просто счастливым туристом, и жизнь была замечательна. Солнце согревало мою маленькую рыжую голову, и ничто не могло испортить моего триумфа. Правильно?
Кроме злых, противных, ужасных QA, которые сокрушили моё эго, отобрав у меня возможность наслаждаться жизнью. Снова. За два дня до установки патча, не меньше. Как эти парни могут спать по ночам, я не знаю...
Они выяснили, что могут воспроизвести десинк, подлетев максимально близко к станции на капсуле, поставив там закладку, а затем прилетев на эту закладку на титане, таким образом, чтобы корпус корабля во многих местах пересёкся с корпусом станции. У меня было только несколько часов, чтобы что-нибудь сделать, иначе патч пришлось бы отложить на несколько недель. Быстро начав процесс устранения, мы обнаружили, что в этом виноваты настройки скорости и попытки устранения гипотетической ситуации с десинком, о которой писалось выше. Таким образом, мы вернули всё на место, оставив только исправление момента с использованием скорости корабля в вычислениях.
Как это всё закончилось? Что будет потом?
Потом будет Dominion. Мне жаль, мальчики и девочки, что вам придётся подождать более тонких настроек скорости при столкновениях, и я извиняюсь, что мы раньше времени сказали, что избавились от десинка в этом патче, хотя и осталась гипотетическая возможность его появления. Но, добро пожаловать на Singularity, где через несколько недель начнут появляться нововведения, относящиеся к расширению Dominion, вместе с улучшенными методами просчётов скорости при столкновениях и синхронизацией. Если вы всё же обнаружите десинк, то не забудьте описать его в багрепорте. Это, конечно, вызовет мои слёзы, но, лучше уж вы, чем ребята из QA...
Так, это всё. Класс, вы свободны.
Источник:
http://www.eveonline...?a=blog&bid=694
http://skoli.clan.su...y/2009-09-11-24
Сообщение отредактировал Heritor: 13 September 2009 - 10:57