Closed lep closed 12 months ago
my fault
i was expecting FlushGameCache
to be similar to FlushStoredInteger
to remove all save data types, so i didnt test again after changing it to FlushGameCache
what actually happen:
FlushStoredInteger
deletes saved data
FlushGameCache
deletes all saved data and makes the handle no longer valid
function onEvent takes nothing returns nothing
if HaveStoredInteger(GC, "test", "test") then
call FlushStoredInteger(GC, "test", "test")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "previous onEvent failed")
else
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "previous onEvent successed")
endif
call StoreInteger(InitGameCache("test.w3v"), "test", "test", 0)
set i = i + 1
if i - i / 2 * 2 == 1 then // i % 2 == 1
// crash this thread
call I2S(1 / 0)
endif
call FlushStoredInteger(GC, "test", "test")
endfunction
log:
successed
failed
successed
failed
successed
failed
// repeat N time
after change FlushStoredInteger
to FlushGameCache
successed
failed
successed
successed
// repeat N time successed
going to try if adding set GC = InitGameCache("test.w3v")
after FlushGameCache(GC)
produce same log as using FlushStoredInteger
edit:
it does produce same log as using FlushStoredInteger
test for modify gamecache file on 1.26a:
function init takes nothing returns nothing
local gamecache GC = InitGameCache("test.w3v")
call StoreString(GC, "test_missionKey", "test_valueKey", "test_value")
call SaveGameCache(GC)
// didnt test if this was necessary
call ReloadGameCachesFromDisk()
call BJDebugMsg(GetStoredString(InitGameCache("test.w3v"), "test_missionKey", "test_valueKey")) // test_value
// copy Warcraft III\Save\Profile*\Campaigns.w3v
// 1.32+ path (just guessing): Documents\Warcraft III\BattleNet\*\Campaigns\Classic\Campaigns.w3v
// didnt test this too
call ReloadGameCachesFromDisk()
// and this
set GC = InitGameCache("test.w3v")
call StoreString(GC, "test_missionKey", "test_valueKey", "test_value123")
// and this
call SaveGameCache(GC)
call BJDebugMsg(GetStoredString(InitGameCache("test.w3v"), "test_missionKey", "test_valueKey")) // test_value123
// restore copied Campaigns.w3v
call ReloadGameCachesFromDisk()
call BJDebugMsg(GetStoredString(InitGameCache("test.w3v"), "test_missionKey", "test_valueKey")) // test_value
endfunction
~~pros: no OP limit in Preloader script (wont exceed anyways unless very very huge bytecode generated and interpreter would exceed this OP limit even Preloader script didnt)~~
cons:
no longer possible for LAN reload
OP limit exceed in JHCR_Init_parse
would make partial reload (currently limited by bytecode reload limit (12 or 24 depend on patch) and probably wont cause this)
some features might possible to add if modify gamecache directly (if patch 1.33+ didnt cache gamecache
):
auto reload if detected modified (a global variable
in map script to track reloaded count and a gamecache data
to track how many time file generated and if detected global variable + 1 == gameacache data
preform reload)
warn the user if missing intermediate bytecode (global variable != gamecache data && global variable + 1 != gameacache data
(currently possible but useless on patch 1.33+ due to Preloader
caching))
by using TriggerSleepAction
we can reset executed OP count but it may let other trigger running partial reloaded bytecode and passing bad value to nfunction therefore making game crashing
by using ExecuteFunc
wont have such issue but it wont simple as using TriggerSleepAction
some code about using ExecuteFunc
to repeat a operation that usually would endup OP limit exceeded
// usual code
// OP limit = default
scope test initializer init
globals
integer i
endglobals
private function test takes nothing returns nothing
loop
set i = i + 1
endloop
endfunction
private function onEvent takes nothing returns nothing
set i = 0
call ExecuteFunc(SCOPE_PRIVATE + "test")
call BJDebugMsg(I2S(i)) // OP limit exceeded (forgot value but it was around 27000 ~ 30000)
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, GetLocalPlayer(), "", false)
call TriggerAddAction(t, function onEvent)
endfunction
endscope
// bypass OP limit
// OP limit = logically infinity but will cause stack overflow if nested too deep
scope test initializer init
globals
integer i
endglobals
private function test takes nothing returns nothing
loop
exitwhen i == 9999999 // 1 more digit war3 will stack overflow due to nested ExecuteFunc
set i = i + 1
if i - i / 9374 * 9374 == 0 then // on 300000 opcode limit thread OP limit exceeded at 9375th loop
call ExecuteFunc(SCOPE_PRIVATE + "test") // change to call AbilityId(SCOPE_PRIVATE + "test") to find the number
endif
endloop
endfunction
private function onEvent takes nothing returns nothing
set i = 0
call ExecuteFunc(SCOPE_PRIVATE + "test")
call BJDebugMsg(I2S(i)) // war3 hang ~1s then display 9999999
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, GetLocalPlayer(), "", false)
call TriggerAddAction(t, function onEvent)
endfunction
endscope
// bypass a little OP limit
// OP limit = default ^ 2
scope test initializer init
globals
integer i
endglobals
private function test takes nothing returns nothing
loop
set i = i + 1
exitwhen i == 999999999
endloop
endfunction
private function onEvent takes nothing returns nothing
set i = 0
loop
exitwhen i == 999999999
call ExecuteFunc(SCOPE_PRIVATE + "test")
endloop
call BJDebugMsg(I2S(i)) // war3 hang 3 min then OP limit exceeded (forgot to check the value)
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, GetLocalPlayer(), "", false)
call TriggerAddAction(t, function onEvent)
endfunction
endscope
// bypass a little more OP limit - just testing, the above one should already sufficient
// OP limit = default ^ 3
scope test initializer init
globals
integer i
endglobals
private function test takes nothing returns nothing
loop
exitwhen i == 999999999
set i = i + 1
endloop
endfunction
private function test1 takes nothing returns nothing
loop
exitwhen i == 999999999
set i = i + 1
call ExecuteFunc(SCOPE_PRIVATE + "test")
endloop
endfunction
private function onEvent takes nothing returns nothing
set i = 0
loop
exitwhen i == 999999999
call ExecuteFunc(SCOPE_PRIVATE + "test1")
endloop
call BJDebugMsg(I2S(i)) // war3 hang 5min then display 999999999
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, GetLocalPlayer(), "", false)
call TriggerAddAction(t, function onEvent)
endfunction
endscope
by default 1.26a OP limit is 300000
There are many interesting possibilities when using gamecache. The greatest point to me is to remove the dumb difference when compiling jhcr. But also that -- ignoring the op limit -- we can now load bigger chunks without worrying about running out of ability ids. I'm aware of all the stuff like directly modifying a GC file or techniques to circumvent the op limit. I say one step at a time. Some parts i wrote with re-entrance in mind (i think) but not the reloading itself. I don't think it would be too bad but i haven't taken a closer look yet. I propose that i merge this as is, that is just using GC to transfer stuff. I tested it under latest bnet patch and you tested it under 1.26a. That's good enough for me right now.
Based off of https://github.com/lep/jhcr/issues/6 I tested this under patch 1.33. But the
FlushGameCache
line doesn't work under 1.33. Does it really work under 1.26?