Добавлен , не опубликован
Способ реализации:

Предыстория.

Решил я внедрить в свой проект кастомный мультишот, да такой чтобы с правильным уроном, орбами и прочим. Нашел хорошую систему за авторством WereElf. Проверил код на возможные утечки, явных не нашел, написано толково. Однако HandleCounter с каждым выстрелом давал ясно понять, что на долгую катку с большим количеством стрелков можно не рассчитывать. С каждым снарядом дамми-юниты создавались, умирали, очищались и оставляли за собой хвосты. Мистика? Призраки? -_-)"
-Ghost-хэндлы. Сборщик мусора JASS забирать их не торопился, и я стал думать. Много думать, сильно думать. И пришел к решению, которое в той или иной степени по разному уже было кем-то реализовано. Использовать многоразовых дамми. Один дамик для мультишота не годится, значит нужно использовать нескольких. Но сколько их нужно? Тест надо было автоматизировать, и пошло-поехало.
В итоге родилась библиотека, реализующая "каршеринг" дамми-юнитов. Взял, покатался, поставил на место. Позволяет использовать свой пулл дамми юнитов для каждой задачи используя всего 3 простых, но эффективных метода.
  1. call DPM_Create("PoolName", size, LifeTime, RawCode) - Создает и регистрирует новый пул.
Задаем имя пула, например "FLAMEARROWS", прикидываем сколько дамиков в моменте нам может потребоваться, указываем на какой период кастер арендует дамика, указываем равкод дамика. Можно использовать где угодно, повторный вызов не перепишет существующую запись и будет проигнорирован. Хотя я рекомендую использовать в функции инициации триггера в котором будет использоваться, для удобства.
  1. set u = DPM_Acquire("FLAMEARROWS", Owner) - Арендуем свободного дамика для указанного игрока на время заданное по умолчанию при создании пула, а дальше перемещаем к кастеру и делаем свои дела. По истечении времени дамик вернется обратно в точку указанную в настройках. При парковке свободные дамики теряют совесть проходимость и в ожидании тусуются строго в одной точке, при надобности можно запарковать в темной непроходимой части карты.
(паркуйте своих дамиков правильно, не указывайте координаты за пределами карты, для крашей подобного рода у нас уже есть фантом лансер в доте айсфрога)
  1. call DPM_LifeTime(u, "FLAMEARROWS") - При необходимости использовать другое время аренды устанавливаем через сколько этот арендованный юнит припаркуется, станет нейтральным и снова свободным для аренды.
