Как стать автором
Поиск
Написать публикацию
Обновить
61.55
Swordfish Security
Информационная безопасность, DevSecOps, SSDL

Как пройти Stardew Valley за 4 минуты через code injection

Уровень сложностиСредний
Время на прочтение18 мин
Количество просмотров1.2K

Привет! С вами Влад Павловский, руководитель группы безопасности приложений в Swordfish Security. В этой статье мы посмотрим (в том числе с технической точки зрения) баги, глитчи и недоработки, которые сообщество Stardew Valley нашло и собрало в кучу, чтобы спустя 9 лет после релиза игры реализовать сбор Community Center за 4 минуты от старта игры с нового сохранения.

Эта статья является «режиссёрской версией» доклада, представленного в августе 2025 года на конференции OFFZONE. В связи с форматом и ограниченным таймингом выступления пришлось сократить некоторый контекст как по ретроспективе, так и по сути багов – в тексте эти детали будут на месте!

Коротко об игре

Stardew Valley – это одна из самых уютных и ламповых игр в жанре «симулятора фермера», у которой весьма простая завязка: нам оставляют в наследство домик в деревне, и мы начинаем всячески вести своё хозяйство. Можно копать грядки, сажать картошку, разводить скот, ловить рыбу, ходить в шахты, бить слизней, общаться с соседями, дружить, заводить отношения, жениться, получать квесты, попадать в приключения – и многое другое.

Впереди будет много картинок и пачка гифок!
Впереди будет много картинок и пачка гифок!

Игра была разработана примерно за 4,5 года Эриком Барони (в Интернете более известным как ConcernedApe). Более детально история разработки публиковалась в широко известной книге «Кровь, пот и пиксели». С момента выхода игры в феврале 2016 года её купили более 40 миллионов человек на самых разных платформах: ПК, домашние и портативные консоли, смартфоны на Android и iOS.

И даже больше!

Для игры сделали неофициальный порт в рамках проекта PortMaster, так что при желании (и наличии лицензионной копии) можно запустить полноценную рабочую версию игры на ретроприставках вроде Anbernic RG35XX или TrimUI Brick.

Или на уютной раскладушке Anbernic RG34XXSP <3
Или на уютной раскладушке Anbernic RG34XXSP <3

За почти 10 лет игра собрала очень хорошие отзывы от игроков и даже, можно сказать, обрела культовый статус. В начале июля 2025 года она даже занимала первое место в Steam по пользовательскому рейтингу за всё время. Чуть позже Portal 2 вернул себе лидерство – но на момент публикации статьи Stardew Valley остаётся топ-2!

Из этого вполне логично следует, что Stardew Valley имеет огромное и до сих пор активное сообщество. Мемы, треды на Reddit, ролики на YouTube и, что немаловажно, активное моддинг-сообщество – всё это живо и продолжает развиваться!

А про этот мем даже рассказывали на хабре
А про этот мем даже рассказывали на Хабре

А где большое сообщество – там есть и спидраннеры...

101 по спидранам Stardew Valley

Как будто есть небольшая проблема: в своём формате игра представляет собой песочницу без последней миссии, финального босса, титров и «Спасибо, что прошли нашу игру». Впрочем, это не стало фатальным препятствием – сообщество выбрало себе промежуточные цели и договорилось о четырёх основных категориях, в рамках которых можно соревноваться в прохождении на скорость:

  • Restoration – восстановить ключевые объекты игры (например, Community Center, JojaMart или лодки Вилли) путём выполнения соответствующих условий.

  • Bundles – выполнить один из наборов узелков (room bundles) в Community Center.

  • Marriage – выстроить отношения и жениться на одном из NPC (или выйти замуж за него/неё), живущих в городке Pelican Town.

  • Mines – пройти на скорость вглубь одной из шахт, населённых разными монстрами.

И такой подход понравился игрокам: количество людей, загрузивших свои ролики на доску лидеров speedrun.com, вот-вот достигнет 1000, а игру с удовольствием берут на спидран-марафоны и высоко рейтят в самых разных кругах – от локальных онлайн-ивентов и региональных историй (включая самое крупное русскоязычное сообщество Russian Underground Speedrun Community) до крупнейших фестивалей, таких как ESA или GDQ, собирающих за неделю до 3 миллионов долларов на благотворительность.

