Появилась перспективная идея - подсунуть жассхелперу свой парсер вместо pjass и через него запилить работу с Lua во внешнем редакторе.
Возможно идея не самая свежая - тут или на хайве кто-то мог уже это придумать раньше и лучше меня.
Результат первого теста - если оставить жассхелпер и выпилить pjass парсер, то все исправно работает в Lua режиме - пропадает необходимость отключать сам жассхелпер, хоть и толку от него самого мало без pjass парсера, естественно.Но это дает возможность затолкать жассхелперу что-то другое вместо pjass парсера не нарушая работоспособность редактора в Lua режиме.
На очереди второй тест - проверить порядок выполнения парсеров и не тупиковая ли версия кода используется на этапе работы жассхелпера. Пока у меня сложилось впечатление, что сперва генерится стандартный .j файл, со вставками lua кода, а потом этот файл скармливается конвертеру в Lua. Если жассхелпер отрабатывает до конвертера в Lua и результат его работы передается дальше, а не теряется, то кастомный парсер вполне может подтягивать внешний Lua код и подсовывать его в нужное место до конверта всего кода карты в Lua.
А потом останется только написать этот самый парсер который будет втягивать внешние файлы.
Увы, продолжить смогу только вечером, если вобще смогу найти достаточно времени.
NazarPunk, первое срабатывание - да, может даже со 100% надежностью, а создание и старт таймера произойдут раньше. Я предпочитаю не рисковать и выношу такие вещи в места которые гарантированно не вызываются слишком рано.
Даже систему модулей/библиотек в итоге себе для этого пилить начал, хотя изначально не собирался сильно этим заморачиваться.
NazarPunk, использование анонимной функции в триггере без необходимости это, имхо, лишнее - лишний кложур за собой тащиш. Старт таймера там где он у тебя стартует тоже выглядит опасно - этот код же выполнится еще до триггеров инициализации карты.
А ничего что ты координаты где собираешся создавать перезаписываеш постоянно при переборе? Вместо того чтобы взять их прямо перед удалением...
Соответственно, в x y при текущей реализации всегда будут координаты последнего рудника, а не того который был удален...
Логичней было бы не отдавать наружу из перебора координаты вобще, а использовать их только внутри перебора, а потом отдельно брать координаты рудника перед удалением в другие локалки.
То что ты нашел - это я храню список функций которые вызываются триггером отслеживающим смерти юнитов - чтобы не плодить множество таких триггеров без необходимости и заодно контролировать порядок срабатывания обработчиков смерти когда это понадобится.
В твоем случае - смотря что ты чистить будеш.
Если данные связаные с системой - обнуляеш те переменные в таблице-записи которые использовал и которые не нужны в других местах. data.s = nil data.a=nil и так далее.
Если все данные на юнита (юнит умер или удаляется) - записываем nil по хендлу. HandleData[GetHandleId(u)] = nil.
У меня в либе для ручной чистки я вызвал бы HandleData:Purge(GetHandleId(u)), если бы не полагался на автоматическую чистку по какой-то причине, но там внутри пока все то-же самое присвоение nil.
Для особо тяжелых случаев можно заводить еще дочерние записи в таблице-записи - если какая-то система хранит много данных на одного юнита, особенно если все эти данные временные, то можно ложить их целой таблицей и потом всей таблицей и убивать
data.somebulkdata = { a=42 b=42 c=42}
data.somebulkdata = nil
Еще есть нюанс - для критичных по производительности мест стоит использовать массивы вместо таблиц. По сути те-же таблицы, но с целочисленной нумерацией элементов вместо обращения по имени. Внутренная реализация таблиц оптимизирует такую ситуацию и обращение к элементам происходит быстрее.
Чтобы повторно не создавать запись для юнита, а извлечь текущую?
Поскольку я исхожу из того что в одном месте хранится вся привязанная к юниту информация, то да, извлекается старая запись если такая была.
Но не только - еще это нужно чтобы создать запись для юнита если её еще нет. И именно поэтому сыпался твой вариант без этой строки - по хендлу мы вынимали nil (он же null если по джасовски) если на хендл еще ничего не было записано и на этом все падало, естественно, т.к. nil это ни разу не таблица. У меня без этой строки работало потому как у меня в библиотеке переопределена операция получения значения из таблицы HandleData - либа сама создает новые таблицы-записи если на хендл еще ничего не записано.
Рекомендую освоить xpcall для отладки, он принимает два аргумента - функцию которую надо выполнить, можно анонимную и функцию в которую будет передана ошибка если что-то пойдет не так при выполнении функции из первого аргумента. В моем первом комменте много вызовов xpcall, посмотри на примере.
Bergi_Bear, смотри выше - я в последнем комменте дописал "минимальный" вариант замены моих библиотек, тебе не хватает одной важной строки при работе с данными
if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
А еще у меня там местами xpcall отладочный висит, там где я ловил косяки и не убрал его потом, без него код чуть проще становится, он нужен только для отлова ошибок и в моем случае вывода их на экран.
Это печально, новые костыли, а я уже обрадовался, что ничего обнулять и чистить не нужно =(
Не нужно обнулять локалки в функциях и все что ограниченного срока жизни, но всякие системы хранения данных они же либо глобальные либо в "локальном" скопе всей карты и, соответственно, живут пока загружена карта - там вручную подчищать надо, естественно. Можно делать это полу-автоматически как у меня, можно вручную когда становится известно что данные уже не нужны. Можно было бы использовать таблицу со слабыми ключами, но тогда возникает вопрос что использовать в качестве ключей - можно прямо на самого юнита вешать, чтобы когда игра его удалит из памяти, таблица почистилась сборщиком мусора, но нет гарантии что это будет работать и не будет вызывать десинки т.к. сборщику мусора синхронизация не указ. Bergi_Bear:
но мне можно себе такой вариант забрать или надо ещё что-то дополнительно записать?
У меня библиотека отгорожена только ради того чтобы автоматизировать сборку мусора и создание новых таблиц при обращении по хендлу на который ничего нет.
Минимальный вариант, наверно, такой:
local HandleData = {} -- я использую локальные переменные в скопе карты, при этом важен порядок но доступ к ним идет чуть быстрее чем в глобальном скопе
function ForceUnit (u,a,d,s,flag)
local data = HandleData[GetHandleId(u)]
if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
data.a = a
data.d = d
data.s = s
data.flag = flag
GroupAddUnit(gforce, u)
end
-- --------------
ForGroup(gforce, function()
local u=GetEnumUnit()
local h=GetHandleId(u)
local data = HandleData[h]
local a=data.a
local d=data.d
local s=data.s
local flag=data.flag
...
end
На примере твоего кода - в моем варианте работа с данными будет выглядеть примерно так:
function ForceUnit (u,a,d,s,flag)
local data = HandleData[GetHandleId(u)]
data.a = a
data.d = d
data.s = s
data.flag = flag
GroupAddUnit(gforce, u)
end
-- --------------
ForGroup(gforce, function()
local u=GetEnumUnit()
local h=GetHandleId(u)
local data = HandleData[h]
local a=data.a
local d=data.d
local s=data.s
local flag=data.flag
...
end
Насчет удобства я пока не могу оценить ибо вообще ничего не понимаю, что происходит, но очищать ничего не надо? юнит умер, его записи остались в таблице, сборщик мусора все сделает?
Сборщик сам не настолько крут, поэтому у меня там есть функции Purge и Reset для сноса данных из таблицы, а также целая либа CleanUp для перехвата смертей и удаления юнита, на которую либа хранения данных вешает свой хук в котором и подчищаются данные для таких юнитов.
Bergi_Bear, у тебя по таблице под каждое значение, а в моем варианте одна таблица в которой внутри по таблице на каждый хендл на который что-то прикручено. Мой вариант удобней в использовании т.к. все что связано с одним юнитом собрано в одном месте.
Два варианта
1 - массив-таблица с индексами по хендлу и еще одной таблицей в качестве значений
2 - передача переменных через кложуры (не твой случай ведь тебе на юнита данные вязать надо т.к. таймер один на всех)
Оба способа есть в примере ниже, если сможеш разобраться в том что там происходит.
немного моего сырого кода из тестовой карты
--========== Common Utils start ====================
local function RAW(id) --есть более эффективный вариант, но пока и так сойдет
return id:byte(1) * 0x1000000 +
id:byte(2) * 0x10000 +
id:byte(3) * 0x100 +
id:byte(4)
end
local function ehandler( err )
print( "ERROR:", err )
end
--========== Common Utils end ====================
--========= Libraries start ==============
local Libraries = {libs = {}}
function Libraries:Init()
print("Libraries Init")
for id,lib in ipairs(self.libs) do
if (lib.Init) then
print("Pre Init "..lib.name)
-- lib:Init()
xpcall(function() lib:Init() end,ehandler)
print("Post Init "..lib.name)
else
print("Skip Init "..lib.name)
end
end
for id,lib in ipairs(self.libs) do
if (lib.Final) then
print("Pre Final "..lib.name)
-- lib:Final()
xpcall(function() lib:Final() end,ehandler)
print("Post Final "..lib.name)
else
print("Skip Final "..lib.name)
end
end
end
function Libraries:new(name,dep)
local lib = {}
lib.name = name
lib.dep = dep
table.insert( self.libs, lib )
self[name] = lib
return lib
end
--========= Libraries end ==============
--============ CustomDataLibrary start ============
local DataStorage = Libraries:new("DataStorage",{})
function DataStorage:Reset(h)
print("data reset "..h)
self[x] = {}
return self[x]
end
function DataStorage:Purge(h)
print("data purge "..h)
self[h] = nil
end
function DataStorage:new (o)
o = o or {}
o.Reset = DataStorage.Reset
o.Purge = DataStorage.Purge
setmetatable(o, DataStorage)
DataStorage.__index = function (table, key) table[key] = {} return table[key] end
return o
end
local HandleData
local ProtoData
function DataStorage:Init()
HandleData = DataStorage:new()
ProtoData = DataStorage:new()
end
function DataStorage:Final()
local clean = Libraries.CleanUp
table.insert( clean.DeathHandlers,function(u,h) HandleData:Purge(h) end)
table.insert( clean.RemoveHandlers,function(u,h) HandleData:Purge(h) end)
end
--============ CustomDataLibrary end ==============
--============ CleanUpLib start ===============
local CleanUp = Libraries:new("CleanUp",{})
CleanUp.DeathHandlers={}
CleanUp.RemoveHandlers={}
CleanUp.RemoveUnitBase=nil
function CleanUp.OnDeath()
local unit u = GetTriggerUnit()
local id = GetHandleId(u)
for n,h in ipairs(CleanUp.DeathHandlers) do h(u,id) end
end
function CleanUp.OnRemove(u)
local id = GetHandleId(u)
for n,h in ipairs(CleanUp.RemoveHandlers) do h(u,id) end
return CleanUp.RemoveUnitBase(u)
end
function CleanUp:Init()
local trig = CreateTrigger()
TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_DEATH )
TriggerAddAction( trig, CleanUp.OnDeath )
self.RemoveUnitBase=RemoveUnit
RemoveUnit = self.OnRemove
end
function CleanUp:Final()
table.insert( self.DeathHandlers,function(u,h) print("Final Unit Death Handler "..GetUnitName(u)) end)
table.insert( self.RemoveHandlers,function(u,h) print("Final Unit Remove Handler "..GetUnitName(u)) end)
end
--============ CleanUpLib end ===============
--============ TestLib start ===============
local TestLib = Libraries:new("TestLib",{})
function TestLib.TestGroup(g)
print("group test start")
xpcall(function()
local a = 42
local i = 0
ForGroup(g, function()
xpcall(function()
local u = GetEnumUnit()
print('i='..i..' a='..a.." "..GetHandleId(u).." "..GetUnitName(u))
i = i +1
end,ehandler)
end)
end,ehandler)
print("group test end")
end
TestLib.TimerCounter=0
function TestLib.TestTimers()
local t1 = CreateTimer()
local i = 0
local a = 42
local x = TestLib.TimerCounter
print('start timer '..x)
TimerStart(t1, 1, true, function()
print('timer '..x..' tick '..i..' a='..a)
i = i +1
end)
TestLib.TimerCounter = TestLib.TimerCounter+1
end
function TestLib.TestUnit(u)
print("unit test start")
print(u)
local h = GetHandleId(u)
print("handle "..h)
local data = HandleData[h]
if (data.num==nil)then
data.num=0
end
data.num = data.num+1
BlzSetUnitRealField(u, UNIT_RF_DEFENSE, data.num)
print("unit test end")
end
--============ TestLib end ===============
О как меня переклинило то - был уверен что видел в нативках такую возможность, а вот фиг, нет такого, только к виджету прикрутить можно, но виджеты это юниты, разрушаемый декор и предметы.
» WarCraft 3 / Официально стал доступен PTR 1.31
Возможно идея не самая свежая - тут или на хайве кто-то мог уже это придумать раньше и лучше меня.
» WarCraft 3 / Самый производительный Bullet Hell
Даже систему модулей/библиотек в итоге себе для этого пилить начал, хотя изначально не собирался сильно этим заморачиваться.
» WarCraft 3 / Самый производительный Bullet Hell
» WarCraft 3 / Призыв юнитов со способность "Перерождение"
» WarCraft 3 / Проблема с защитой карты
» WarCraft 3 / Проблема с защитой карты
» WarCraft 3 / Проблема с защитой карты
» WarCraft 3 / Оператор конкатенации в макросах
» WarCraft 3 / Оператор конкатенации в макросах
» WarCraft 3 / Проблема с заменой шахт
Соответственно, в x y при текущей реализации всегда будут координаты последнего рудника, а не того который был удален...
Логичней было бы не отдавать наружу из перебора координаты вобще, а использовать их только внутри перебора, а потом отдельно брать координаты рудника перед удалением в другие локалки.
» WarCraft 3 / Призыв юнитов со способность "Перерождение"
» WarCraft 3 / Изменение огненного столба
Ред. prog
» WarCraft 3 / Переделка кода на Lua
Если данные связаные с системой - обнуляеш те переменные в таблице-записи которые использовал и которые не нужны в других местах. data.s = nil data.a=nil и так далее.
Если все данные на юнита (юнит умер или удаляется) - записываем nil по хендлу. HandleData[GetHandleId(u)] = nil.
У меня в либе для ручной чистки я вызвал бы HandleData:Purge(GetHandleId(u)), если бы не полагался на автоматическую чистку по какой-то причине, но там внутри пока все то-же самое присвоение nil.
data.somebulkdata = { a=42 b=42 c=42}
data.somebulkdata = nil
Ред. prog
» WarCraft 3 / Переделка кода на Lua
Но не только - еще это нужно чтобы создать запись для юнита если её еще нет. И именно поэтому сыпался твой вариант без этой строки - по хендлу мы вынимали nil (он же null если по джасовски) если на хендл еще ничего не было записано и на этом все падало, естественно, т.к. nil это ни разу не таблица. У меня без этой строки работало потому как у меня в библиотеке переопределена операция получения значения из таблицы HandleData - либа сама создает новые таблицы-записи если на хендл еще ничего не записано.
» WarCraft 3 / Переделка кода на Lua
» WarCraft 3 / Переделка кода на Lua
if (data==nil) then data = {} HandleData[GetHandleId(u)] = data end
Ред. prog
» WarCraft 3 / Переделка кода на Lua
Bergi_Bear:
Минимальный вариант, наверно, такой:
» WarCraft 3 / Переделка кода на Lua
» WarCraft 3 / Переделка кода на Lua
» WarCraft 3 / Отлов урона на 131 PTR
» WarCraft 3 / Отлов урона на 131 PTR
Ред. prog
» WarCraft 3 / Переделка кода на Lua
1 - массив-таблица с индексами по хендлу и еще одной таблицей в качестве значений
2 - передача переменных через кложуры (не твой случай ведь тебе на юнита данные вязать надо т.к. таймер один на всех)
» WarCraft 3 / Заклинание: Вихрь Иллюзий
» WarCraft 3 / Заклинание: Вихрь Иллюзий
» WarCraft 3 / Заклинание: Вихрь Иллюзий