Д
данянул <3
@danyanull8.8K подп.
22.2Kпросмотров
17 октября 2025 г.
📷 ФотоScore: 24.4K
Сегодня в Clash Royale случился МЕМ ДНЯ: какой-то человек попробовал повыпускать карты на поле за 0 эликсира и у него... получилось! Я серьезно, вот видео: https://x.com/sk__555/status/1979011746590339076. Я разобрался почему возникла такая ошибка и теперь готов рассказать и вам. tl;dr: в последней версии игры ребята начали в запросе клиента передавать стоимость карты, а сервер использовать это значение без должной валидации. Начнем с того, что бои в Clash Royale полностью эмулируются на стороне сервера. В начале боя и на сервере, и на клиентах создается одинаковый экземпляр LogicGameMode. Любое действие в бою — это та или иная команда (LogicCommand). Сначала она отправляется на сервер, который пытается ее выполнить в рамках локального LogicGameMode, после чего уже сервер рассылает её всем клиентам, которые выполнят команду в тот же самый момент времени (тик). Таким образом между всеми клиентами и сервером достигается одинаковое состояние в каждый момент времени. Можно ли выполнить так любую команду? Едва ли. Зачастую внутри каждой команды код выглядит следующим образом: / LogicDoSpellCommand.execute / int execute(LogicGameMode mode) { / ... / LogicSummoner summoner = mode.getSummoner(this.playerId); LogicSpell spell = summoner.getSpell(this.spellIndex); int cost = spell.getElixirCost(); if (summoner.getElixirPoints() < cost) { / Возвращаем ошибку и ничего не делаем больше / return -13; } / ... / summoner.useElixir(cost); summoner.castSpell(spell, this.x, this.y); return 0; } LogicSummoner здесь представляет короля (игрока), а LogicSpell — конкретную карту в его колоде. Это каноничные названия, так уж сложилось. Код выше — упрощенный пример. Каждая команда при её выполнении проверяет свою корректность, и выполняется только в том случае, если игрок действительно мог сделать соответствующее действие. Даже если каким-то образом убедить свой локальный клиент в том, что выпустить "Рыцаря за 0 эликсира" — можно, то остальные клиенты и сервер такую команду не выполнят. А затем, после сверки чексуммы, сервер еще и заставит такого игрока-"читера" загрузить настоящиее состояние. Визуальный "Рыцарь за 0 эликсира" пропадет спустя полсекунды. Чтож, это была предыстория, рассказывающая, почему читерить в боях в Clash Royale нельзя. Но у кого-то получилось, автор, ты что, нас обманываешь??7🙂 Нет-нет, что вы... вы еще не забыли пример кода, который я скинул выше? Это был реальный отрывок кода, только вот в последней версии его решили отрефакторить и переписать. Раньше LogicDoSpellCommand содержала в себе координаты (x, y), порядковый номер карты (spellIndex) и всё такое. А вот начиная с последней версии (12.169) ситуация поменялась: вместо старого-доброго spellIndex пришел новый 32-битный Int32, в который запаковали сразу несколько полей: 22 lower bits: unknown purpose 6 bits: spell index (от 0 до 31) 4 bits: elixir cost (от 0 до 15) Давайте назовем этот волшебный Int32 — SpellReference. Хотя доподлинно неизвестно, как эта структура называется на самом деле. И как вы можете догадаться — да, стоимость карты (эликсир) теперь берется из этой структуры. Которая в свою очередь формируется клиентом. Очень умно, блинкласс!👍 Из забавного — они пытаются проверять эту структуру на корректность. То есть сначала они берут, достают spellIndex, по нему получают Spell, из Spell получают SpellReference. Только вот код сверки выглядит примерно так: return ((command.reference ^ spell.reference) & 0x39FF8F) == 0; Да, это какое-то безумие. Комментарии излишни.
22.2K
просмотров
3538
символов
Да
эмодзи
Да
медиа

Другие посты @danyanull

Все посты канала →
Сегодня в Clash Royale случился МЕМ ДНЯ: какой-то человек по — @danyanull | PostSniper