В рамках статьи я разбираю одну конкретную подкатегорию – Community Center Restoration Glitched.

Что такое Community Center Restoration Glitched

В игре есть Community Center – его можно очень условно назвать «домом культуры», где, по задумке, должны собираться и проводить время жители Pelican Town. Но в начале игры он заброшен и там ничего не происходит. Чтобы восстановить его, нужно закрыть bundles – узелки с предметами различных категорий. Тематики разнообразные: овощи, фрукты, продукция животных, рыба, металлы и т.д.

И это даже не половина!
И это даже не половина!

Все эти предметы требуют выполнения разных условий: конкретного времени года, нужной погоды, правильного времени суток, нужного места добычи (например, есть речные и морские рыбы). Поэтому эта активность изначально задумывалась как постепенное закрытие узелков предметами в рамках занятий, которыми игрок занимается на протяжении многих часов.

Исключительно задуманным способом выполнить такую цель с чистого сохранения за 4 минуты невозможно – самое быстрое прохождение в подкатегории Glitchless занимает 2 часа и 12 минут. Поэтому на помощь приходят баги. Важно: использование багов, о которых идёт речь, не требует модификации игры или внешних программ вроде ArtMoney или Cheat Engine – рассказанное далее прохождение можно воспроизвести на новом сохранении для официально купленной и только что загруженной версии игры (и даже на актуальном патче!).

Небольшое отступление о технической составляющей игры

Изначально Stardew Valley была создана на собственном движке рендера на основе фреймворка XNA. В AMA на Reddit ConcernedApe рассказывал о понятной причине выбора: на момент старта разработки был очень популярен сервис Xbox Live Arcade – отдельный раздел магазина Xbox, где выкладывались и продвигались небольшие игры, которые можно было купить относительно дёшево и загрузить на консоль из Интернета, без необходимости идти в магазин за физической копией. В частности, примерно в то время эксклюзивно на XBLA вышли такие инди-хиты, как Braid и Fez, тоже созданные с использованием XNA. Таким образом, в 2011 году выбор платформы от Microsoft выглядел очень логичным. Правда, со временем популярность сервиса упала, и новой целевой платформой стал ПК.

Но это уже совсем другая история
Но это уже совсем другая история

ConcernedApe позже рассказывал, что пожалел о выборе XNA и немного позже, в патче 1.5.5, который вышел в конце 2021 года, игра была переведена на фреймворк MonoGame. Это дало поддержку 64-битного режима работы и предоставило больше возможностей для моддеров благодаря снятию ограничения на использование только 4 Гб RAM.

Что в этой истории важно для нас:

  • Логика игры написана на C#

  • Игры, использующие вышеуказанные фреймворки, весьма доступны для декомпиляции, если код не был как-то дополнительно обфусцирован

  • Инди-разработчики с небольшим бюджетом зачастую не могут себе позволить задаваться вопросом защиты дистрибутива – важно в первую очередь завершить разработку и выпустить игру

    • Впрочем, впоследствии открытость игры к изучению стала для Stardew Valley преимуществом, снизив порог входа для создания модификаций

Как промежуточный итог, получить на руки логику игры и даже запустить её в режиме отладки также просто, как запустить dnSpy и открыть файл StardewValley.dll в директории установки игры.

Так что же там можно найти?

Item ID Glitch

Сначала посмотрим, наверное, на самый известный баг в сообществе Stardew Valley – на Reddit я постоянно натыкался на посты вида «Is item ID bug fixed in this patch?» ещё до того, как начал искать материалы для доклада.

Спойлер

Нет, не пофиксили. Подробности ниже 🙂

Итак, в игре можно получать предметы от NPC. Это может происходить по разным причинам: мы кому-то помогли и получили награду, или с кем-то подружились и заслужили подарок – что-то в таком духе. Это реализовано так: в тексте реплики может быть указан ID предмета в квадратных скобках в формате [NUMBER]. Перед отображением диалога игра парсит этот ID, убирает его из текста и добавляет предмет в инвентарь.

