Dragoon
offline
Опыт:
544Активность: |
Ограничение на количество действий в функциях JASS
P.S. В тексте будут использоваться функции из статьи http://xgm.guru/forum/showthread.php?t=4094&page=1
Часть 1 В JASS присутствует крайне неприятная особенность - у функций есть ограничение на количество действий. Легко понять это поможет следующий пример. Код:
Дело в том, что функция BitwiseXOR вызывает еще одну функцию - BitwiseOperation с определенными параметрами. BitwiseOperation так же вызывает несколько пользовательских функций. Получается цепочка некоторой длины. И в какой-то момент времени внутренний счетчик действия варкрафта оказывается превышен и вся цепочка завершается. Причем, завершается не только BitwiseOperation или BitwiseXOR , но и сам триггер, в котором была вызвана эта функция. Примеров для подобных случаев можно привести множество, так как достаточно сложные заклятья или функции могут множество раз вызывать другу друга, использовать циклы и т.п. Есть подозрение, что в варкрафте существует определенная "система ценностей" операндов. Т.е., к примеру, set ценится намного ниже call и следовательно его можно вставлять в фунции намного чаще. Теперь рассмотрим как можно решить эту проблему. 1) Уменьшить "ценность" функции, т.е. убрать лишние действия, в общем выполнить оптимизацию. 2) Иногда помогает данный кусок кода Код:
Логически это бессмысленно, т.е. мы 'усыпляем' нашу функцию на 0 секунд. Однако как это ни бессмысленно, это помогает... Вероятно когда мы вызываем эту функцию, варкрафт сбрасывает счетчик или уменьшает его. Предложенные выше методы действенны, однако далеко не всегда помогают. В моем случае они были бессильны, поэтому пришлось искать дополнительные возможности решения. И решение было найдено. Варкрафт - многопоточное приложение, т.е. в нем может несколько действий выполняться одновременно (почти одновременно). К примеру у нас есть триггер, реагирующий на событие - применение юнитом определенной способности. Если два и более юнитов применят одновременно способность, то варкрафт запустит сразу несколько копий этого триггера ( именно эта особенность заставляет использовать локальные переменные ). Причем, для каждого нового триггера создается свой собственный счетчик. В этом и заключается вся суть решения. Мы создаем функцию-оболочку, назначение которой - лишь запустить какое-либо ресурсоемкое действие, которое нам было необходимо выполнять в цикле. В моем случае это Код:
Далее мы динамически создаем триггер и добавляем ему в качестве действия только что созданную функцию. Теперь в коде вместо прямого вызова функции BitwiseXOR мы будет использовать вызов триггера, который и инициирует выполнение этой функции. Код:
Подобную конструкцию уже можно использовать и она даже будет работать, но в какой-то момент времени вы заметите баги. Дело в том, что, как я уже писал, каждый вызываемый триггер запускается в отдельном потоке, и варкрафт вполне может успеть запустить в цикле несколько копий триггера ttrigger. А вот это уже совсем нехорошо. Будем избавляться... Я предлагаю использовать глобальную переменную-флаг, которую мы будем устанавливать в False перед запуском функции TriggerExecute, а затем в коде функции-оболочки будем заносить в переменную True, что означает - код функции завершился. К цикле же мы будем ждать, пока переменная не станет равной True и только тогда можно запускать еще раз динамически созданный триггер. Код:
Код функции-оболочки в данном случае меняется на Код:
Приведенный пример уже будет работать, причем выполнять подобным способом действия в цикле можно огромное количество раз, не боясь, что код аварийно завершится. Рассмотрим недостатки системы 1) Так как функция оболочка не может получать параметров, то функция вызывается с предустановленными значениями. Методы решения: а) Использование глобальных переменных-контейнеров для передачи параметров б) Использование Return Bug и SCV (как это можно использовать - далее) 2) Появление в функции побочного кода, соответственно он становится менее воспринимаем другими людьми. Методов решения естественно нет, так как без этого самого кода ваши функции просто не будут работать. Часть 2 Я написал несколько функций, которые упрощают работу с динамическими триггерами и передачей функциям-оболочкам параметров. В них я использовал Retrun Bug и SVC. Кратко о том, что это вообще такое. 1) Return Bug - позволяет получить адрес любого объекта 2) SVC - Super Custom Value . Идея заключается в том, чтобы использовать уникальный адрес объекта в качестве общего ключа при обращении к кэшу. Позволяет сделать любой объект хранилищем переменных. Подробнее читайте в статьях Sergey'а, DimonT'а и NETRAT'а. Перед использованием функций необходимо создать глобальную переменную типа gamecache(Буфер игры) и триггер следующей структуры: Код:
Теперь собственно к самому коду. Первые функции всего лишь позволяют нам получать адреса объектов и функций. Код:
Следующие функции отвечают за создание-уничтожение динамических триггеров. Код:
Теперь несколько функций, позволяющий передавать параметры. Код:
И последняя функция отвечает за запуск потока и возвращение в главный код лишь по выполнении функции-оболочки Код:
Как видно, все глобальные переменные теперь перенесены в кэш. И кстати, теперь в функции-оболочке ОБЯЗАТЕЛЬНО в конце должна стоять строка Код:
Эта строка устанавливает флаг выполнения в True, что и говорит функции RunThreadAndWait о том, что можно возвращаться в главный поток. В примере приведены пять триггеров. 1) Init - инициализирует кэш и запускает триггер-пример, показывающий битовые операции 2) Execution 2 - без использования новых функций и возможностей. 3) Execution 1 - с ними. 4) Execution 3 - показывает, что вообще без использования каких-либо доработок триггер не работает корректно 5) Test - всего лишь пример работы битового модуля Для просмотра результатов необходимо выключать один из них. Даже два сразу работать НЕ БУДУТ, так как используют одну и ту же переменную для создания плавающего текста. Смысла делать их параллельными не вижу, поэтому оставил все так, как есть. Отредактировано Dragoon, 03.05.2006 в 18:44. |
03.05.2006, 15:21 | #1
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
remal
нечто
offline
Опыт:
2,087Активность: |
потоки в варе выполняются по очереди. каждый поток выполняется либо определённое время (и завершается от Watchdog Timer'a), либо до вызова triggersleepaction/sleep. также переход "очерёдности" происходит при использовании executefunc. поэтому вводить глобальную переменную не надо. |
04.05.2006, 14:47 | #2
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Dragoon
offline
Опыт:
544Активность: |
Но почему тогда необходимо применять локальные переменные в способностях и т.п. ? Если бы триггеры выполнялись по очереди, то глобалки были бы панацеей. Я не прав ?
Dragoon добавил: Кстати, от глобальной переменной я отказался в части 2. Там создается связанная с созданным триггером переменная-флаг. Да и если ее полностью удалить, то в целевой функции станут невозможны функции TriggerSleepAction и ExecuteFunction , как ты сам и писал... А так файтически нет ограничений на исполняемый код |
04.05.2006, 15:05 | #3
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
NETRAT
offline
Опыт:
83,712Активность: |
О, нет, не по очереди, а в потоках, только фиг его знает как менеджер их выполняет в варе |
05.05.2006, 01:43 | #4
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
FellGuard
Losyash
offline
Опыт:
39,547Активность: |
NETRAT, труе, не по очереди. Для каждого игрока свой поток АИ, плюс триггеры, когда одновременно запускаются несколько штук, плюс главный поток - если бы все это было бы по очереди, то Вар был бы на уровне тетриса - например, АИ на более чем 3 человека уже начинал бы тормозить и глючить с принятием решений и выдачей приказов.. Такого нет.
|
05.05.2006, 10:17 | #5
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Dragoon
offline
Опыт:
544Активность: |
Вот я про то и говорю... |
05.05.2006, 22:05 | #6
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Toadcop
offline
Опыт:
54,313Активность: |
Dragoon ну это уже было известно ! т.е. все большие лооп конструкции надо Ексекутить :) а том он вешает их и всё :) да Близзы мастеры своего дела :) !
|
06.05.2006, 20:09 | #7
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Dragoon
offline
Опыт:
544Активность: |
Toadcop, целью статьи было показать конкретный метод решения и его реализацию, а не рассказывать об этой неприятности... :) |
10.05.2006, 13:29 | #8
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|