В отличии от метода создания+удаления и UnitApplyTimedLife, дамики будут кушать фиксированное количество памяти и не терять хвосты-хэндлы по всей памяти. Для систем с множеством дамми-юнитов мастхэв.
Библиотека самодостаточна, инициализирует сама себя, просто вставь и пользуйся. Код сопровожден комментариями с инструкцией по применению.
(Я не мастер клёвых шапок, посты форматировать оказалось сложнее, чем кодить на JASS)
Код библиотеки:
Открыть
// =========================================================================================================
// *********************************************************************************************************
// ** [LIBRARY] DPM (Dammy Pool Manager) - Менеджер Мульти-Пулов Dammy-юнитов **
// ** ------------------------------------------------------------------------------- **
// ** АВТОР: Animo Mori
// ** ВЕРСИЯ: 2.0 (Автономная инициализация и чистый API)
// ** ------------------------------------------------------------------------------- **
// ** ОПИСАНИЕ: Автоматически создает и управляет пулами Dammy-юнитов для устранения утечек.
// ** Использует хэш-таблицы для хранения данных и переиспользования ресурсов.
// ** ------------------------------------------------------------------------------- **
// ** ПУБЛИЧНЫЙ API (Вызов извне: DPM_FunctionName):
// ** 1. DPM_Create(poolName, size, lifeTime, rawCode)  -- Создает и регистрирует новый пул.
// ** 2. DPM_Acquire(poolName, player)                  -- Получает свободный Dammy-юнит из пула.
// ** 3. DPM_LifeTime(unit, poolName)                   -- Запускает таймер, возвращающий юнит в пул.
// ** ------------------------------------------------------------------------------- **
// *********************************************************************************************************
// =========================================================================================================
library DPM initializer InitDPM // Инициализируется автоматически при старте карты

    globals
        private hashtable PoolManagerHash      // Хэш-таблица для хранения ID пулов по имени
        private integer NextPoolId             // ID для следующего нового пула
        public hashtable DPHash               // Публичная хэш-таблица для внутренних нужд других систем

        // Массивы для хранения данных о пулах (доступ по PoolId)
        private unit array Pool_Stack[8192]          // Стек свободных юнитов
        private integer array Pool_FreeCount[8192]   // Количество свободных юнитов в стеке
        private real array Pool_LifeTime[8192]       // Время жизни юнита (до возврата в пул)
        private integer array Pool_UnitId[8192]      // RawCode юнита для данного пула
        private constant real BaseX = 0.0           // укажите где парковать дамиков
        private constant real BaseY = 0.0           
    endglobals

    private function InitDPM takes nothing returns nothing
        set PoolManagerHash = InitHashtable()
        set DPHash = InitHashtable()
        set NextPoolId = 1
    endfunction 

    // 1. Создание и регистрация нового пула (PUBLIC API: DPM_Create)
    public function Create takes string poolName, integer size, real lifeTime, integer rawCode returns boolean
        local integer poolKey = StringHash(poolName)
        local integer poolId
        local integer count = size 
        
        if HaveSavedInteger(PoolManagerHash, poolKey, 0) then
            return false // Пул уже существует
        endif

        set poolId = NextPoolId
        set NextPoolId = NextPoolId + 1 

        if poolId >= 8192 then
            return false // Превышен лимит пулов
        endif
        
        call SaveInteger(PoolManagerHash, poolKey, 0, poolId)
        set Pool_FreeCount[poolId] = size
        set Pool_LifeTime[poolId] = lifeTime
        set Pool_UnitId[poolId] = rawCode

        loop // Создание юнитов и помещение их в стек
            exitwhen count <= 0
            set count = count - 1
            set Pool_Stack[poolId*8192 + count] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), rawCode, BaseX, BaseY, 0.0)
            call SetUnitPathing(Pool_Stack[poolId*8192 + count], false)
        endloop
        
        return true
    endfunction 
    
    // 2. Получить свободный даммик из пула и установить владельца (PUBLIC API: DPM_Acquire)
    public function Acquire takes string poolName, player p returns unit
        local integer poolKey = StringHash(poolName)
        local integer poolId = LoadInteger(PoolManagerHash, poolKey, 0)
        local integer count
        local unit u
        
        if poolId == 0 then
            return null
        endif

        set count = Pool_FreeCount[poolId]
        
        if count > 0 then
            set Pool_FreeCount[poolId] = count - 1
            set u = Pool_Stack[poolId*8192 + count - 1] 
            
            call SetUnitOwner(u, p, false)
            call SetUnitX(u, 0.0) 
            call SetUnitY(u, 0.0)
            return u
        endif
        return null // Пул пуст
    endfunction
    


    // ПРИВАТНЫЕ ФУНКЦИИ 
    private function ReturnUnitToStack takes unit u, integer poolId returns nothing
        local integer count
        
        call SetUnitOwner(u, Player(PLAYER_NEUTRAL_PASSIVE), false)
        call UnitRemoveAbility(u, 'Amrf') 
        call SetUnitPathing(u, false)
        call SetUnitX(u, BaseX) 
        call SetUnitY(u, BaseY)
        
        set count = Pool_FreeCount[poolId]
        set Pool_Stack[poolId*8192 + count] = u 
        set Pool_FreeCount[poolId] = count + 1
    endfunction

    private function TimerCallback takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer pk = GetHandleId(t)
        local unit u = LoadUnitHandle(DPHash, pk, 0)
        local integer poolId = LoadInteger(DPHash, pk, 1)

        if u != null and poolId > 0 then
            call ReturnUnitToStack(u, poolId)
        endif
        
        call FlushChildHashtable(DPHash, pk)
        call DestroyTimer(t)
        set t = null
        set u = null
    endfunction