Было бы здорово уметь самому добавить в диалог конструкцию такого вида… А мы и умеем! NPC достаточно вежливы, чтобы время от времени здороваться и называть нас по имени, а имя нашего персонажа мы задаём прямым вводом текста перед стартом игры. Выглядит как план?

Создаём нашего персонажа с нужным именем и идём в первую катсцену – Робин должна с нами поздороваться
Создаём нашего персонажа с нужным именем и идём в первую катсцену – Робин должна с нами поздороваться
Да, вместо имени пока плейсхолдер, но мы готовы к рендеру диалога
Да, вместо имени пока плейсхолдер, но мы готовы к рендеру диалога
Удивительное рядом - парсинг квадратных скобок начинается сразу после подстановки имени!
Удивительное рядом - парсинг квадратных скобок начинается сразу после подстановки имени!
Игра вычислила, что под числом 322 находится деревянный забор и готовится создать и вручить нам его
Игра вычислила, что под числом 322 находится деревянный забор и готовится создать и вручить нам его
Имя волшебным образом испарилось, а мы начинаем игру с бонусным куском забора!
Имя волшебным образом испарилось, а мы начинаем игру с бонусным куском забора!

Предмет будет добавляться в инвентарь каждый раз, когда наше имя появляется в диалоге с NPC.

Баг обнаружили вскоре после релиза – первое упоминание на Reddit я нашёл от 1 апреля 2017 года, а тем же летом на YouTube-канале DangerouslyFunny вышел ролик Spawn Any Item Trick, рассказывающий о глитче широкой аудитории. Чуть позже, в октябре 2017 года IGN брали интервью у ConcernedApe, и он сказал следующее:

I am aware of it, but I don’t think I want to fix it. It's not game-breaking, it's kind of amusing, and the likelihood that someone would randomly stumble upon it is extremely low. So I feel like it's okay to leave it, and actually kind of fun.

Но в глубине души он злится =D

В игре есть Shrine of Illusions, в котором за небольшую плату можно поменять имя персонажа. Если включить в имя ID предмета, в качестве ничего не значащей пасхалки прилетит случайное сообщение из перечня:

  • Calling all cars! Calling all cars! We've got [a/an] {{item name}} thief on the loose!

  • ConcernedApe: What kind of a name is that? Seems fishy...

  • Mr. Qi: Easy {{item name}}, huh? I'm disappointed, kid. I thought you wanted to beat this game fair and square?

  • *Grandpa is shaking his head... 'I built this farm the old fashioned way, not with unlimited {{item name}}...'

  • ConcernedApe is mad that you're cheating...ConcernedApe: I'm not actually mad... have fun.

Выглядит это примерно так:

После чего передал IGN полный перечень предметов и их ID, актуальный на тот момент. В ходе дальнейшей поддержки игры, в том числе контентными патчами, эта таблица устарела, а база предметов стала более комплексной с вводом Qualified Item ID, но сообщество создало и продолжает поддерживать свои таблицы такого формата.

Баг до сих пор работает в актуальной версии игры, только в версии для Nintendo Switch его пришлось поправить. Объявление выглядело примерно так:

Item ID exploit no longer works: We hadn't planned on fixing this 'bug' but discovered that unintended use of the glitch can cause crashes, which is not acceptable on console. (Nintendo raised this as an issue in lotcheck and we had to re-submit with a fix for it.) I know players enjoyed this glitch, sorry it had to be taken out.

Animation Cancel

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

private void checkForEscapeKeys()
        {
                KeyboardState kbState = Game1.input.GetKeyboardState();
                if (!base.IsMainInstance)
                {
                        return;
                }
                if (kbState.IsKeyDown(Keys.LeftAlt) && kbState.IsKeyDown(Keys.Enter) && (Game1.oldKBState.IsKeyUp(Keys.LeftAlt) || Game1.oldKBState.IsKeyUp(Keys.Enter)))
                {
                        if (Game1.options.isCurrentlyFullscreen() || Game1.options.isCurrentlyWindowedBorderless())
                        {
                                Game1.options.setWindowedOption(1);
                        }
                        else
                        {
                                Game1.options.setWindowedOption(0);
                        }
                }
                if ((Game1.player.UsingTool || Game1.freezeControls) && kbState.IsKeyDown(Keys.RightShift) && kbState.IsKeyDown(Keys.R) && kbState.IsKeyDown(Keys.Delete))
                {
                        Game1.freezeControls = false;
                        Game1.player.forceCanMove();
                        Game1.player.completelyStopAnimatingOrDoingAction();
                        Game1.player.UsingTool = false;
                }
        }
