Ultima Online празднует свое 25-летие! Вы знаете, что это значит, верно? Время истории!
В духе #gamedev и вдохновившись классическим постмортемным видео UO от Рафа Костера и др., я подумал, что было бы интересно поделиться несколькими историями из средних лет Ultima Online между моим пребыванием в EA Redwood Shores и Mythic Fairfax.
Сегодняшняя история: технический разбор сожженных домов мошенников после обнаружения «преступного» кольца незаконных предметов.
Дублирование: эксплойт нулевого дня UO
Когда Ultima Online только запускалась, мы, игроки, обнаружили некоторые ошибки.
Под некоторыми, я имею в виду, много.
И самой ценной, дьявольской, стоящей создания секретного веб-форума, чтобы торговать ими, была: обман.
Возможность дублировать предмет, или, лучше сказать, сумку, полную предметов, была самым выгодным эксплойтом, возможным в игре. Лучше, чем взлом скорости, лучше, чем прямая передача урона — даже лучше, чем вторжения в дома до блокировки.
У некоторых бета-тестеров был секрет. Конечно, не все из нас, но были определенные захудалые элементы толпы бета-тестирования, которые вместо того, чтобы следовать добродетели честности, предпочитали копить свои знания об эксплойтах, а не сообщать о них команде разработчиков.
Как только бета-версия UO закончилась и игра была запущена, внезапно на экономику обрушилась волна обмана предметов.
Что это был за «тщательно охраняемый» эксплойт?
Злоупотребление границами areaserv.
Резинка для развлечения и прибыли
Я до сих пор помню, как впервые увидел это своими глазами.
Это был 1997 год, и я бегал к северу от лагеря орков недалеко от города Коув, когда увидел, как некоторые рандо делали самую странную вещь на свете: они бегали взад и вперед по этому раздражающе медленному участку игровой карты, роняя маленькие сундуки на землю.
Так вот, я знал, что эта конкретная область как... Самое страшное.
Видите ли, на той грунтовой дороге между горами и крепостью орков была действительно раздражающая часть карты, которая была довольно неотзывчивой и дергающейся, когда вы шли по ней.
На самом деле, если вы наткнетесь на него, вы можете получить «резиновую ленту» - обнаружите, что теряете несколько секунд игрового времени, когда вы возвращаетесь на предыдущую позицию до того, как сервер «догонит».
(Напомните мне, чтобы я когда-нибудь рассказал вам все об интерполяции сервера / клиента UO и тикрейте!)
Это было особенно плохо, если монстр, такой как неудобно расположенный капитан орков прямо к югу от вас, преследовал вас и забил вас до смерти во время вашей жалкой запаздывающей попытки бежать.
Каково же было мое удивление, когда эти два игрока начали взволнованно кричать друг другу в тексте (который пролетел над их головами), что «это сработало! ОМГЗ!"
Да, им удалось, как они взволнованно хвастались, придумать трюк, чтобы бросить сундук на одну сторону «запаздывающего пятна», пытаясь забрать / передать его другому игроку, когда они оба переходили с одной стороны на другую, и теперь у каждого из них была копия одного и того же сундука: и его содержание.
Пуперы!
Бесшовные границы серверов сложны
То, что я видел, было эксплойтом, основанным на границах «areaserv», которые разделяли игровой мир Ultima Online.
UO никогда ничего не делал легким путем; В конце концов, его первое поколение дизайнеров пробовало вещи — изобретало совершенно новые вещи — на что более поздние онлайн-игры просто качали головой и говорили: «Нет, это слишком сложно».
Например, как сбалансировать нагрузку тысяч игроков на огромной игровой карте площадью 29 360 128 метров² в 1997 году?
Вместо того, чтобы иметь разные «зоны» с загрузочными экранами или «длинными туманными горными перевалами», разработчики UO просто изобрели прямоугольные, бесшовные «подкарты» с (справедливо!) невидимым методом, который просто позволял вам ходить с одной стороны на другую, в то время как вещи на другой стороне все еще были видны и обновлялись в режиме реального времени.
Таким образом, каждый игровой сервер (или «осколок»), на котором вы играли, был фактически разделен на «areaservs», и существовал невероятно хорошо написанный «зеркальный» код, который обрабатывал передачу состояния игры, условий объекта и событий с одной стороны границы на другую.
Но пересечение этих границ в качестве игрока иногда заметно отставало.
В сторону: Если вы посмотрите на карту areaserv выше, вы можете заметить, что Britannia должна была быть разделена немного более тщательно, чтобы границы areaserv не пересекали важные/населенные области игрового процесса... посмотрите на бедное логово пирата: есть причина, по которой эти скрытые подземные туннели никогда не были интересными для PvP.
По сути, areaservs копировали вашего персонажа игрока и отправляли между собой связанное сообщение, содержащее всю вашу информацию. Как только игрок пересекал границу, он уничтожал старую копию игрока на начальной стороне.
И это были не только игроки: автономные мобильные объекты (мобы), такие как монстры, животные и NPC, также могли пересекать их.
(Примечание: для будущих подвигов у этих монстров и животных также были свои собственные рюкзаки / инвентарь, на которые игроки могли использовать попытки эксплойта; много драмы ламы.)
Естественно, любая ошибка в коде areaserv представляла собой наибольший вероятный источник эксплойтов «обмана», особенно в сочетании с целенаправленными манипуляциями с игровым состоянием игрока перед последовательностью резервного копирования/выключения сервера каждое утро.
Мы чувствовали себя так, как будто играли в «ударь крота»
И после того, как я присоединился к команде UO, я узнал несколько вещей:
- Код areaserv великолепен. Он был исправлен и переписан много раз, но к моим более поздним годам он стал довольно «умным» в отношении того, как он предвидел состояние игрока и предварительно сериализованные объекты еще до того, как произошли переносы границ.
- Тем не менее, независимо от того, насколько хорош был код, мы, разработчики, привнесли в игру столько сложностей, что источники обманов были в изобилии, и чаще всего это были не просто проблемы с областями. Не помогало и то, что геймдизайнеры могли писать код производственного уровня на Wombat (наш собственный язык сценариев, основанный на событиях), в котором было множество оболочек для кода управления объектами C++.
Действительно казалось, что мы постоянно отступаем; Мы не могли быть очень активными в отношении обманщиков и полагались на отчеты игроков и службу поддержки клиентов для выявления вопиющих эксплойтов.
И вот однажды мне пришла в голову странная идея.
У UO не было базы данных
Есть несколько вещей, которые вы должны знать об UO, каким он был в середине 2000-х годов.
- Каждый сегмент выполнял последовательность выключения/резервного копирования в определенное время в ранние утренние часы. (Отлично подходит для того, чтобы не исправлять утечки памяти!)
- Полное игровое состояние каждого areaserv было выгружено из памяти в большой двоичный файл резервной копии — к тому времени, когда я присоединился к команде, они приближались к 4 ГБ в размере.
- После того, как areaservs завершили резервное копирование, они выключились, перезапустились и перешли в режим ожидания, в то время как «gameserv» делал то же самое. Вы можете думать о gameserv как о сервере, предназначенном для координации всех areaservs и передачи вошедших в систему игроков нужному areaserv.
- Gameserv завершал работу, перезапускался, а затем давал указание каждой areaserv загрузить свою последнюю заведомо исправную резервную копию.
- Каждый areaserv загружает двоичную резервную копию и воссоздает ранее сохраненное состояние игры; он также будет выполнять любые триггеры/хуки в коде сценария для объектов/мобильных устройств, которые говорят «Делать X при загрузке сервера».
- [Куча других вещей здесь, в том числе появление новых мобов или ежедневных раритетов]
- Areaservs сообщали, что все в порядке, и информировали gameserv, а gameserv повторно объявлял о себе серверам входа в систему, что он доступен для игры.
#2 был самой большой проблемой с попыткой найти обману: игровое состояние UO не было в «базе данных» — не было возможности запрашивать имущество или объекты игрока, чтобы найти незаконные товары. Это было 4 ГБ двоичного двоичного объекта; Данные имели смысл только при загрузке обратно в саму игру.
Однако я попробовал: я *хотел* прочитать двоичные блобы. Я *хотел* предоставить Службе поддержки клиентов инструменты для поиска обманутых или украденных предметов. Это было просто невозможно с инструментами того времени.
Затем я вспомнил #5, и это натолкнуло меня на мысль о другой линии атаки.
Глобальный реестр хэшей
Каждый динамический объект в UO — будь то мобильный телефон, плеер или предмет — способен хранить как данные, так и сценарии на себе.
Скрипты не сохраняют состояние после завершения выполнения кода, поэтому мы будем хранить необходимые данные в «objvars» и прикреплять их к тем же объектам, к которым были прикреплены скрипты.
Сами скрипты содержали тонны «триггеров», которые представляли собой обработчики событий, привязанные к условиям игры. Как я уже упоминал в #5 выше, у одного из них было имя, которое выглядело примерно так: «beforeServerLoad» (черт возьми, если я помню настоящее имя), которое специально выполнялось на этапе загрузки резервной копии процесса запуска areaserv.
Я подумал: «Что, если бы мы просто *отмечали* самые ценные предметы в игре всякий раз, когда они загружались из резервной копии?»
Я пошел к ведущему инженеру, Supreem, и попросил только одно дополнение к коду C++ и связанное с ним сопоставление с «функцией вомбата»: хеш-функцию (привет, крипто!).
Я хотел сделать так, чтобы gameserv хранил непрерывный список «отмеченных» элементов во время процесса загрузки areaserv для идентификации дубликатов.
Глобальный реестр хэшей.
План разворачивается: невидимый краситель
Вот как это работало:
- Каждый объект в игре, когда он загружался, имел хук для общего сценария «предварительной загрузки», который присоединялся, выполнялся и отсоединялся, прежде чем он позволял объекту выполнять любые другие сценарии. Это было действительно полезно для устранения ошибок развертывания, если мы делали что-то не так, и это ломало какие-либо объекты в игре во время патча.
- Я добавил триггер в скрипт предварительной загрузки с событием «beforeServerLoad», которое проверяло тип элемента: если он был в заранее определенном списке самых дорогих/редких типов элементов, он продолжал с планом.
- Если на элементе не было "тега", то он создавался сейчас: objvar типа "string", который содержал хеш текущего времени, areaserv и gameserv id (и некоторые другие уникальные данные). Это был очень длинный, случайно выглядящий фрагмент текста (больше, чем 32-битное целое число), например: 29bb546a415ff874e5129549fe8064249e8f1b2996fa2e7d52879d2ec24e06fd.
- Как только у него был тег (или если он уже был), он отправлял сообщение в gameserv (помните, что на шард был только один из них) и просил gameserv поместить его в реестр поиска. Если *тег* уже существовал в реестре, то элемент был постоянно помечен как «Я ОБМАНУТ» (опять же, с objvar) открытым текстом — потому что представители службы поддержки клиентов в божественном клиенте могли его видеть.
- Реестр поиска сохранялся только до тех пор, пока все areaservs не загрузились, а затем он очищался для экономии памяти.
Идея заключалась в том, что если ценный предмет (достойный обмана) имеет уникальный хэш, хранящийся на нем в виде objvar, то процесс дублирования — каким бы он ни был — будет копировать значение того же objvar. Тогда будут два уникальных объекта, каждый из которых содержит один и тот же хэш.
Лучшее время для проверки этого (массово) было во время загрузки сервера, и эта система помечала все объекты objvar «I AM DUPED».
(Примечание: моя память здесь может быть нечеткой, и нам, возможно, также пришлось привязать сценарий к событию onPlayerLoad; Я просто не помню, все ли игроки были загружены с помощью areaserv или они были извлечены из резервных данных по запросу).
Это было очень похоже на штамповку редких предметов какими-то невидимыми чернилами, которые флуоресцировали только при определенных условиях.
Поэтому мы выпустили обновленный код и позволили ему работать глобально в течение нескольких недель.
А потом началась охота.
Мваха. Мвахаха. МВАХАХАХАХАХА!
Корпоративная реакция
В течение нескольких недель мы поняли, что первый этап сработал: мы нашли тонну обманутых редких предметов, и они в основном принадлежали одному и тому же набору учетных записей игроков на нескольких игровых серверах.
Несмотря на наше волнение, здесь мы сделали паузу: что нам теперь делать?
Мы сели, чтобы поговорить с руководством, службой поддержки клиентов и нашим комьюнити-менеджером.
Управление
Наш продюсер был в восторге от того, чтобы объяснить генеральному менеджеру студии, чего мы достигли. Я помню, как сидел на собрании с улыбкой на лице, довольный как удар (каким может быть только высокомерный молодой геймдев), просто услышав то, чего я не ожидал:
«Ммм, я не думаю, что удаление их всех [обманутых предметов] — хорошая идея, вы навредите слишком многим игрокам».
На самом деле, я не думал об этом.
Совсем.
Я был слишком взволнован тем, что достиг своей давней цели, чтобы «поймать каких-то дураков».
Я хотел разозлиться и что-то сказать, может быть, утверждать, что он просто не понял, но сдержал себя и после встречи вернулся к команде и сказал им, что нам запрещено автоматически удалять все обманы.
Обслуживание клиентов
Поэтому мы обратились к команде CS за данными.
Это было правдой. Это была бы ужасная идея.
Обманы распространились так быстро, как только они были созданы, что если бы мы просто удалили их все из-под всех игроков, которые купили их (с их кровно заработанным золотом) у пуперов, мы бы затронули значительную часть базы игроков.
Конечно, некоторых из них устроила бы «мораль» наших действий, но в масштабе сотен или тысяч пострадавших игроков (на один осколок) мы просто просили, чтобы разочарование вызвало волну тихого ухода.
Я действительно не должен был удивляться этому: генеральный менеджер нашей студии занимается MMO намного дольше, чем когда-либо.
Чтобы усложнить ситуацию, команда обслуживания клиентов начала задавать нам трудные вопросы, например: «Сколько именно обманутых рунических молотов доблести должно быть у человека, прежде чем мы их запретим?»
Подождите, мы должны были присвоить произвольное значение того, сколько обманов, удерживаемых одним человеком, было подозрительным? Ой. О, малыш.
Это было сложнее, чем код. Это был дизайн сообщества, а не просто игровой дизайн.
Комьюнити-менеджер
К счастью, у нас был уравновешенный комьюнити-менеджер, который привык к нашему чрезмерному изобилию. Честно говоря, они просто привыкли управлять мной, когда я был слишком увлечен чем-то.
«CS в любом случае собирается запретить их [фактических дуперов]. Почему бы не сделать это событием?» — о том, как прошел этот разговор.
Ну, я был ведущим дизайнером живых мероприятий, так что... Мы замышляли.
Пожар! Хе-хе, огонь!
Мы идентифицировали самих обманщиков и их склады: у них были дома, полные их обманутых предметов, и продавцы NPC, продававшие их игрокам.
«Кольцо дублирования» растянулось на несколько серверов, состоящих из отдельных групп, не обязательно работающих вместе. Тем не менее, все они развивали одно и то же поведение: зарабатывали тонны золота UO на продаже обманушек, а затем продавали золото UO на вторичных рынках за наличные деньги.
Итак, мы с Адидой написали сценарий, который, будучи прикрепленным к дому, будет:
- Удалите дом и все его содержимое. Всё это. Тотчас. Рекурсивно. *Пуф*
- Порождайте кучу неподвижных «обломков жилья» в заранее определенной прямоугольной области, которая соответствовала тем же размерам, в которых существовал дом. Он был окрашен в темно-черный цвет, чтобы выглядеть так, как будто на нем была сажа.
- Создайте кучу вечных «огненных полей» среди обломков.
- Создайте соломенный манекен с надписью «Чучело предателя», чтобы поместить его посреди горящих обломков.
Затем мы выбрали день и нанесли удар.
Массовая служба поддержки клиентов забанила обманщиков по таймеру, прямо перед тем, как связанный с ними сервер был подключен к сети.
Затем мы приостановили подключение gameserv/loginserv к тем же серверам, и мы с Адидой бегали (на самом деле телепортировались) к каждому жилому месту, прикрепляли сценарий, смотрели, как от радости вспыхивают пожары, и двигались дальше.
Мы делали это партиями связанных серверов, чтобы каждое «кольцо обмана» не успело заметить, что их забанили, и попытаться войти в альт-аккаунты, чтобы очистить свои дома, прежде чем мы сможем добраться до них
Реакция сообщества
Смущение! Хаос! Радость! Смех!
Ветки обсуждений появились на Stratics и MMORPG.com почти мгновенно, полные вопросов и догадок — и нескольких ложных предположений, таких как то, что сотрудник CS попал в бан (ложь!).
Что ж, это аккуратно, но больше так не делай
Десятки домов были разрушены по всей мультивселенной Ultima Online, и пламя, лизающее закопченные обломки, было видимым свидетельством решимости нашей команды бороться с читерами.
Это было фантастически!
И нам сказали больше так не делать.
Лол.
Мы подверглись критике за то, что сделали что-то настолько дерзкое публичное, чтобы обмануть игроков, хотя мы позаботились о том, чтобы не идентифицировать их напрямую с другими игроками, но все же едва ли прошли мимо высшего руководства.
Служба поддержки клиентов была проинструктирована использовать свое усмотрение для работы с элементами «Я ОБМАНУТ» с тех пор, и, честно говоря, к настоящему времени я не знаю, помнит ли кто-нибудь, что система существует (если она вообще существует).
Некоторые извлеченные уроки (сюрприз Сатоши?)
Читатели, которые сталкивались с NFT, могут заметить жуткое сходство того, как мы помечаем эти редкие предметы (которые не были взаимозаменяемыми!) уникальными хешами, с тем, как сегодня работают невзаимозаменяемые токены (NFT) на основе блокчейна.
По иронии судьбы, одна вещь, с которой наш метод не работал, — это «стопки» объектов (взаимозаменяемые предметы, такие как золотые монеты).
Но Сатоши Накамото решил и эту проблему! Первая статья о Биткойне описывает идеальный метод (онлайн или офлайн) для проверки событий с отметками времени, таких как создание валюты. Это помогло бы предотвратить обман и гарантировало, что внутренние злоумышленники (обладающие способностями к созданию предметов) не смогут просто сгенерировать кучу золота для продажи вне игры.
Не то чтобы я говорил, что MMO нужны блокчейны; но оказывается, что технология реестра Proof-of-Work была использована здесь! (Неудивительно, что Amazon теперь предлагает аналогичную услугу: QLDB).
Наконец, одна из самых важных вещей, которые мы узнали, - это необходимость иметь дело с экономикой после пожара: особенно когда игроки хотели побороться за теперь доступные, очень премиальные места в жилье.
Но это уже другая история.
Об авторе
Тим «Draconi» Коттен был ведущим геймдизайнером Ultima Online: Stygian Abyss, и когда он не пишет истории из диких дней онлайн-игр, он работает над применением всех этих с трудом завоеванных уроков к дизайну Метавселенной.