19

» WarCraft 3 / Переодические вылеты в карте, может кто по коду ошибки скажет.

Принятый ответ
Инструкция по адресу 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)
= addr ** *
0019DF10: E8 DF 19 00 75 91 45 6F 10 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
начало метода CGameState::GetAgentHandle
.text:6F430C80 ; int __thiscall CGameState::GetAgentHandle(CGameState *this, CAgent *agent, BOOL AutoFree)
.text:6F430C80 CGameState__GetAgentHandle proc near    ; CODE XREF: CUnit__PostEvent+8F↑p
.text:6F430C80                                         ; CUnit__NotifyAboutInstantOrder+195↑p ...
.text:6F430C80
.text:6F430C80 agent           = dword ptr  4
.text:6F430C80 AutoFree        = dword ptr  8
.text:6F430C80
.text:6F430C80                 push    ebx
.text:6F430C81                 push    ebp
.text:6F430C82                 mov     ebp, [esp+8+agent]
.text:6F430C86                 test    ebp, ebp
.text:6F430C88                 push    esi
.text:6F430C89                 mov     ebx, ecx
.text:6F430C8B                 jz      loc_6F430D9B
.text:6F430C91                 mov     esi, [ebp+10h]
нативка Player
.text:6F3BBB30 ; DWORD __cdecl Jass::Common::Player(int number)
.text:6F3BBB30 Jass__Common__Player proc near          ; DATA XREF: InitJass+3007↓o
.text:6F3BBB30
.text:6F3BBB30 number          = dword ptr  4
.text:6F3BBB30
.text:6F3BBB30                 mov     eax, [esp+number]
.text:6F3BBB34                 mov     ecx, CGameWar3__Instance
.text:6F3BBB3A                 push    esi
.text:6F3BBB3B                 push    eax
.text:6F3BBB3C                 call    CGameWar3__GetPlayer
.text:6F3BBB41                 mov     ecx, CGameWar3__Instance
.text:6F3BBB47                 mov     esi, eax
.text:6F3BBB49                 call    CGameWar3__GetGameState
.text:6F3BBB4E                 push    0
.text:6F3BBB50                 push    esi
.text:6F3BBB51                 mov     ecx, eax
.text:6F3BBB53                 call    CGameState__GetAgentHandle
.text:6F3BBB58                 pop     esi
.text:6F3BBB59                 retn
.text:6F3BBB59 Jass__Common__Player endp
19

» WarCraft 3 / Переодические вылеты в карте, может кто по коду ошибки скажет.

Если мне интересно, почему карта крашнулась, то я обычно запускаю эту программу, пускай она и не всегда работает (но в таком случае я могу покопаться в памяти игры отладчиком, так что лень исправлять).
По твоему краш-репорту видно, что произошел вызов нативки Player с параметром -1, который и привел к ожидаемому крашу.
19

» WarCraft 3 / Синхронизация данных

Я не изучил всё вдоль и поперек, но подозреваю, что при более низких задержках пропускная способность должна увеличиться.
Чтобы узнать наверняка, стоит провести тесты на хост-боте: запустить скрипт, который будет синхронизировать большое количество данных и замерить потраченное на это время при разных задержках.
Чтобы узнать время, можешь использовать нативку GetTickCount добавляемую этим модом.
19

» WarCraft 3 / MapHack для реплея

Главное назначение этого мода — вернуть панель приказов, которую зачем-то удалили разработчики. А для быстрой перемотки используют ReplaySeeker, но, чтобы отмотать время назад, прийдется перезагрузить реплей и проигрывать его с самого начала до нужного момента.
В теории, если добавить в реплей ключевые точки через равные промежутки времени, то можно будет загружать реплей с ближайшей позиции и начинать промотку уже с неё.
Осталось только найти желающих реализовать это улучшение.
19

» WarCraft 3 / Своя нативка на С++

Использование неправильной конвенции вызова приведет к повреждению памяти стэка.
Хранящиеся в нём локальные переменные и адреса возврата могут быть перезаписаны случайными значениями, что приведет к непредсказуемым последствиям.
Хотя, стоит отметить, что виртуальная машина JASS'а (по крайней мере на 1.26а версии) способна правильно вызывать нативки использующие как cdecl так и stdcall, что используется мемхаком.
Но, если такую неправильную нативку вызовет кто другой, то весьма вероятны баги и краши.
19