Alt+Enter тоже немного ломал игру

Дело в том, что переключение игры в оконный режим и обратно в более ранних версиях принудительно открывало меню паузы – причём даже в катсценах. Дополнительной фишкой был тот факт, что предмет, на который кликнули в меню паузы перед новым нажатием Alt+Enter, приводил к принудительному выкидыванию предмета, даже если это был фермерский инструмент, выбрасывание которого не предполагалось. Так можно было супербыстро освободить инвентарь перед началом диалога, во время которого добавлялись уже нужные предметы.

И ещё диалоги можно было быстрее скроллить с переключением оконного режима – такой трюк назвали «Window Shift Cancel».

На актуальном патче эти штуки уже не работают =(

В итоге, по какой-то причине эта функция осталась в релизной версии игры. Спустя некоторое время один из участников Discord-сервера по спидранам Stardew Valley нашёл её во время изучения декомпилированного билда версии 1.2, и игроки начали использовать её в своих целях.

Видите, как отменяется замах топора? В рамках длинных забегов это экономит время, но в конце покажу более серьёзное применение этой штуки
Видите, как отменяется замах топора? В рамках длинных забегов это экономит время, но в конце покажу более серьёзное применение этой штуки

Mailbox Glitch

Ох, здесь начинается серьёзная аналитика!

Рядом с домом у игрока есть почтовый ящик, в который время от времени попадает какое-нибудь славное письмо. Посмотрим пример:

Как быстро ты растёшь, ага... прислал подарочек... ТАК, А ЧТО ЭТО ЗА ТАКОЕ В КОНЦЕ?
Как быстро ты растёшь, ага... прислал подарочек... ТАК, А ЧТО ЭТО ЗА ТАКОЕ В КОНЦЕ?

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

Папа в своё время немного увлекался скриптингом
Папа в своё время немного увлекался скриптингом
И к чему это привело? У дочери совсем ничего не осталось в инвентаре!
И к чему это привело? У дочери совсем ничего не осталось в инвентаре!

Возможно, вы уже заметили на скриншоте из отладки методы HandleActionCommand и HandleItemCommand. Посмотрим немного внутрь:

Действительно, в игре парсятся строки, которые начинаются на %action или %item, чтобы потом запустить какое-то действие
Действительно, в игре парсятся строки, которые начинаются на %action или %item, чтобы потом запустить какое-то действие

Насколько велика мощь почтового ящика

Начнём с перечня команд, которые в принципе можно прокинуть в почтовый ящик:

Команда

Описание

%action <action> %%

Запускает триггер действия, например: %action AddMoney 500%%, чтобы добавить игроку 500 золотых

%item id [<item id> [count]]+ %%

Добавляет предмет с определённым ID в инвентарь игрока

%item money <amount> %%

Добавляет указанное количество золота в инвентарь игрока

%item money <min> <max> %%

Добавляет случайное количество золота между <min> и <max> в инвентарь игрока

%item cookingRecipe <key> %%

Даёт игроку рецепт блюда с определённым ID

%item craftingRecipe <key> %%

Даёт игроку схему крафта определённого предмета

%item quest <quest ID> %%

Игрок получает предложение выполнить квест с определённым ID

Здесь и далее списки будут неполными, более детальную информацию можно найти в фанатской вики: страница раз и страница два.

Окей, часть с %item выглядит понятно: можно добавить денег и предметов, выучить рецепт блюда, схему крафта предмета или получить квест. А что насчёт %action?

Action

Описание

AddBuff <buff ID> [ms duration]
RemoveBuff <buff ID>

Добавляет или удаляет баф на персонажа игрока

AddItem <item ID> [cnt] [quality]
RemoveItem <item ID> [count]

Добавляет или удаляет предмет с указанным ID и качеством в инвентарь игрока

AddMoney <amount>

Добавляет золото в инвентарь игрока

If <query> ## <action if true>
If <query ## <action if true> ## <action if false>

Условный оператор на базе одного из PLAYER_STAT game state query

IncrementStat <stat key> [amount]

Увеличивает значение одного из параметров PLAYER_STAT

AddQuest <quest ID>
RemoveQuest <quest ID>

Игроку добавляется/убирается квест с указанным ID

AddMail <player> <mail ID> [type]
RemoveMail <player> <mail ID> [type]

Добавляет или удаляет письмо игрока

Тут много всего важного и интересного для нас. Подсвечу самое важное:

  • AddItem здесь хитрее, чем прошлые способы получения предметов – он позволяет получить предмет определённого качества. Дело в том, что в Community Center есть узелок, который потребует такие предметы – например, 5 золотых тыковок. Раньше приходилось спавнить себе 20 семян тыкв, 20 лучших удобрений и несколько спринклеров, бежать в теплицу, всё это дело сажать, удобрять, спать несколько ночей и бежать собирать урожай в надежде, что хотя бы 5 тыкв будут золотыми. Теперь всё надёжно!

  • Условный оператор в сочетании с IncrementStat позволяет нам реализовать некоторую логику, в которой, например, каждое прочтение письма будет увеличивать какое-то внутриигровое значение, а потом мы можем через If смотреть текущее значение параметра и делать то, что нам нужно в текущий момент времени

  • AddMail позволяет нам добавлять письма в почтовый ящик. Причём четырьмя разными вариантами:

    • now – письмо сразу попадает в почтовый ящик

    • tomorrow – письмо будет в почтовом ящике в следующий игровой день

    • received – письмо попадёт в список прочитанных

    • all – объединяет все перечисленные варианты

Нам, в первую очередь, интересны две опции добавления писем. now позволяет создать бесконечный цикл: новое письмо приходит в тот же момент, когда мы читаем предыдущее. А received работает за счёт того, что внутри письма могут находиться флаги, влияющие на глобальный прогресс нашего сохранения:

Флаг

Описание

HasTownKey

Игрок получил ключ от города и может заходить во все двери

canReadJunimoText

Игрок умеет читать язык Джунимо и может заполнять бандлы в Community Center

ccIsComplete

Community Center выполнен (???)

jojaMember

Игрок купил членство в JojaMart

Beat_PK

Игрок прошёл аркадную игру Journey of the Prairie King в баре

HasUnlockedSkullDoor

Игрок открыл доступ в Skull Cavern

Community Center выполнен? Мы же здесь ровно за этим!

Флаг действительно существует и записывает в профиль сохранения, что Community Center выполнен, но бандлы-то остались! А по правилам доски лидеров забег заканчивается в момент, когда последний узелок завершён и проходит финальная анимация

В частности, нам нужны два верхних флага:

  • HasTownKey даёт нам ключ от всех дверей в Pelican Town, разрешая заходить в любое здание в любое время дня и ночи

  • canReadJunimoText разрешает нам приступить к заполнению узелков без необходимости ходить в замок волшебника и пить какое-то сомнительное зелье

Кажется, этого нам достаточно. Попробуем собрать план!

Логика полома почтового ящика

Вы же не забыли про условный оператор?
Вы же не забыли про условный оператор?

Что в этой схеме мы ещё не проговорили (или я потерял 😂):

  • "Волшебная палочка" – это Return Scepter, который позволяет нам одним кликом возвращаться домой. Для нас важно уметь быстро возвращаться к почтовому ящику.

  • Удаление всех начальных предметов – тоже часть плана: предметы для фермерства для завершения цели нам не нужны.

  • В качестве x подойдёт любое внутриигровое значение, которое мы не будем трогать другими способами. Например, в наши планы не входит сон, поэтому DAYS_PLAYED отлично подойдёт!

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

  • В первый раз не заполняем инвентарь, потому что изначально он слишком маленький – придётся честно (почти, деньги-то нарисуем) добежать до магазина Пьера и купить себе максимальный рюкзак, который вместит в себя 36 предметов.

Звучит круто! Секунду, у нас же поле для имени персонажа маленькое и в лучшем случае вместит только одну команду. Какая ещё блок-схема? Что же, у нас есть ещё трюк в рукаве...

Обход ограничения на имя персонажа

Во-первых, игроки выяснили, что ограничение строки имени зависит от того, сколько символов поместится в поле ввода. Кроме того, в разных языках применяются шрифты разного размера. Переключаемся на китайский!

На английском языке помещается всего 12 цифр, на китайском – уже 17
На английском языке помещается всего 12 цифр, на китайском – уже 17

Пять лишних символов, конечно, нам погоды не делают. Но кто сказал, что нам нужно вводить имя в одну строку?

С игрой всё в порядке, она честно прочитает это имя и будет его использовать
С игрой всё в порядке, она честно прочитает это имя и будет его использовать

Как оказалось, если заранее написать текст с переносом строк в условный блокнот, скопировать его и вставить в игру – все переносы строк будут считаны, а ограничений на количество строк никто не вводил!

Чудесно, теперь мы можем реализовать нашу схему в виде скрипта, отправить его в игру и начать прохождение.

ВАЖНО! Приложенный под спойлером скрипт не был написан мной: он взят из описания забега мирового рекордсмена TheSamanthaTM. Это крутой спидраннер, который более 3 лет делает забеги по Stardew Valley во всех возможных категориях. Ознакомиться с оригиналом можно здесь.

Осторожно, здесь 395 строк!
[279][253]
%action AddMail${
^}$ All dad1${
^}$ Now%%
%action AddMail${
^}$ All HasTownKe${
^}$y Received%%
%action AddMail${
^}$ All ccDoorUnlo${
^}$ck Received%%
%action AddMail${
^}$ All canReadJu${
^}$nimoText${
^}$ Received%%
%action If DAYS_P${
^}$LAYED 1 1 ## A${
^}$ddMoney ${
^}$6000%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddMoney ${
^}$99998500%%
%action RemoveI${
^}$tem (T)Axe%%
%action RemoveI${
^}$tem (T)Hoe%%
%action RemoveI${
^}$tem (T)Pick${
^}$axe%%
%action RemoveI${
^}$tem (T)Water${
^}$ingCan%%
%action RemoveI${
^}$tem (W)47%%
%action If DAYS_P${
^}$LAYED 1 1 ## A${
^}$ddItem (T)Retu${
^}$rnScepter%%
%action Incremen${
^}$tStat book_${
^}$speed 1%%
%action Incremen${
^}$tStat book_${
^}$speed2 1%%
%action AddBuff ${
^}$22 -2%%
%action AddBuff ${
^}$statue_of_ble${
^}$ssings_0%%


