Spectre, которая забрала все группы
Небольшой абуз, который позволяет серьезно подпортить нервы в игре, но раз фрог уж допустил такую проблему и не исправляет её уже несколько лет, почему бы и нет.
Нам понадобятся:
Я не могу сказать, в чем именно проблема (потому что не пускался в тесты), но факт фактом — люди предпочитают использовать готовые системы с подготовленным массивом групп, вместо того, чтобы создавать их динамически. Грубо говоря, на старте игры сразу создаются объекты-группы, которые никогда не уничтожаются и просто используются заново. Безотходное производство.
Выглядит код очень просто:
где GroupCounter является указателем на последний занятый номер группы. Запрос группы превращается в перебор массива, нахождение свободной группы и передача ссылки на неё. Память чиста, производительность тоже довольна.
Еще одна функция занимается утилизацией групп. Попользовался — положи на место через эту функцию. Если её не использовать, то группа останется «занятой» навсегда — важно всегда расставлять собственные уборщики использованного.
В результате за 120 применений Reality без подходящей цели (когда на карте нет иллюзий от Spectre) можно занять все свободные группы и заставить движок генерировать новые группы. При этом в коде возникает новая проблема.
Когда функция занимает 120-ю группу, счетчик сбрасывается на 0. Но при этом в цикле ожидается, что i строго равно счетчику минус единица. И при этом i инкрементируется, и проверки на корректность условия нет. Игра просто уходит в бесконечный цикл и здесь возможно всё, что угодно.
На моей памяти у бесконечных циклов есть несколько вариантов завершения:
В первом случае — группы нет, и все манипуляции, которые хотела провести функция с нею, обречены на провал. Это означает, что часть скилов просто перестанут работать.
Во втором случае вернется группа и всё пройдет корректно, плюс прибавится немного использованной памяти.
Ну а проблемы третьего варианта для игроков очевидны. При этом технически никакой ошибки видно не будет.
Нам понадобятся:
- Spectre — 1 штука;
- 6-й уровень или выше — 1 штука;
- Кнопка R — 1 штука;
- 30-120 секунд, в зависимости от среднего APM.
Предыстория (механика)
В WC3, как и в любой нормальной игре, есть механизм сбора мусора. Использованные однажды объекты не должны оставаться в памяти и занимать место, если они никому не нужны. Но не все объекты одинаково хорошо удаляются, и группы юнитов — один из таких объектов.Я не могу сказать, в чем именно проблема (потому что не пускался в тесты), но факт фактом — люди предпочитают использовать готовые системы с подготовленным массивом групп, вместо того, чтобы создавать их динамически. Грубо говоря, на старте игры сразу создаются объекты-группы, которые никогда не уничтожаются и просто используются заново. Безотходное производство.
Выглядит код очень просто:
function GG takes nothing returns group
local integer i=GroupCounter
loop
exitwhen i==GroupCounter-1
if GroupTaken[i]==false then
set GroupCounter=i+1
if GroupCounter==120 then
set GroupCounter=0
endif
set GroupTaken[i]=true
return Groups[i]
endif
set i=i+1
if i==120 then
set i=0
endif
endloop
call DBG(AllPlayers,5.00,"|c00ff0303CRITICAL ERROR: FOUND NO AVAILABLE GROUPS|r")
call DBG(AllPlayers,5.00,"|c00ff0303Send this replay to [email]IceFrog@gmail.com[/email]|r")
return CreateGroup()
endfunction
где GroupCounter является указателем на последний занятый номер группы. Запрос группы превращается в перебор массива, нахождение свободной группы и передача ссылки на неё. Память чиста, производительность тоже довольна.
Еще одна функция занимается утилизацией групп. Попользовался — положи на место через эту функцию. Если её не использовать, то группа останется «занятой» навсегда — важно всегда расставлять собственные уборщики использованного.
Короче
В коде ульты Spectre фрог, как обычно, вписал условие, не подумав как следует. Уничтожение взятой группы происходит только в случае, если цель для Reality была найдена, в противном случае функция просто закрывается.В результате за 120 применений Reality без подходящей цели (когда на карте нет иллюзий от Spectre) можно занять все свободные группы и заставить движок генерировать новые группы. При этом в коде возникает новая проблема.
Когда функция занимает 120-ю группу, счетчик сбрасывается на 0. Но при этом в цикле ожидается, что i строго равно счетчику минус единица. И при этом i инкрементируется, и проверки на корректность условия нет. Игра просто уходит в бесконечный цикл и здесь возможно всё, что угодно.
На моей памяти у бесконечных циклов есть несколько вариантов завершения:
- Вызов функции пристрелит сам варкрафт через некоторое время — до выдачи группы дело не дойдет;
- Варкрафт пристрелит чисто цикл и продолжит выполнение функции;
- Варкрафт молча закроется.
В первом случае — группы нет, и все манипуляции, которые хотела провести функция с нею, обречены на провал. Это означает, что часть скилов просто перестанут работать.
Во втором случае вернется группа и всё пройдет корректно, плюс прибавится немного использованной памяти.
Ну а проблемы третьего варианта для игроков очевидны. При этом технически никакой ошибки видно не будет.
22 комментария