» WarCraft 3 / Своя нативка на С++

Даже в твоём коде файла moduleutils.pas используется stdcall:
Библиотека "JassApi.dll" экспортирует функции, используя соглашение о вызове stdcall, а нативки варкрафта, возвращаемые функцией GetJassNative, используют соглашение cdecl.
И приведеный тобою код на самом деле находится в модуле "jassapi.pas".
19

» WarCraft 3 / Своя нативка на С++

int __stdcall test()
Нативки используют конвенцию вызова cdecl.
Можно увидеть пример наивной функции, использующей JREAL?
Я не специалист по C++, но если хочешь создать нативку принимающую/возвращающую вещественные числа, то это будет выглядеть примерно так:
uint32_t __cdecl jIncrementReal(float *value) {
	return reinterpret_cast<uint32_t>(*value + 1.0f);
}

RegisterJassNative("IncrementReal", "(R)R", &jIncrementReal);
В JASS скрипте объявляется следующим образом:
native IncrementReal takes real value returns real
Также можешь взглянуть на этот ресурс.
В примере оттуда есть сгенерированные обертки для всех нативок из "common.j", но на pascal'е.
19

» WarCraft 3 / Работа с нативными функциями

Вышла новая версия!
Прокрутить к ресурсу
  • Изменения в RedirectCalls
    • Теперь может быть безопасно загружен во время игры.
    • Может быть одновременно установлен в папку с игрой и встроен в карту: активен будет лишь первый загруженый экземпляр.
19

» WarCraft 3 / Сценарий на любом языке

Вышла новая версия!
Прокрутить к ресурсу
  • Адаптировал к новым версиям JassAPI и RedirectCalls.
  • Больше деталей о работе системы.
  • Выложил исходники cJASS-скрипта и встроенной в него программы.
  • Упростил процесс установки.
  • Приложил генератор кода для нативок.
19

» WarCraft 3 / Сценарий на любом языке

Интересно, но это будет работать с мультиплеерными картами?
Разумеется, в этом и есть вся суть. Нет нужды просить всех игроков качать специальные лаунчеры, которые добавят новые возможности, карта сама распакует и загрузит DLL'ки.
Учтите, что данный ресурс использует старые версии JassAPI и RedirectCalls, так что, примеры из него не получится использовать с последними версиями.
19

» WarCraft 3 / Установка варика в 2023 году

Ты имеешь в виду редактор? Как это делается?
В настройках операционной системы.
19

» WarCraft 3 / Установка варика в 2023 году

Язык программ, не поддерживающих юникод, должен быть установлен на русский.
19

» WarCraft 3 / Сценарий на любом языке

Этот файл автоматически сгенерирован на основе "common.j".
Отрывок из common.j
constant itemtype ITEM_TYPE_PERMANENT                   = ConvertItemType(0)
constant itemtype ITEM_TYPE_CHARGED                     = ConvertItemType(1)
constant itemtype ITEM_TYPE_POWERUP                     = ConvertItemType(2)
constant itemtype ITEM_TYPE_ARTIFACT                    = ConvertItemType(3)
constant itemtype ITEM_TYPE_PURCHASABLE                 = ConvertItemType(4)
constant itemtype ITEM_TYPE_CAMPAIGN                    = ConvertItemType(5)
constant itemtype ITEM_TYPE_MISCELLANEOUS               = ConvertItemType(6)
constant itemtype ITEM_TYPE_UNKNOWN                     = ConvertItemType(7)
constant itemtype ITEM_TYPE_ANY                         = ConvertItemType(8)
// Deprecated, should use ITEM_TYPE_POWERUP
constant itemtype ITEM_TYPE_TOME                        = ConvertItemType(2)
19

» WarCraft 3 / Синхронизация данных

Перезалил архив с модом. Добавил недостающую библиотеку "MinHook_x86.dll".
Надо будет разобраться, как её статически скомпоновать с библиотекой на FreePascal.
19

» 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 байт).
Следовательно, пошлется до 36 байт.
19

» WarCraft 3 / Синхронизация данных