%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 16%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 18%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 20%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 22%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 396%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 398%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 402%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 404%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 406%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 408%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 410%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 412%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 414%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 416%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 418%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 420%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 422%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 78%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 88%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 90%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem${
^}$ 709 10 4%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem${
^}$ 388 198 4%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem${
^}$ 390 99 4%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem ${
^}$24 6 4%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 188%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 190%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 192%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 256%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 260%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 258%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem ${
^}$254 6 4%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem ${
^}$270 6 4%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 272%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 276%%
%action If DAYS_P${
^}$LAYED 2 2 ## A${
^}$ddItem 280%%


%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 186%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 174%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 182%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 438%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 440%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 613%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 634%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 635%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 636%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 637%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 638%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 145%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 143%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 699%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 706%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 142%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 136%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 698%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 700%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 130%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 131%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 150%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 701%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 132%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 140%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 148%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 715%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 716%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 717%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 718%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 719%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 128%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 156%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 164%%
%action If DAYS_P${
^}$LAYED 3 3 ## A${
^}$ddItem 734%%


%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 334%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 335%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 336%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 80%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 82%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 84%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 86%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 768%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 769%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 724%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 259%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 430%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 376%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 228%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 194%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 420%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 397%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 421%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 444%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 62%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 266%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 422%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 392%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 702%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 536%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem ${
^}$262 10 4%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem ${
^}$178 10 4%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem ${
^}$613 3 4%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 725%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 348%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 446%%
%action If DAYS_P${
^}$LAYED 4 4 ## A${
^}$ddItem 637%%