// 3. Запуск таймера для возврата (PUBLIC API: DPM_LifeTime)
    public function LifeTime takes unit u, string poolName returns nothing
        local integer poolKey = StringHash(poolName)
        local integer poolId = LoadInteger(PoolManagerHash, poolKey, 0)

        if poolId == 0 then 
            return 
        endif
        
        local timer t = CreateTimer()
        local integer pk = GetHandleId(t)
        
        call SaveUnitHandle(DPHash, pk, 0, u) 
        call SaveInteger(DPHash, pk, 1, poolId) 

        call TimerStart(t, Pool_LifeTime[poolId], false, function TimerCallback)
        set t = null
    endfunction

endlibrary
`
ОЖИДАНИЕ РЕКЛАМЫ...
23
Ваш ресурс не прошёл автомодерацию. Пожалуйста, внесите исправления или ожидайте проверки модератором.
Причина
Должен присутствовать файл одного из типов: bas, c, clj, cls, cpp, cxx, cs, d, go, h, hpp, hxx, hs, inc, java, l, lisp, m, ml, pas, pp, p, php, pl, pm, py, r, rb, rs, lua, jass, j, resx, scala, swg, tcl, xsl, y, js, css, scss, less, ini, json, xml, twig, conf, vb, vbe, vbs, galaxy, yaml, gml, w3m, w3x, scm, scx, sc2map, sc2ma, pud, w3n, sc2mod, asset, assets, mat, uasset, apx, apb, ies, gmez, yy, zip, 7z, rar, tar, gz, apk, gzip, jar, lzip, tgz, tbz2, bz2, zipx, zz, dmg, ipg, z, iso, txt, md, tex, rtf, etf, lic, log, text, adoc, txt2
Your resource has not passed automoderation. Please make corrections or wait for the moderator to check.
Reason
There must be a file of one of the following types: bas, c, clj, cls, cpp, cxx, cs, d, go, h, hpp, hxx, hs, inc, java, l, lisp, m, ml, pas, pp, p, php, pl, pm, py, r, rb, rs, lua, jass, j, resx, scala, swg, tcl, xsl, y, js, css, scss, less, ini, json, xml, twig, conf, vb, vbe, vbs, galaxy, yaml, gml, w3m, w3x, scm, scx, sc2map, sc2ma, pud, w3n, sc2mod, asset, assets, mat, uasset, apx, apb, ies, gmez, yy, zip, 7z, rar, tar, gz, apk, gzip, jar, lzip, tgz, tbz2, bz2, zipx, zz, dmg, ipg, z, iso, txt, md, tex, rtf, etf, lic, log, text, adoc, txt2
// CID1
1
Лучше использовать встроенный в WFE HandleCounter... Он подсчитывает реальное количество хендлов и не полагается на мистические костыли жасс машины
2
DiZzicH, увы ни один счетчик хэндлов не подскажет при заведомо правильно составленном коде что именно протечёт в самом движке во время игры. И без мистических хк опытным путём воочию видно при стресс-тесте какой-нибудь системы с дамиками как за 5 минут проседает и начинает тормозить игра. Сам юнит то может и правильно убит/обнулён, но за его недолгую, но счастливую жизнь какую-то память о себе (не то чтобы светлую) он все равно оставит. Да, своего рода костыль для решения проблемы которой по идее не должно существовать, если бы джасовский Garbage Collector честно отрабатывал свой хлеб, но увы.
Этот метод позволил добиться стабильности и производительности нескольких систем в которых дамми-юнитов требовалось много, без всяких пересчетов-перепроверок и добиваний того, что и так должно было быть добито.
1
Сам юнит то может и правильно убит/обнулён, но за его недолгую, но счастливую жизнь какую-то память о себе (не то чтобы светлую) он все равно оставит.
Могу ошибаться, но структуры юнитов кешируются игрой и потом переиспользуются, а твоя система по сути делает тоже самое что и делает движок игры...
увы ни один счетчик хэндлов не подскажет при заведомо правильно составленном коде что именно протечёт в самом движке во время игры.
Не покажет, но использовать заведомо некорректный HC такая себе практика
И всё-же такая система имеет место быть, чтобы не разгонять счетчик хендлов и не плодить лишний мусор
38
Модерация
Приветствую! Ресурс был снят с публикации и требует доработки по следующим причинам:
В данном случае вообще не нужно указывать никаких кнопок "получить"...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.