Инструкция по адресу 0x6F430C91 попыталась прочесть память по адресу 0x00000020, но так как там ничего не было — она крашнулась.
Этот код расположен в пределах game.dll (0x6F000000 - 0x6FBB5000 F:\Warcraft III 1.26S\Game.dll), что видно в разделе Loaded Modules. Учтите, что пусть эта библиотека и предпочитает распологаться по адресу 0x6F000000, но операционная система может загрузить её куда либо еще и тогда, с теми же смещениями, абсолютные адреса будут отличаться. К примеру, если библиотека будет по адресу 0x24F00000, то вместо 0x6F430C91 вы увидите 0x25330C91 (0x24F00000 + 0x430C91).
По смещению 0x430C91 находится код функции CGameState::GetAgentHandle, которая принимает указатель на агент и возвращает его хэндл.
В дампе памяти стека видны значения трёх сохраненных регистров (0x00000010 0x0019DFE8 0xFFFFFFFF), а также адрес возврата 0x6F3BBB58 и полученные аргументы: agent=0x00000010, auto_free=0x00000000. Кто-то передал кривой указатель 0x10, который ссылается на нечитаемую память.
Адрес возврата ведет к нативке Player. В стеке можно увидеть сохраненное ею значение регистра (0x6F9473BE), адрес возврата к коду виртуальной машины (0x6F45D1B8) и полученный ею аргумент (number=0xFFFFFFFF), который в виде знакового целого равняется числу -1.
Вывод: кто-то вызвал нативку Player с параметром -1, что и привело к крашу.
Найти конкретного виновника поможет программа, ссылку на которую я скинул выше.
Начало дампа стека
Обратите внимание, что дамп стека выровнен по границе 16 байт, так что, на самом деле вершина стека находится не по адресу 0x0019DF10, а как написано ниже — 0x0019DF18, так что, в данном случае, первые 8 байт можно пропустить (зачеркнуты). Синим выделены сохраненные значения регистров, зеленым — адреса возврата, а красным — кривые параметры, приведшие к крашу.
Stack: 1024 bytes starting at (ESP = 0019DF18)
Если мне интересно, почему карта крашнулась, то я обычно запускаю эту программу, пускай она и не всегда работает (но в таком случае я могу покопаться в памяти игры отладчиком, так что лень исправлять).
По твоему краш-репорту видно, что произошел вызов нативки Player с параметром -1, который и привел к ожидаемому крашу.
Я не изучил всё вдоль и поперек, но подозреваю, что при более низких задержках пропускная способность должна увеличиться.
Чтобы узнать наверняка, стоит провести тесты на хост-боте: запустить скрипт, который будет синхронизировать большое количество данных и замерить потраченное на это время при разных задержках.
Чтобы узнать время, можешь использовать нативку GetTickCount добавляемую этим модом.
Главное назначение этого мода — вернуть панель приказов, которую зачем-то удалили разработчики. А для быстрой перемотки используют ReplaySeeker, но, чтобы отмотать время назад, прийдется перезагрузить реплей и проигрывать его с самого начала до нужного момента.
В теории, если добавить в реплей ключевые точки через равные промежутки времени, то можно будет загружать реплей с ближайшей позиции и начинать промотку уже с неё.
Осталось только найти желающих реализовать это улучшение.
Использование неправильной конвенции вызова приведет к повреждению памяти стэка.
Хранящиеся в нём локальные переменные и адреса возврата могут быть перезаписаны случайными значениями, что приведет к непредсказуемым последствиям.
Хотя, стоит отметить, что виртуальная машина JASS'а (по крайней мере на 1.26а версии) способна правильно вызывать нативки использующие как cdecl так и stdcall, что используется мемхаком.
Но, если такую неправильную нативку вызовет кто другой, то весьма вероятны баги и краши.
Даже в твоём коде файла moduleutils.pas используется stdcall:
Библиотека "JassApi.dll" экспортирует функции, используя соглашение о вызове stdcall, а нативки варкрафта, возвращаемые функцией GetJassNative, используют соглашение cdecl.
И приведеный тобою код на самом деле находится в модуле "jassapi.pas".
Интересно, но это будет работать с мультиплеерными картами?
Разумеется, в этом и есть вся суть. Нет нужды просить всех игроков качать специальные лаунчеры, которые добавят новые возможности, карта сама распакует и загрузит DLL'ки.
Учтите, что данный ресурс использует старые версии JassAPI и RedirectCalls, так что, примеры из него не получится использовать с последними версиями.
Перезалил архив с модом. Добавил недостающую библиотеку "MinHook_x86.dll".
Надо будет разобраться, как её статически скомпоновать с библиотекой на FreePascal.
Сколько байт буфера занимает каждое выделение юнита за один момент времени?
Теперь ты можешь проверить это и сам, но всё же отвечу:
При добавлении одного юнита в выделение, шлется NET_COMMAND_UNIT_SELECTION_MODIFY (12 байт).
Каждый вызов SelectUnit всегда создает событие (если есть подписчики) и следовательно шлется NET_COMMAND_UNIT_SELECTION_EVENT (10 байт).
Также, могут слаться NET_COMMAND_UNIT_REFRESH_SUB_GROUP (1 байт) и NET_COMMAND_UNIT_SELECT_SUB_GROUP (13 байт).
Всё же, информация о том, что игра может отказаться слать команду из-за переполнения буфера оказалась ложной. Если это происходит, то игра сначала сбрасывает буфер хосту и затем снова проверяет, поместится ли команда и только лишь в противном случае отбрасывает её.
Проверил, у меня тоже после загрузки сохраненной игры пропадает заголовок у диалога.
Существует событие загрузки игры, но диалог ломается, если во время него изменить название. В таком случае, можно попробовать редактировать диалог не сразу, а после срабатывания таймера с нулевым интервалом. Еще, как вариант, можно устанавливать имя диалога каждый раз перед его показом.
Те, кто уже успел скачать, лучше перекачайте, так как кнопка "скачать" ссылалась на архив с версией без лимита операций (JassAcceleratorLimitless.zip), которая может привести к десинхронизации при игре в картах, в которых поток выполнения jass-скрипта обрывается из-за, например, бесконечных циклов. Разработчик мог не заметить ошибки, а вас выбросит из игры.
В таком случае, даже с поддержкой рефорджа она всё равно не помогла бы, так как процесс варкрафта уже будет убит ко времени запуска программы и никаких данных собрать не выйдет.
Здесь лучше подошел бы какой-нибудь логер вызовов нативок.
У функции SyncStoredString реализована отправка команды синхронизации, но не её прием.
Более того, все команды, что будут идти сразу за ней в одном буфере действий игрока проигнорируются.
Вызов следующего кода приведет к тому, что юнит визуально будет выделен, но на команды реагировать не будет.
Ред. IceFog
» WarCraft 3 / Переодические вылеты в карте, может кто по коду ошибки скажет.
В дампе памяти стека видны значения трёх сохраненных регистров (0x00000010 0x0019DFE8 0xFFFFFFFF), а также адрес возврата 0x6F3BBB58 и полученные аргументы: agent=0x00000010, auto_free=0x00000000. Кто-то передал кривой указатель 0x10, который ссылается на нечитаемую память.
Найти конкретного виновника поможет программа, ссылку на которую я скинул выше.
Синим выделены сохраненные значения регистров, зеленым — адреса возврата, а красным — кривые параметры, приведшие к крашу.
Stack: 1024 bytes starting at (ESP = 0019DF18)
E8 DF 19 00 75 91 45 6F10 00 00 00 E8 DF 19 00 ....u.Eo........0019DF20: FF FF FF FF 58 BB 3B 6F 10 00 00 00 00 00 00 00 ....X.;o........
0019DF30: BE 73 94 6F B8 D1 45 6F FF FF FF FF A4 D1 45 6F .s.o..Eo......Eo
Ред. IceFog
» WarCraft 3 / Переодические вылеты в карте, может кто по коду ошибки скажет.
» WarCraft 3 / Синхронизация данных
Ред. IceFog
» WarCraft 3 / MapHack для реплея
Осталось только найти желающих реализовать это улучшение.
» WarCraft 3 / Своя нативка на С++
Хранящиеся в нём локальные переменные и адреса возврата могут быть перезаписаны случайными значениями, что приведет к непредсказуемым последствиям.
Но, если такую неправильную нативку вызовет кто другой, то весьма вероятны баги и краши.
» WarCraft 3 / Своя нативка на С++
Ред. IceFog
» WarCraft 3 / Своя нативка на С++
В примере оттуда есть сгенерированные обертки для всех нативок из "common.j", но на pascal'е.
» WarCraft 3 / Работа с нативными функциями
Прокрутить к ресурсу
» WarCraft 3 / Сценарий на любом языке
Прокрутить к ресурсу
Ред. IceFog
» WarCraft 3 / Сценарий на любом языке
» WarCraft 3 / Установка варика в 2023 году
» WarCraft 3 / Установка варика в 2023 году
» WarCraft 3 / Сценарий на любом языке
» WarCraft 3 / Синхронизация данных
Надо будет разобраться, как её статически скомпоновать с библиотекой на FreePascal.
Ред. IceFog
» WarCraft 3 / Синхронизация данных
При добавлении одного юнита в выделение, шлется NET_COMMAND_UNIT_SELECTION_MODIFY (12 байт).
Каждый вызов SelectUnit всегда создает событие (если есть подписчики) и следовательно шлется NET_COMMAND_UNIT_SELECTION_EVENT (10 байт).
Также, могут слаться NET_COMMAND_UNIT_REFRESH_SUB_GROUP (1 байт) и NET_COMMAND_UNIT_SELECT_SUB_GROUP (13 байт).
» WarCraft 3 / Синхронизация данных
» WarCraft 3 / Пропадают названия диалогов
Существует событие загрузки игры, но диалог ломается, если во время него изменить название. В таком случае, можно попробовать редактировать диалог не сразу, а после срабатывания таймера с нулевым интервалом. Еще, как вариант, можно устанавливать имя диалога каждый раз перед его показом.
» WarCraft 3 / Открытая виртуальная машина
Ред. IceFog
» WarCraft 3 / Открытая виртуальная машина
Ред. IceFog
» WarCraft 3 / Работа с нативными функциями
Похоже, что главным файлом ресурса считается идущий последним в списке, а не первым.
» WarCraft 3 / Просмотр состояния виртуальной машины JASS
Здесь лучше подошел бы какой-нибудь логер вызовов нативок.
» WarCraft 3 / Фатал
» WarCraft 3 / Работа с нативными функциями
Ред. IceFog
» WarCraft 3 / Внутреннее устройство виртуальной машины
» WarCraft 3 / Помогите с синхронизацией
Более того, все команды, что будут идти сразу за ней в одном буфере действий игрока проигнорируются.