[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
[GoldCoin]
%action Incremen${
^}$tStat DaysPlay${
^}$ed%%

Запускаем таймер!

Своё прохождение я записал и выложил на Youtube в июле – по ссылке можно посмотреть его целиком и в 60 кадрах/сек. Здесь попробую гифками показать самые яркие моменты!

Берём разгон!
Берём разгон!

Здесь мы уже получили и употребили два предмета для ускорения бега, но чтение письма – а здесь мы действительно открываем почтовый ящик и сразу его закрываем – докидывает дополнительные бафы, увеличивает количество денег до нужной суммы на покупку рюкзака и (тут не видно) опустошает инвентарь, оставляя только Return Scepter.

Дальше – больше: мы получаем наши первые предметы и бежим в Community Center заполнять бандлы. После закрытия первого набора персонаж видит собравшихся вокруг духов Junimo, от удивления подпрыгивает на месте и слушает их речь. Всё это – непропускаемая катсцена. Непропускаемая же, верно?

Неверно!
Неверно!

Разложу по шагам, что произошло:

  1. Мы заполняем большую часть набора и закрываем окно.

  2. Запускается катсцена появления новых наборов бандлов. Камера делает пролёт по локации, персонаж должен стоять на месте.

  3. Мы прожимаем Animation Cancel и возвращаем контроль над персонажем.

  4. Снова открываем первый набор бандлов и дозаполняем его.

  5. На выходе триггерится катсцена, где духи Junimo приветствуют нас и хотят забрать первый набор.

  6. Мы снова прожимаем Animation Cancel и несёмся ко второму набору бандлов опустошать инвентарь.

А когда предметы в инвентаре заканчиваются, мы летим домой, нажав Return Scepter:

Вжух, и у нас снова полный инвентарь!
Вжух, и у нас снова полный инвентарь!

Мне особенно нравится проявление Animation Cancel в конце забега, где после предпоследнего набора бандлов мы бежим к финальному вслепую – сквозь белую заставку от предыдущего набора:

Наверное, можно было заучить этот момент чуть лучше и зажать кнопку "вверх" раньше
Наверное, можно было заучить этот момент чуть лучше и зажать кнопку "вверх" раньше

Результат – Community Center закрыт за 3 минуты 47 секунд! Отставание в 9 секунд от мирового рекорда обусловлено лишь более медленным механическим заполнением бандлов =)