Всё же, информация о том, что игра может отказаться слать команду из-за переполнения буфера оказалась ложной. Если это происходит, то игра сначала сбрасывает буфер хосту и затем снова проверяет, поместится ли команда и только лишь в противном случае отбрасывает её.
IDA HexRays псевдокод функции отправки команды
void __fastcall NetSendCommandStore(CDataStore *stream, int SessionId)
{
  CNetData *NetData; // esi
  CPlayerWar3 *player; // eax
  DWORD length; // [esp+Ch] [ebp-Ch] MAPDST BYREF
  void *buffer; // [esp+10h] [ebp-8h] BYREF

  NetData = (CNetData *)*((_DWORD *)GetWc3Prop(PROP_SYNDATA)[4] + SYNDATA_NETDATA);
  if ( SessionId == NetData->ActiveSessionId_
    && *(_DWORD *)&NetData->Sessions.buffer_field1EC[0x304 * SessionId + 0x7C] >= GAME_STATE_LOADING )
  {
    stream->vtable->GetValues(stream, &buffer, &length, 0);
    if ( length )
    {
      if ( SessionId
        || !g_GameWar3
        || (player = CGameWar3::GetPlayer(g_GameWar3, g_GameWar3->LocalPlayerId),
            !CPlayerWar3::IsActionLimitReached(player, *(_BYTE *)buffer)) )
      {
        if ( length + NetData->ActionsStream.Length - NetData->ActionsStreamOverhead >= 1024 )
          CNetData::SendCommands(NetData); // после этого вызова буфер очищается
        if ( length + NetData->ActionsStream.Length - NetData->ActionsStreamOverhead >= 1024 )
          debug_log("NetSendCommandStore: rejected, %u bytes\n", length);
        else
          Stream::Write(&NetData->ActionsStream, buffer, length);
      }
    }
  }
}
19

» WarCraft 3 / Пропадают названия диалогов

Принятый ответ
Проверил, у меня тоже после загрузки сохраненной игры пропадает заголовок у диалога.
Существует событие загрузки игры, но диалог ломается, если во время него изменить название. В таком случае, можно попробовать редактировать диалог не сразу, а после срабатывания таймера с нулевым интервалом. Еще, как вариант, можно устанавливать имя диалога каждый раз перед его показом.
19

» WarCraft 3 / Открытая виртуальная машина

У меня были мысли о создании проэкта для совместного реверсинга варкрафта, как у майнкрафта есть MCP. Только нужно придумать как это всё оформить.
19

» WarCraft 3 / Открытая виртуальная машина

Те, кто уже успел скачать, лучше перекачайте, так как кнопка "скачать" ссылалась на архив с версией без лимита операций (JassAcceleratorLimitless.zip), которая может привести к десинхронизации при игре в картах, в которых поток выполнения jass-скрипта обрывается из-за, например, бесконечных циклов. Разработчик мог не заметить ошибки, а вас выбросит из игры.
19

» WarCraft 3 / Работа с нативными функциями

IceFog, Пожалуйста выложи свой JassApi,dll
Исправил. Теперь кнопка скачать ссылается на правильный архив.
Похоже, что главным файлом ресурса считается идущий последним в списке, а не первым.
19

» WarCraft 3 / Просмотр состояния виртуальной машины JASS

без ошибки, просто фризит и вылетает
В таком случае, даже с поддержкой рефорджа она всё равно не помогла бы, так как процесс варкрафта уже будет убит ко времени запуска программы и никаких данных собрать не выйдет.
Здесь лучше подошел бы какой-нибудь логер вызовов нативок.
19

» WarCraft 3 / Работа с нативными функциями

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

» WarCraft 3 / Внутреннее устройство виртуальной машины

Если убрать проверку на аргументы, то игра начнет автоматически обнулять переменные и тогда перестанут утекать слоты таблицы хэндлов.
Также можно уничтожать объекты на которые ссылались освобожденные хэндлы и забыть об ошибках выделения памяти из-за мусорных точек, групп и прочего.
Но в таком случае, мод должен будет присутствовать на всех клиентах, а иначе десинхронизация.
19

» WarCraft 3 / Помогите с синхронизацией

У функции SyncStoredString реализована отправка команды синхронизации, но не её прием.
Более того, все команды, что будут идти сразу за ней в одном буфере действий игрока проигнорируются.
Вызов следующего кода приведет к тому, что юнит визуально будет выделен, но на команды реагировать не будет.
call SyncStoredString(Cache, Key1, Key2)
call SelectUnit(u, True)