Библиотека с твоим скриптом загружается и исполняется паралельно у всех игроков.
Просто пиши код также, как ты делал это на JASS'е. Правила те же: все действия должны быть одинаковы на компьютерах всех игроков. Локально можно делать лишь некоторые, такие как смена цвета/размера юнита.
Vampir_kolik, создание юнита для локального игрока вызовет десинк на любом языке.
На игровом клиенте красного игрока создается юнит, принадлежащий игроку красному, а на клиенте синего - синему. В итоге состояние мира отличается и они играют в разные игры.
В моём примере главный работник создается для красного игрока, а прочие для игроков 1-10.
Вижу, ты цитируешь старую версию исходников библиотеки со скриптом, советую скачать последнюю.
Я как-то делал интереса ради одну систему, которая позволяла достичь этой цели.
Принцип работы: сначала временный скрипт распаковывает и загружает библиотеки, а затем он компилирует и запускает оригинальный скрипт карты, после чего самоуничтожается. Таким образом, к моменту запуска основного сценария, нативки уже зарегистрированы и компилятор не ругается.
Вот только, на сайте сейчас нерабочая версия висит. Я скрывал ресурс ибо лень исправлять, но модератор зачем-то его опубликовал.
Впрочем, думаю есть способ получше: можно модифицировать байт-код во время исполнения таким образом, чтобы вызов обычной функции превратился в вызов нативки.
Будет выглядеть как-то так
function MyNativeFunction takes nothing returns nothing
endfunction
function main takes nothing returns nothing
call RedirectToNative(function MyNativeFunction)
// На самом деле вызовется не пустая функция объявленная выше, а нативка с таким же именем.
call MyNativeFunction()
endfunction
ну там действительно есть дллки, которые появляются во время запуска карты.
Не думаю, что карта стоит упоминания, если только в ней не находится код, извлекающий файлы из архива, в чем я сомневаюсь, ведь он имеет расширение mix, а значит должен быть библиотекой, которая и сама себя сможет распаковать.
Могу предположить, что автор защиты не стал особо мудрить и поместил архив в ресурсы библиотеки, может даже не зашифровав его. Если всё так, то тебе лишь нужно открыть файл в программе, умеющей показывать вшитые в исполняемые файлы ресурсы, и извлечь их. Возможно, среди них найдется искомый архив.
Еще, как вариант, при помощи специальных программ, можно посмотреть какие файлы открывает игра и, если повезет, среди них может обнаружиться распакованый архив.
Если же нет, то придется восстанавливать исходный код библиотеки, после чего можно будет выяснить, каким же образом она добавляет новые файлы в игру.
Можешь заняться этим сам, воспользовавшись программами для обратной разработки (например, IDA), либо нанять специалистов, которые выполнят работу за тебя.
Файл mix, в первую очередь, является библиотекой (DLL). К ней можно прикрепить MPQ-архив, получив тем самым mix-архив, но вовсе необязательно, что твой файл относится к этому типу.
Возможно, эта библиотека извлекает из себя архив во временную папку и загружает оттуда в игру. А может, она перехватывает запросы игры на ресурсы (модели, текстуры) и загружает их из памяти. Сделано так могло быть из нежелания автора делиться своими трудами.
А еще, автор может, при желании, засунуть туда вирусов и майнить на твоей видеокарте или удалить тебе все файлы с диска, если ты ему в игре не понравишься.
Из-за того, что нативки возвращают строки в виде индексов из таблицы строк главного потока карты, все прочие потоки, не являющиеся дочерними ему, например те, что исполняют прелоад скрипты или ИИ, не могут получить результат от нативок с таким типом. Индекс не будет подходить для их таблицы строк.
Будет получена случайная строка или произойдет выход за границы таблицы с печальным концом.
Получение строк в результате вызова нативок работает лишь в главном потоке.
Инструкция по адресу 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 байт).
» WarCraft 3 / Сценарий на любом языке
Просто пиши код также, как ты делал это на JASS'е. Правила те же: все действия должны быть одинаковы на компьютерах всех игроков. Локально можно делать лишь некоторые, такие как смена цвета/размера юнита.
» WarCraft 3 / Сценарий на любом языке
На игровом клиенте красного игрока создается юнит, принадлежащий игроку красному, а на клиенте синего - синему. В итоге состояние мира отличается и они играют в разные игры.
В моём примере главный работник создается для красного игрока, а прочие для игроков 1-10.
» WarCraft 3 / Открытая виртуальная машина
Ред. IceFog
» WarCraft 3 / Работа с нативными функциями
» WarCraft 3 / Помогите вспомнить карту !
Ред. IceFog
» WarCraft 3 / Намертво зависает вкладка Сценарий/свойство игрока.
Ред. IceFog
» WarCraft 3 / Laddik не может открыть архив
Можешь заняться этим сам, воспользовавшись программами для обратной разработки (например, IDA), либо нанять специалистов, которые выполнят работу за тебя.
Ред. IceFog
» WarCraft 3 / Laddik не может открыть архив
» WarCraft 3 / Просмотр состояния виртуальной машины JASS
» WarCraft 3 / Получение строки в Prealod файле
Ред. 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 байт).