Заключение и какие-то выводы

Являются ли баги, рассмотренные в рамках статьи, сложными к исправлению? В сущности, нет. В Item ID Glitch и Mail Injection было бы достаточно вставлять имя персонажа уже после парсинга текста на внутриигровую логику. Animation Cancel, по сути, можно было просто убрать (или, если эта функция почему-то задействована в другой логике, убрать её триггер на сочетание клавиш).

Можно ли сказать, что разработчик забросил игру и из-за этого баги не были поправлены? Тоже нет – последний патч 1.6.15 выходил в декабре 2024 года, значительно позже того, как баги были найдены. И нет никаких гарантий, что ConcernedApe не пойдёт делать патч 1.7, чтобы снова "отдохнуть от разработки своей второй игры". 🙂

Breaking news!

Незадолго до публикации статьи ConcernedApe сообщил о работе над патчем 1.7. В день его выхода получится, что игра поддерживается 10 лет!

Как мы уже знаем, в данном случае решение не исправлять баги было осознанным, потому что эти глитчи не ломают общее впечатление от игры, а для core-аудитории даже наоборот, добавляют элемент фана. Что важно, их использование никак не вредит другим игрокам!

А для enterprise-разработки мы с коллегами в Swordfish Security стараемся помочь сделать так, чтобы ошибки подобного рода не доходили до релиза и и устранялись на более ранних этапах. Подписывайтесь на наш блог на Хабре, чтобы в будущем прочитать больше удивительных историй и полезных статей в сфере безопасной разработки!

До новых встреч!

Теги:
Хабы:
+5
Комментарии4

Публикации

Информация

Сайт
swordfish-security.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия