Jass Hot Code Reload

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497

Jass Hot Code Reload


A compiler to allow hot code reload in WarCraft 3. This means you can update
your map script and see the changes in a running game without restarting it.
Do note though that this is alpha software. Expect bugs and make backups.
This works without memhack.

Compiler usage


The compiler has two commands: init and update. The first command you execute
has to be the init command. The init command expects atleast three arguments:
Path to common.j, path to Blizzard.j and path to your maps war3map.j .
If your map was compiled by jasshelper you should use the --jasshelper flag
for the compiler to work correctly.

Code:
$ jhcr init common.j Blizzard.j war3map.j --jasshelper

The above usage will create a file jhcr_war3map.j. You should import this
as your maps new war3map.j.

Now you can make changes to your map. Once you've done this, get the new
war3map.j and call jhcr again.

Code:
jhcr update war3map.j --preload-path Path\To\CustomMapData --jasshelper

Again, if you've used jasshelper to compile your map, pass the --jasshelper flag.
The update command will create a file called JHCR.txt in the path you've specified.

As this tool operates only on the maps script and not on the map itself you
have to extract and insert the war3map.j yourself. Personally i use zezulas
mpqeditor via the command line.

For example this script could be used to create the map tmp.w3x from the orignal
map test.w3x:
Code:
cp test.w3x tmp.w3x
MPQEditor extract tmp.w3x war3map.j
jhcr init common.j Blizzard.j war3map.j --jasshelper
pjass common.j Blizzard.j jhcr_war3map.j
cp test.w3x tmp.w3x
MPQEditor add tmp.w3x jhcr_war3map.j war3map.j

Now you can start WarCraft 3 with the --loadfile argument to run tmp.w3x.

And this can be used to load the updates:
Code:
cp test.w3x tmp2.w3x
MPQEditor extract tmp2.w3x war3map.j
jhcr update war3map.j --jasshelper --preload-path 'C:\Users\lep\Documents\Warcraft III\CustomMapData\'

Map preparation


To load updates to the mapscript use call ExecuteFunc("JHCR_Init_parse") when appropiate. I use pressing escape.

  • reload
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Custom script: call ExecuteFunc("JHCR_Init_parse")

Blog posts


 

Attachments

  • jhcr-1.31.zip
    2.5 MB · Views: 38
  • jhcr-1.32.9.zip
    2.5 MB · Views: 110
Last edited:

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Sounds awesome. How does it work?
It's a multi step process. init takes the map script and injects the runtime and transforms the map script in such a way that the runtime can work with it. update looks which functions have changed since the last update or init and compiles those to a simple language. You can look at the generated code in human readable form by passing the --asm flag to update. This code is then loaded into the map via preload and interpreted by the runtime.
 

Chaosy

Tutorial Reviewer
Level 39
Joined
Jun 9, 2011
Messages
13,083
Hm, that does not tell me much.

My concern would be if it uses some sort of 'clever game mechanics' in order to function, meaning a bugfix from blizzard could render it useless.
This really seems mind blowing as I cannot imagine how to update I'd imagine that updating the map's script file does not make reload it into memory.
So since I cannot imagine how to do it, I kinda think there is some questionable method at play.

My best guess is that the program does some memory editing, but then I do not see the point of using custom scripts to use the system.
Needless to say I do not understand x)
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Hm, that does not tell me much.

My concern would be if it uses some sort of 'clever game mechanics' in order to function, meaning a bugfix from blizzard could render it useless.
This really seems mind blowing as I cannot imagine how to update I'd imagine that updating the map's script file does not make reload it into memory.
So since I cannot imagine how to do it, I kinda think there is some questionable method at play.

My best guess is that the program does some memory editing, but then I do not see the point of using custom scripts to use the system.
Needless to say I do not understand x)
You could assume that i know better what i wrote than you))
You can look at the generated map script and search for memory hack stuff. I even developed this on 1.30.4. Also i never said it updates the maps script file.
I would advise you to reread my original post, as it really explains how it's done.
But if you've got other questions i'm happy to answer.
Unless Blizzard does something stupid like limiting the amount of nested ifs or something this should just continue to work with any coming patch as it really just is plain jass.

Also please give code arrays Blizzard. That would reduce the output of this by so so much.
 

Dr Super Good

Spell Reviewer
Level 59
Joined
Jan 18, 2005
Messages
26,620
By the sounds of LeP's explanation what it does is compile JASS into some custom language, a special form of bytecode, which then gets fed into a realtime interpreter. The bytecode is read using preload like save/load codes are and the interpreter changes to using the new code when an update is applied. The underlying JASS and JASS bytecode is not modified.
 
Level 13
Joined
Nov 7, 2014
Messages
570
This is pretty awesome. It took me some time to set things up but I am pretty sure that hot reloading is faster than exiting the game, rebuilding the map and starting the game again.

I am not sure how many xs of overhead it is to run a bytecode vm (Lep) in a slow bytecode vm (Jass) but... hey it works!

The setup I ended up with (more or less):
Code:
// init
clijasshelper.exe --scriptonly  --debug common.j blizzard.j main.j war3map.j
jhcr.exe init common.j blizzard.j war3map.j --jasshelper
clijasshelper.exe common.j blizzard.j jhcr_war3map.j test.w3x

// start map
"Warcraft III.exe" -window -loadfile test.w3x

// should we 'update' after starting the map?

loop
    // update
    clijasshelper.exe --scriptonly  --debug common.j blizzard.j main.j war3map.j
    jhcr.exe update war3map.j --jasshelper --preload-path "C:\Users\lep\Documents\Warcraft III\CustomMapData\

    // This step can get annoying... maybe an optional jhcr.exe flag can be set
    // to send an Esc key to Warcraft III.exe's window automatically?
    // I guess this would require some Windows shenanigans.
    //
    call ExecuteFunc("JHCR_Init_parse") // e.g by pressing the Esc key

    // make changes to main.j etc., then trigger an update
endloop
 
Last edited:

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
This is pretty awesome. It took me some time to set things up but I am pretty sure that hot reloading is faster than exiting the game, rebuilding the map and starting the game again.

I am not sure how many xs of overhead it is to run a bytecode vm (Lep) in a slow bytecode vm (Jass) but... hey it works!

The setup I ended up with (more or less):
Code:
// init
clijasshelper.exe --scriptonly  --debug common.j blizzard.j main.j war3map.j
jhcr.exe init common.j blizzard.j war3map.j --jasshelper
clijasshelper.exe common.j blizzard.j jhcr_war3map.j test.w3x

// start map
"Warcraft III.exe" -window -loadfile test.w3x

// should we 'update' after starting the map?

loop
    // update
    clijasshelper.exe --scriptonly  --debug common.j blizzard.j main.j war3map.j
    jhcr.exe update war3map.j --jasshelper --preload-path "C:\Users\lep\Documents\Warcraft III\CustomMapData\

    // This step can get annoying... maybe an optional jhcr.exe flag can be set
    // to send an Esc key to Warcraft III.exe's window automatically?
    // I guess this would require some Windows shenanigans.
    //
    call ExecuteFunc("JHCR_Init_parse") // e.g by pressing the Esc key

    // make changes to main.j etc., then trigger an update
endloop
Speed seems OK to me; in my albeit small tests i reloaded a timer function which was executed 64 times a second. i also designed everything in such a way that it would be rather fast to execute (ofc there is always room to improve).
No need to directly update after init. in fact that would probably result in empty bytecode.
just run update when you changed something in your code.

also i don't plan on adding automatic reload on the warcraft side. you could probably use autohotkey or something after calling jhcr update. but i dont know about that stuff.
 
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
570
It seems that adding a global variable (or modifing its initial value) is not reflected after an "update".
On the other hand, it seems that new functions can be declared (functions that were not present at "init" time).

It's kind of hard to tell but I don't think integers come before the other types in the dispatch of the "Set/GetGlobal/Array" instructions. I think integers are the most common type.

PS: The productivity boost from this tool, scriptwise, is nuts! Most importantly, I think it brings back the fun of scripting!
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
It seems that adding a global variable (or modifing its initial value) is not reflected after an "update".
On the other hand, it seems that new functions can be declared (functions that were not present at "init" time).

It's kind of hard to tell but I don't think integers come before the other types in the dispatch of the "Set/GetGlobal/Array" instructions. I think integers are the most common type.

PS: The productivity boost from this tool, scriptwise, is nuts! Most importantly, I think it brings back the fun of scripting!
Yes, the current limitations are that you can define and use new globals but they're initialized to 0/0.0/""/null. Also you can define up to 100 new functions and use them except in ExecuteFunc. I plan on fixing both.

Also the reason i wrote this tool is exactly the fast iteration process when developing; always restarting maps costs so much time. And the first successfull reload was quite magical.
I don't think changing integers id to come first would result in any meaningfull performance boost but i will keep it in mind.
 
Level 13
Joined
Nov 7, 2014
Messages
570
I think I found a few bugs.

Single quote integers:
JASS:
private function on_reload takes nothing returns nothing
    call writeln("Reload OK")
    // call UnitAddItemById(g_unit, 'bspd') // not ok
    call UnitAddItemById(g_unit, 0x62737064) // ok
endfunction

Negation:
JASS:
private function on_reload takes nothing returns nothing
    local integer a
    local integer b
    call writeln("Reload OK")

    set a = 0
    set b = -48 // 0 - 48 seems ok

    call writeln("a: " + I2S(a))
    set a = a - b
    call writeln("a: " + I2S(a))
    set a = a - b
    call writeln("a: " + I2S(a))
endfunction

Button stops working after an update (I have no idea what goes wrong):
JASS:
private function draw_loop takes nothing returns nothing
    local integer x
    local integer y
    local integer w
    local integer h
    local Bd_Mouse m
    local Bd_Mbtn lmb

    set m = Bd_Mouse(0)
    set lmb = m.lmb

    call bd_draw_begin(0)
    call bdw_auto_wid_base(1000)

    set w = 256
    set h = 64

    set x = 0 - 1024
    set y = 768

    if bdw_text_button("click me", x, y, w, h, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, false, lmb) then
        call writeln("A") // change "A" to "B" then trigger an "update"
    endif

    call bd_draw_end()
endfunction

private function on_start takes nothing returns nothing
    call TimerStart(CreateTimer(), 1.0/10.0, true, function draw_loop)
endfunction


PS: Sending the Esc key to Warcraft III's window automatically requires some Windows minutia but doesn't seem that bad:
Code:
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")

using u16 = unsigned short;
using u32 = unsigned int;
using s32 = int;
using bool32 = s32;
// we should probably static assert that sizeof(X) is what we wanted

using HWND = void*;

struct MOUSEINPUT {
    s32 dx;
    s32 dy;
    u32 mouseData;
    u32 dwFlags;
    u32 time;
    void* dwExtraInfo;
};

struct KEYBDINPUT {
    u16 wVk;
    u16 wScan;
    u32 dwFlags;
    u32 time;
    void* dwExtraInfo;
};

struct HARDWAREINPUT {
    u32 uMsg;
    u16 wParamL;
    u16 wParamH;
};

struct INPUT {
    u32 type;
    union {
        MOUSEINPUT mi;
        KEYBDINPUT ki;
        HARDWAREINPUT hi;
    };
};

#define cast(T, e) (T)(e)
#define WINAPI __stdcall

extern "C" {
    void exit(s32);
    s32 printf(char const* fmt, ... );

    HWND WINAPI FindWindowA(char const* class_name, char const* win_name);
    bool32 WINAPI SetForegroundWindow(HWND w);
    u32 WINAPI SendInput(u32, INPUT*, s32 key);
} // extern "C"

void
send_key(s32 k) {
    enum {
        INPUT_KEYBOARD = 1,
        KEYEVENTF_KEYUP = 2,
    };

    INPUT input = {};
    input.type = INPUT_KEYBOARD;
    input.ki.wVk = cast(u16, k);

    // press the key
    input.ki.dwFlags = 0;
    SendInput(1, &input, sizeof(INPUT));

    // release the key
    input.ki.dwFlags = KEYEVENTF_KEYUP;
    SendInput(1, &input, sizeof(INPUT));
}

s32
main(s32 args_len, char const** args) {
    enum { VK_ESCAPE = 0x1B, };

    // char const* class_name = "ConsoleWindowClass";
    // const char* win_name = "C:\\Windows\\system32\\cmd.exe";

    // char const* class_name = "Warcraft III"; // before patch 1.30?
    char const* class_name = "OsWindow"; // after patch 1.30?
    const char* win_name = "Warcraft III";

    if (args_len > 1) { class_name = args[1]; }
    if (args_len > 2) { win_name = args[2]; }

    HWND w = FindWindowA(class_name, win_name);
    if (w == 0) {
        printf("error: could not find Warcraft III window\n");
        exit(1);
    }

    SetForegroundWindow(w);
    send_key(VK_ESCAPE);

    return 0;
}
 

Attachments

  • jhcr-bd.zip
    13.2 MB · Views: 160

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
I think I found a few bugs.

Single quote integers:
JASS:
private function on_reload takes nothing returns nothing
    call writeln("Reload OK")
    // call UnitAddItemById(g_unit, 'bspd') // not ok
    call UnitAddItemById(g_unit, 0x62737064) // ok
endfunction

Negation:
JASS:
private function on_reload takes nothing returns nothing
    local integer a
    local integer b
    call writeln("Reload OK")

    set a = 0
    set b = -48 // 0 - 48 seems ok

    call writeln("a: " + I2S(a))
    set a = a - b
    call writeln("a: " + I2S(a))
    set a = a - b
    call writeln("a: " + I2S(a))
endfunction

Button stops working after an update (I have no idea what goes wrong):
JASS:
private function draw_loop takes nothing returns nothing
    local integer x
    local integer y
    local integer w
    local integer h
    local Bd_Mouse m
    local Bd_Mbtn lmb

    set m = Bd_Mouse(0)
    set lmb = m.lmb

    call bd_draw_begin(0)
    call bdw_auto_wid_base(1000)

    set w = 256
    set h = 64

    set x = 0 - 1024
    set y = 768

    if bdw_text_button("click me", x, y, w, h, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, false, lmb) then
        call writeln("A") // change "A" to "B" then trigger an "update"
    endif

    call bd_draw_end()
endfunction

private function on_start takes nothing returns nothing
    call TimerStart(CreateTimer(), 1.0/10.0, true, function draw_loop)
endfunction


PS: Sending the Esc key to Warcraft III's window automatically requires some Windows minutia but doesn't seem that bad:
Code:
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")

using u16 = unsigned short;
using u32 = unsigned int;
using s32 = int;
using bool32 = s32;
// we should probably static assert that sizeof(X) is what we wanted

using HWND = void*;

struct MOUSEINPUT {
    s32 dx;
    s32 dy;
    u32 mouseData;
    u32 dwFlags;
    u32 time;
    void* dwExtraInfo;
};

struct KEYBDINPUT {
    u16 wVk;
    u16 wScan;
    u32 dwFlags;
    u32 time;
    void* dwExtraInfo;
};

struct HARDWAREINPUT {
    u32 uMsg;
    u16 wParamL;
    u16 wParamH;
};

struct INPUT {
    u32 type;
    union {
        MOUSEINPUT mi;
        KEYBDINPUT ki;
        HARDWAREINPUT hi;
    };
};

#define cast(T, e) (T)(e)
#define WINAPI __stdcall

extern "C" {
    void exit(s32);
    s32 printf(char const* fmt, ... );

    HWND WINAPI FindWindowA(char const* class_name, char const* win_name);
    bool32 WINAPI SetForegroundWindow(HWND w);
    u32 WINAPI SendInput(u32, INPUT*, s32 key);
} // extern "C"

void
send_key(s32 k) {
    enum {
        INPUT_KEYBOARD = 1,
        KEYEVENTF_KEYUP = 2,
    };

    INPUT input = {};
    input.type = INPUT_KEYBOARD;
    input.ki.wVk = cast(u16, k);

    // press the key
    input.ki.dwFlags = 0;
    SendInput(1, &input, sizeof(INPUT));

    // release the key
    input.ki.dwFlags = KEYEVENTF_KEYUP;
    SendInput(1, &input, sizeof(INPUT));
}

s32
main(s32 args_len, char const** args) {
    enum { VK_ESCAPE = 0x1B, };

    // char const* class_name = "ConsoleWindowClass";
    // const char* win_name = "C:\\Windows\\system32\\cmd.exe";

    // char const* class_name = "Warcraft III"; // before patch 1.30?
    char const* class_name = "OsWindow"; // after patch 1.30?
    const char* win_name = "Warcraft III";

    if (args_len > 1) { class_name = args[1]; }
    if (args_len > 2) { win_name = args[2]; }

    HWND w = FindWindowA(class_name, win_name);
    if (w == 0) {
        printf("error: could not find Warcraft III window\n");
        exit(1);
    }

    SetForegroundWindow(w);
    send_key(VK_ESCAPE);

    return 0;
}
Really appreciate your feedback. The first two bugs are already fixed. Uploaded to first post.
For the third, could you upload a map which i can just drop into WorldEditor and save there without jhcr already injected? Or the exact clijasshelper commands i guess.
 
Last edited:

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Sorry, can't reproduce. Works for me (after adding init and reload trigger).
So i don't really know how to progress. Try downloading the new version, although from what i can see your code doesn't use the former bugged rawcodes and negate. Try printing a message before calling ExecuteFunc("JHCR_Init_parse") to make sure it really is called. Have you maybe made other changes than just printing something different?
 
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
570
Hm. I added some debug printing in bdw_text_button. Before an update the value of g_Wid stays 1001 (as it should be). After an update the value starts to be incremented by 1 on each draw_loop call.

JASS:
function bdw_text_button takes string text, integer x, integer y, integer w, integer h, integer br, integer bg, integer bb, integer ba, integer tr, integer tg, integer tb, integer ta,  boolean eye_candy, Bd_Mbtn btn returns boolean
    local integer p = bd_p
    local integer wi = next_auto_wid(p)
    local boolean clicked = false
    local integer xx = x
    local integer yy = y
    local integer ww = w
    local integer hh = h
    local string s = ""

    if g_Wi == 0 then
        if btn.down and is_point_in_rect(btn.down_x, btn.down_y, x, y, w, h) then
            set g_Wi = wi
            call player_disable_drag_select(p)
        endif
    endif

    if btn.up then
        set s = "U"
    else
        set s = "D"
    endif
    call writeln("p: " + I2S(p) + ", wi: " + I2S(g_Wi) + ", g_Wi: " + I2S(g_Wi) + ", btn: " + s + ", g_Wid: " + I2S(g_Wid))

    if wi == g_Wi then
        if btn.up then
            set g_Wi = 0
            call player_enable_drag_select(p)
            if is_point_in_rect(btn.up_x, btn.up_y, x, y, w, h) then
                set clicked = true
            endif
        endif

        if eye_candy then
            set xx = R2I(xx + 0.05*ww)
            set yy = R2I(yy + 0.05*hh)
            set ww = R2I(ww - 0.1*ww)
            set hh = R2I(hh - 0.1*hh)
        endif
    endif

    call bd_rect(xx, yy, ww, hh, 2, br, bg, bb, ba)
    call bd_text_centered(text, xx, yy, ww, hh, tr, tg, tb, ta)

    return clicked
endfunction

This seems like a bug as well
JASS:
private function on_start takes nothing returns nothing
    local code fn = function draw_loop
    call TimerStart(CreateTimer(), 1.0/24.0, true, fn)
endfunction

function JHCR_lep__on_start takes nothing returns nothing
    local integer fn= function lep__draw_loop
    call TimerStart(CreateTimer(), ( 1.0 / 24.0 ), true, JHCR_Wrap_i2code(fn))
endfunction

Edit:

Adding the two dummy lines in this function seems to fix the problem =)?
JASS:
function bdw_auto_wid_base takes integer base returns nothing
    local integer no_inline_1
    local integer no_inline_2
    set g_Wid[bd_p] = base
endfunction
 
Last edited:

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Hm. I added some debug printing in bdw_text_button. Before an update the value of g_Wid stays 1001 (as it should be). After an update the value starts to be incremented by 1 on each draw_loop call.

JASS:
function bdw_text_button takes string text, integer x, integer y, integer w, integer h, integer br, integer bg, integer bb, integer ba, integer tr, integer tg, integer tb, integer ta,  boolean eye_candy, Bd_Mbtn btn returns boolean
    local integer p = bd_p
    local integer wi = next_auto_wid(p)
    local boolean clicked = false
    local integer xx = x
    local integer yy = y
    local integer ww = w
    local integer hh = h
    local string s = ""

    if g_Wi == 0 then
        if btn.down and is_point_in_rect(btn.down_x, btn.down_y, x, y, w, h) then
            set g_Wi = wi
            call player_disable_drag_select(p)
        endif
    endif

    if btn.up then
        set s = "U"
    else
        set s = "D"
    endif
    call writeln("p: " + I2S(p) + ", wi: " + I2S(g_Wi) + ", g_Wi: " + I2S(g_Wi) + ", btn: " + s + ", g_Wid: " + I2S(g_Wid))

    if wi == g_Wi then
        if btn.up then
            set g_Wi = 0
            call player_enable_drag_select(p)
            if is_point_in_rect(btn.up_x, btn.up_y, x, y, w, h) then
                set clicked = true
            endif
        endif

        if eye_candy then
            set xx = R2I(xx + 0.05*ww)
            set yy = R2I(yy + 0.05*hh)
            set ww = R2I(ww - 0.1*ww)
            set hh = R2I(hh - 0.1*hh)
        endif
    endif

    call bd_rect(xx, yy, ww, hh, 2, br, bg, bb, ba)
    call bd_text_centered(text, xx, yy, ww, hh, tr, tg, tb, ta)

    return clicked
endfunction

Sorry but g_wid keeps on printing 1001 after update on my side.
Something seems very different between our tests.

This seems like a bug as well
JASS:
private function on_start takes nothing returns nothing
    local code fn = function draw_loop
    call TimerStart(CreateTimer(), 1.0/24.0, true, fn)
endfunction

function JHCR_lep__on_start takes nothing returns nothing
    local integer fn= function lep__draw_loop
    call TimerStart(CreateTimer(), ( 1.0 / 24.0 ), true, JHCR_Wrap_i2code(fn))
endfunction

What do you consider buggy about this?
 
Level 13
Joined
Nov 7, 2014
Messages
570
What do you consider buggy about this?
JASS:
// fn has type integer but the expression 'function lep__draw_loop' has type code
local integer fn= function lep__draw_loop

Something seems very different between our tests.
Don't know if you read my edit above, but it seems to work now (after adding some dummy lines):
JASS:
function bdw_auto_wid_base takes integer base returns nothing
    local integer no_inline_1
    local integer no_inline_2
    set g_Wid[bd_p] = base
endfunction
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
JASS:
// fn has type integer but the expression 'function lep__draw_loop' has type code
local integer fn= function lep__draw_loop

That is intentional. Blizzard doesn't want to give us code arrays so i have to adapt.

Don't know if you read my edit above, but it seems to work now (after adding some dummy lines):
JASS:
function bdw_auto_wid_base takes integer base returns nothing
    local integer no_inline_1
    local integer no_inline_2
    set g_Wid[bd_p] = base
endfunction
I've read it but for me it works without that. Maybe paste the output of your update command with --asm flag.
 
Level 13
Joined
Nov 7, 2014
Messages
570
with dummy lines
Code:
Updating function lep__draw_loop
Writing bytecode
Writing state file
Ok.
fun 2260 lep__draw_loop
lit integer -2 1 0
bind integer 1 -2
call nothing -1 2235 bd_draw_begin
lit integer -4 4 1000
bind integer 1 -4
call nothing -3 2246 bdw_auto_wid_base
lit integer -5 1 0
set integer 5 -5
gga integer -6 20 5
set integer 6 -6
lit integer -7 3 320
set integer 3 -7
lit integer -8 2 64
set integer 4 -8
lit integer -9 1 0
set integer 1 -9
lit integer -10 1 0
set integer 2 -10
lit string -12 8 click me
lit integer -13 1 0
lit integer -14 1 0
lit integer -15 1 0
lit integer -16 3 255
lit integer -17 3 255
lit integer -18 3 255
lit integer -19 3 255
lit integer -20 3 255
lit boolean -21 5 False
bind string 1 -12
bind integer 2 1
bind integer 3 2
bind integer 4 3
bind integer 5 4
bind integer 6 -13
bind integer 7 -14
bind integer 8 -15
bind integer 9 -16
bind integer 10 -17
bind integer 11 -18
bind integer 12 -19
bind integer 13 -20
bind boolean 14 -21
bind integer 15 6
call boolean -11 2248 bdw_text_button
jmpt -11 2
jmp 3
label 2
lit string -23 1 A
bind string 1 -23
call nothing -22 1275 BJDebugMsg
label 3
call nothing -24 2236 bd_draw_end
ret nothing

without dummy lines
Code:
Updating function lep__draw_loop
Writing bytecode
Writing state file
Ok.
fun 2260 lep__draw_loop
lit integer -2 1 0
bind integer 1 -2
call nothing -1 2235 bd_draw_begin
gg integer -3 274
lit integer -4 4 1000
sga integer 7 -3 -4
lit integer -5 1 0
set integer 5 -5
gga integer -6 20 5
set integer 6 -6
lit integer -7 3 320
set integer 3 -7
lit integer -8 2 64
set integer 4 -8
lit integer -9 1 0
set integer 1 -9
lit integer -10 1 0
set integer 2 -10
lit string -12 8 click me
lit integer -13 1 0
lit integer -14 1 0
lit integer -15 1 0
lit integer -16 3 255
lit integer -17 3 255
lit integer -18 3 255
lit integer -19 3 255
lit integer -20 3 255
lit boolean -21 5 False
bind string 1 -12
bind integer 2 1
bind integer 3 2
bind integer 4 3
bind integer 5 4
bind integer 6 -13
bind integer 7 -14
bind integer 8 -15
bind integer 9 -16
bind integer 10 -17
bind integer 11 -18
bind integer 12 -19
bind integer 13 -20
bind boolean 14 -21
bind integer 15 6
call boolean -11 2248 bdw_text_button
jmpt -11 2
jmp 3
label 2
lit string -23 1 A
bind string 1 -23
call nothing -22 1275 BJDebugMsg
label 3
call nothing -24 2236 bd_draw_end
ret nothing

I need to leave soon so won't be able to reply anymore.
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
I need to leave soon so won't be able to reply anymore.
I get wildly different bytecode which makes me assume that you are not testing the same code as i am.
So that would be nice to change.
 
Level 13
Joined
Nov 7, 2014
Messages
570
I've attached the exact main.j I used for testing (it only depends on the library files from before).


At map init time the draw_loop function looks like this:
JASS:
private function draw_loop takes nothing returns nothing
    local integer a
    local integer x
    local integer y
    local integer w
    local integer h
    local integer dx
    local integer dy
    local integer font_size
    local real scale
    local Bd_Mouse m
    local Bd_Mbtn lmb
    local Bd_Mbtn mmb
    local Bd_Mbtn rmb
    local boolean any_changed

    set m = Bd_Mouse(0)
    set lmb = m.lmb
    set mmb = m.mmb
    set rmb = m.rmb

    set font_size = 35

    call bd_draw_begin(0)
    call bdw_auto_wid_base(1000)

    set w = 256
    set h = 64

    set x = -1024
    set y = 768
    set dx = 48
    set dy = -128

    set any_changed = false

    // a lot of commented out lines
    // a lot of commented out lines
    // a lot of commented out lines
    // ...

    call bd_draw_end()
endfunction

I load the map right after an init (the draw_loop function at this point looks like the above).

Without changing anything I trigger an update and I get:
Code:
Updating function entrypoint__on_chat_input
Updating function ascii__chr_init
Updating function ascii__ord_init
Updating function baddraw__img_list_clear
Updating function baddraw__cache_string_bytes
Updating function bdw_slider
Updating function bdw_vscrollbar
Updating function unitsliderstuff__delayed_init
Updating function main
Writing bytecode
// many many lines of asm

I trigger an update (again without changing anything) and I get:
Code:
Writing bytecode
Writing state file
Ok.
// no asm

Now I uncomment the button and trigger an update:
JASS:
    if bdw_text_button("click me", x, y, w, h, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, false, lmb) then
        call writeln("foo")
    endif

I get:
Code:
Updating function unitsliderstuff__draw_loop
Writing bytecode
Writing state file
Ok.
fun 2271 unitsliderstuff__draw_loop
lit integer -1 1 0
set integer 10 -1
gga integer -2 20 10
set integer 11 -2
gga integer -3 21 10
set integer 12 -3
gga integer -4 22 10
set integer 13 -4
lit integer -5 2 35
set integer 8 -5
lit integer -7 1 0
bind integer 1 -7
call nothing -6 2245 bd_draw_begin
lit integer -9 4 1000
bind integer 1 -9
call nothing -8 2256 bdw_auto_wid_base
lit integer -10 3 256
set integer 4 -10
lit integer -11 2 64
set integer 5 -11
lit integer -13 4 1024
neg integer -12 -13
set integer 2 -12
lit integer -14 3 768
set integer 3 -14
lit integer -15 2 48
set integer 6 -15
lit integer -17 3 128
neg integer -16 -17
set integer 7 -16
lit boolean -18 5 False
set boolean 14 -18
lit string -20 8 click me
lit integer -21 1 0
lit integer -22 1 0
lit integer -23 1 0
lit integer -24 3 255
lit integer -25 3 255
lit integer -26 3 255
lit integer -27 3 255
lit integer -28 3 255
lit boolean -29 5 False
bind string 1 -20
bind integer 2 2
bind integer 3 3
bind integer 4 4
bind integer 5 5
bind integer 6 -21
bind integer 7 -22
bind integer 8 -23
bind integer 9 -24
bind integer 10 -25
bind integer 11 -26
bind integer 12 -27
bind integer 13 -28
bind boolean 14 -29
bind integer 15 11
call boolean -19 2258 bdw_text_button
jmpt -19 2
jmp 3
label 2
lit string -31 3 foo
bind string 1 -31
call nothing -30 2269 unitsliderstuff__writeln
label 3
call nothing -32 2246 bd_draw_end
ret nothing
In-game, I can see the button and I can click it (it works, as expected).

Now I comment out the button and uncomment the first 4 sliders then trigger an update:
JASS:
    // if bdw_text_button("click me", x, y, w, h, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, false, lmb) then
    //     call writeln("foo")
    // endif


    set y = y + dy
    call bd_text("R", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(1, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0xFF, 0x00, 0x00, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("G", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(2, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0x00, 0xFF, 0x00, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("B", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(3, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0x00, 0x00, 0xFF, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("A", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(4, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0xFF, 0xFF, 0xFF, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    if any_changed then
        call SetUnitVertexColor(g_my_unit, bdw_slider_val(1), bdw_slider_val(2), bdw_slider_val(3), bdw_slider_val(4))
    endif

I get:
Code:
Updating function unitsliderstuff__draw_loop
Writing bytecode
Writing state file
Ok.
fun 2271 unitsliderstuff__draw_loop
lit integer -1 1 0
set integer 10 -1
gga integer -2 20 10
set integer 11 -2
gga integer -3 21 10
set integer 12 -3
gga integer -4 22 10
set integer 13 -4
lit integer -5 2 35
set integer 8 -5
lit integer -7 1 0
bind integer 1 -7
call nothing -6 2245 bd_draw_begin
lit integer -9 4 1000
bind integer 1 -9
call nothing -8 2256 bdw_auto_wid_base
lit integer -10 3 256
set integer 4 -10
lit integer -11 2 64
set integer 5 -11
lit integer -13 4 1024
neg integer -12 -13
set integer 2 -12
lit integer -14 3 768
set integer 3 -14
lit integer -15 2 48
set integer 6 -15
lit integer -17 3 128
neg integer -16 -17
set integer 7 -16
lit boolean -18 5 False
set boolean 14 -18
add integer -19 3 7
set integer 3 -19
lit string -21 1 R
sub integer -22 2 6
lit integer -23 3 255
lit integer -24 3 255
lit integer -25 3 255
lit integer -26 3 255
bind string 1 -21
bind integer 2 -22
bind integer 3 3
bind integer 4 8
bind integer 5 -23
bind integer 6 -24
bind integer 7 -25
bind integer 8 -26
call nothing -20 2242 bd_text
lit integer -28 1 1
lit integer -29 1 0
lit integer -30 3 255
lit integer -31 1 1
lit integer -32 3 512
lit integer -33 2 40
lit integer -34 1 0
lit integer -35 1 0
lit integer -36 1 0
lit integer -37 3 255
lit integer -38 2 30
lit integer -39 2 60
lit integer -40 3 255
lit integer -41 1 0
lit integer -42 1 0
lit integer -43 3 255
gga integer -44 23 10
bind integer 1 -28
bind integer 2 -29
bind integer 3 -30
bind integer 4 -31
bind integer 5 2
bind integer 6 3
bind integer 7 -32
bind integer 8 -33
bind integer 9 -34
bind integer 10 -35
bind integer 11 -36
bind integer 12 -37
bind integer 13 -38
bind integer 14 -39
bind integer 15 -40
bind integer 16 -41
bind integer 17 -42
bind integer 18 -43
bind integer 19 11
bind integer 20 -44
call boolean -27 2265 bdw_slider
jmpt -27 2
jmp 3
label 2
lit boolean -45 4 True
set boolean 14 -45
label 3
add integer -46 3 7
set integer 3 -46
lit string -48 1 G
sub integer -49 2 6
lit integer -50 3 255
lit integer -51 3 255
lit integer -52 3 255
lit integer -53 3 255
bind string 1 -48
bind integer 2 -49
bind integer 3 3
bind integer 4 8
bind integer 5 -50
bind integer 6 -51
bind integer 7 -52
bind integer 8 -53
call nothing -47 2242 bd_text
lit integer -55 1 2
lit integer -56 1 0
lit integer -57 3 255
lit integer -58 1 1
lit integer -59 3 512
lit integer -60 2 40
lit integer -61 1 0
lit integer -62 1 0
lit integer -63 1 0
lit integer -64 3 255
lit integer -65 2 30
lit integer -66 2 60
lit integer -67 1 0
lit integer -68 3 255
lit integer -69 1 0
lit integer -70 3 255
gga integer -71 23 10
bind integer 1 -55
bind integer 2 -56
bind integer 3 -57
bind integer 4 -58
bind integer 5 2
bind integer 6 3
bind integer 7 -59
bind integer 8 -60
bind integer 9 -61
bind integer 10 -62
bind integer 11 -63
bind integer 12 -64
bind integer 13 -65
bind integer 14 -66
bind integer 15 -67
bind integer 16 -68
bind integer 17 -69
bind integer 18 -70
bind integer 19 11
bind integer 20 -71
call boolean -54 2265 bdw_slider
jmpt -54 4
jmp 5
label 4
lit boolean -72 4 True
set boolean 14 -72
label 5
add integer -73 3 7
set integer 3 -73
lit string -75 1 B
sub integer -76 2 6
lit integer -77 3 255
lit integer -78 3 255
lit integer -79 3 255
lit integer -80 3 255
bind string 1 -75
bind integer 2 -76
bind integer 3 3
bind integer 4 8
bind integer 5 -77
bind integer 6 -78
bind integer 7 -79
bind integer 8 -80
call nothing -74 2242 bd_text
lit integer -82 1 3
lit integer -83 1 0
lit integer -84 3 255
lit integer -85 1 1
lit integer -86 3 512
lit integer -87 2 40
lit integer -88 1 0
lit integer -89 1 0
lit integer -90 1 0
lit integer -91 3 255
lit integer -92 2 30
lit integer -93 2 60
lit integer -94 1 0
lit integer -95 1 0
lit integer -96 3 255
lit integer -97 3 255
gga integer -98 23 10
bind integer 1 -82
bind integer 2 -83
bind integer 3 -84
bind integer 4 -85
bind integer 5 2
bind integer 6 3
bind integer 7 -86
bind integer 8 -87
bind integer 9 -88
bind integer 10 -89
bind integer 11 -90
bind integer 12 -91
bind integer 13 -92
bind integer 14 -93
bind integer 15 -94
bind integer 16 -95
bind integer 17 -96
bind integer 18 -97
bind integer 19 11
bind integer 20 -98
call boolean -81 2265 bdw_slider
jmpt -81 6
jmp 7
label 6
lit boolean -99 4 True
set boolean 14 -99
label 7
add integer -100 3 7
set integer 3 -100
lit string -102 1 A
sub integer -103 2 6
lit integer -104 3 255
lit integer -105 3 255
lit integer -106 3 255
lit integer -107 3 255
bind string 1 -102
bind integer 2 -103
bind integer 3 3
bind integer 4 8
bind integer 5 -104
bind integer 6 -105
bind integer 7 -106
bind integer 8 -107
call nothing -101 2242 bd_text
lit integer -109 1 4
lit integer -110 1 0
lit integer -111 3 255
lit integer -112 1 1
lit integer -113 3 512
lit integer -114 2 40
lit integer -115 1 0
lit integer -116 1 0
lit integer -117 1 0
lit integer -118 3 255
lit integer -119 2 30
lit integer -120 2 60
lit integer -121 3 255
lit integer -122 3 255
lit integer -123 3 255
lit integer -124 3 255
gga integer -125 23 10
bind integer 1 -109
bind integer 2 -110
bind integer 3 -111
bind integer 4 -112
bind integer 5 2
bind integer 6 3
bind integer 7 -113
bind integer 8 -114
bind integer 9 -115
bind integer 10 -116
bind integer 11 -117
bind integer 12 -118
bind integer 13 -119
bind integer 14 -120
bind integer 15 -121
bind integer 16 -122
bind integer 17 -123
bind integer 18 -124
bind integer 19 11
bind integer 20 -125
call boolean -108 2265 bdw_slider
jmpt -108 8
jmp 9
label 8
lit boolean -126 4 True
set boolean 14 -126
label 9
jmpt 14 10
jmp 11
label 10
gg unit -128 8
gg hashtable -130 5
gg integer -131 274
lit integer -132 1 1
bind hashtable 1 -130
bind integer 2 -131
bind integer 3 -132
call integer -129 772 LoadInteger
gg hashtable -134 5
gg integer -135 274
lit integer -136 1 2
bind hashtable 1 -134
bind integer 2 -135
bind integer 3 -136
call integer -133 772 LoadInteger
gg hashtable -138 5
gg integer -139 274
lit integer -140 1 3
bind hashtable 1 -138
bind integer 2 -139
bind integer 3 -140
call integer -137 772 LoadInteger
gg hashtable -142 5
gg integer -143 274
lit integer -144 1 4
bind hashtable 1 -142
bind integer 2 -143
bind integer 3 -144
call integer -141 772 LoadInteger
bind unit 1 -128
bind integer 2 -129
bind integer 3 -133
bind integer 4 -137
bind integer 5 -141
call nothing -127 441 SetUnitVertexColor
label 11
call nothing -145 2246 bd_draw_end
ret nothing
In-game, I can still see the button and click on it (I shouldn't because it's commented out) and none of the sliders are visible (but should be).

Now I uncomment the button and trigger an update:
JASS:
    if bdw_text_button("click me", x, y, w, h, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, false, lmb) then
        call writeln("foo")
    endif


    set y = y + dy
    call bd_text("R", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(1, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0xFF, 0x00, 0x00, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("G", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(2, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0x00, 0xFF, 0x00, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("B", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(3, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0x00, 0x00, 0xFF, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("A", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(4, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0xFF, 0xFF, 0xFF, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    if any_changed then
        call SetUnitVertexColor(g_my_unit, bdw_slider_val(1), bdw_slider_val(2), bdw_slider_val(3), bdw_slider_val(4))
    endif

I get:
Code:
Updating function unitsliderstuff__draw_loop
Writing bytecode
Writing state file
Ok.
fun 2271 unitsliderstuff__draw_loop
lit integer -1 1 0
set integer 10 -1
gga integer -2 20 10
set integer 11 -2
gga integer -3 21 10
set integer 12 -3
gga integer -4 22 10
set integer 13 -4
lit integer -5 2 35
set integer 8 -5
lit integer -7 1 0
bind integer 1 -7
call nothing -6 2245 bd_draw_begin
lit integer -9 4 1000
bind integer 1 -9
call nothing -8 2256 bdw_auto_wid_base
lit integer -10 3 256
set integer 4 -10
lit integer -11 2 64
set integer 5 -11
lit integer -13 4 1024
neg integer -12 -13
set integer 2 -12
lit integer -14 3 768
set integer 3 -14
lit integer -15 2 48
set integer 6 -15
lit integer -17 3 128
neg integer -16 -17
set integer 7 -16
lit boolean -18 5 False
set boolean 14 -18
lit string -20 8 click me
lit integer -21 1 0
lit integer -22 1 0
lit integer -23 1 0
lit integer -24 3 255
lit integer -25 3 255
lit integer -26 3 255
lit integer -27 3 255
lit integer -28 3 255
lit boolean -29 5 False
bind string 1 -20
bind integer 2 2
bind integer 3 3
bind integer 4 4
bind integer 5 5
bind integer 6 -21
bind integer 7 -22
bind integer 8 -23
bind integer 9 -24
bind integer 10 -25
bind integer 11 -26
bind integer 12 -27
bind integer 13 -28
bind boolean 14 -29
bind integer 15 11
call boolean -19 2258 bdw_text_button
jmpt -19 2
jmp 3
label 2
lit string -31 3 foo
bind string 1 -31
call nothing -30 2269 unitsliderstuff__writeln
label 3
add integer -32 3 7
set integer 3 -32
lit string -34 1 R
sub integer -35 2 6
lit integer -36 3 255
lit integer -37 3 255
lit integer -38 3 255
lit integer -39 3 255
bind string 1 -34
bind integer 2 -35
bind integer 3 3
bind integer 4 8
bind integer 5 -36
bind integer 6 -37
bind integer 7 -38
bind integer 8 -39
call nothing -33 2242 bd_text
lit integer -41 1 1
lit integer -42 1 0
lit integer -43 3 255
lit integer -44 1 1
lit integer -45 3 512
lit integer -46 2 40
lit integer -47 1 0
lit integer -48 1 0
lit integer -49 1 0
lit integer -50 3 255
lit integer -51 2 30
lit integer -52 2 60
lit integer -53 3 255
lit integer -54 1 0
lit integer -55 1 0
lit integer -56 3 255
gga integer -57 23 10
bind integer 1 -41
bind integer 2 -42
bind integer 3 -43
bind integer 4 -44
bind integer 5 2
bind integer 6 3
bind integer 7 -45
bind integer 8 -46
bind integer 9 -47
bind integer 10 -48
bind integer 11 -49
bind integer 12 -50
bind integer 13 -51
bind integer 14 -52
bind integer 15 -53
bind integer 16 -54
bind integer 17 -55
bind integer 18 -56
bind integer 19 11
bind integer 20 -57
call boolean -40 2265 bdw_slider
jmpt -40 4
jmp 5
label 4
lit boolean -58 4 True
set boolean 14 -58
label 5
add integer -59 3 7
set integer 3 -59
lit string -61 1 G
sub integer -62 2 6
lit integer -63 3 255
lit integer -64 3 255
lit integer -65 3 255
lit integer -66 3 255
bind string 1 -61
bind integer 2 -62
bind integer 3 3
bind integer 4 8
bind integer 5 -63
bind integer 6 -64
bind integer 7 -65
bind integer 8 -66
call nothing -60 2242 bd_text
lit integer -68 1 2
lit integer -69 1 0
lit integer -70 3 255
lit integer -71 1 1
lit integer -72 3 512
lit integer -73 2 40
lit integer -74 1 0
lit integer -75 1 0
lit integer -76 1 0
lit integer -77 3 255
lit integer -78 2 30
lit integer -79 2 60
lit integer -80 1 0
lit integer -81 3 255
lit integer -82 1 0
lit integer -83 3 255
gga integer -84 23 10
bind integer 1 -68
bind integer 2 -69
bind integer 3 -70
bind integer 4 -71
bind integer 5 2
bind integer 6 3
bind integer 7 -72
bind integer 8 -73
bind integer 9 -74
bind integer 10 -75
bind integer 11 -76
bind integer 12 -77
bind integer 13 -78
bind integer 14 -79
bind integer 15 -80
bind integer 16 -81
bind integer 17 -82
bind integer 18 -83
bind integer 19 11
bind integer 20 -84
call boolean -67 2265 bdw_slider
jmpt -67 6
jmp 7
label 6
lit boolean -85 4 True
set boolean 14 -85
label 7
add integer -86 3 7
set integer 3 -86
lit string -88 1 B
sub integer -89 2 6
lit integer -90 3 255
lit integer -91 3 255
lit integer -92 3 255
lit integer -93 3 255
bind string 1 -88
bind integer 2 -89
bind integer 3 3
bind integer 4 8
bind integer 5 -90
bind integer 6 -91
bind integer 7 -92
bind integer 8 -93
call nothing -87 2242 bd_text
lit integer -95 1 3
lit integer -96 1 0
lit integer -97 3 255
lit integer -98 1 1
lit integer -99 3 512
lit integer -100 2 40
lit integer -101 1 0
lit integer -102 1 0
lit integer -103 1 0
lit integer -104 3 255
lit integer -105 2 30
lit integer -106 2 60
lit integer -107 1 0
lit integer -108 1 0
lit integer -109 3 255
lit integer -110 3 255
gga integer -111 23 10
bind integer 1 -95
bind integer 2 -96
bind integer 3 -97
bind integer 4 -98
bind integer 5 2
bind integer 6 3
bind integer 7 -99
bind integer 8 -100
bind integer 9 -101
bind integer 10 -102
bind integer 11 -103
bind integer 12 -104
bind integer 13 -105
bind integer 14 -106
bind integer 15 -107
bind integer 16 -108
bind integer 17 -109
bind integer 18 -110
bind integer 19 11
bind integer 20 -111
call boolean -94 2265 bdw_slider
jmpt -94 8
jmp 9
label 8
lit boolean -112 4 True
set boolean 14 -112
label 9
add integer -113 3 7
set integer 3 -113
lit string -115 1 A
sub integer -116 2 6
lit integer -117 3 255
lit integer -118 3 255
lit integer -119 3 255
lit integer -120 3 255
bind string 1 -115
bind integer 2 -116
bind integer 3 3
bind integer 4 8
bind integer 5 -117
bind integer 6 -118
bind integer 7 -119
bind integer 8 -120
call nothing -114 2242 bd_text
lit integer -122 1 4
lit integer -123 1 0
lit integer -124 3 255
lit integer -125 1 1
lit integer -126 3 512
lit integer -127 2 40
lit integer -128 1 0
lit integer -129 1 0
lit integer -130 1 0
lit integer -131 3 255
lit integer -132 2 30
lit integer -133 2 60
lit integer -134 3 255
lit integer -135 3 255
lit integer -136 3 255
lit integer -137 3 255
gga integer -138 23 10
bind integer 1 -122
bind integer 2 -123
bind integer 3 -124
bind integer 4 -125
bind integer 5 2
bind integer 6 3
bind integer 7 -126
bind integer 8 -127
bind integer 9 -128
bind integer 10 -129
bind integer 11 -130
bind integer 12 -131
bind integer 13 -132
bind integer 14 -133
bind integer 15 -134
bind integer 16 -135
bind integer 17 -136
bind integer 18 -137
bind integer 19 11
bind integer 20 -138
call boolean -121 2265 bdw_slider
jmpt -121 10
jmp 11
label 10
lit boolean -139 4 True
set boolean 14 -139
label 11
jmpt 14 12
jmp 13
label 12
gg unit -141 8
gg hashtable -143 5
gg integer -144 274
lit integer -145 1 1
bind hashtable 1 -143
bind integer 2 -144
bind integer 3 -145
call integer -142 772 LoadInteger
gg hashtable -147 5
gg integer -148 274
lit integer -149 1 2
bind hashtable 1 -147
bind integer 2 -148
bind integer 3 -149
call integer -146 772 LoadInteger
gg hashtable -151 5
gg integer -152 274
lit integer -153 1 3
bind hashtable 1 -151
bind integer 2 -152
bind integer 3 -153
call integer -150 772 LoadInteger
gg hashtable -155 5
gg integer -156 274
lit integer -157 1 4
bind hashtable 1 -155
bind integer 2 -156
bind integer 3 -157
call integer -154 772 LoadInteger
bind unit 1 -141
bind integer 2 -142
bind integer 3 -146
bind integer 4 -150
bind integer 5 -154
call nothing -140 441 SetUnitVertexColor
label 13
call nothing -158 2246 bd_draw_end
ret nothing
In-game, I can still only see the button, but not the sliders.


My guess is that the instructions for the function aren't properly updated by the runtime or something...




I load the map again right after an init (the draw_loop function starts with "everything" commented out, as before).
This time I don't trigger an update without changing anything.

I immediately uncomment the button and trigger an update, I get:
Code:
Updating function entrypoint__on_chat_input
Updating function ascii__chr_init
Updating function ascii__ord_init
Updating function baddraw__img_list_clear
Updating function baddraw__cache_string_bytes
Updating function bdw_slider
Updating function bdw_vscrollbar
Updating function unitsliderstuff__draw_loop
Updating function unitsliderstuff__delayed_init
Updating function main
Writing bytecode
// many many lines of asm
In-game, I don't see the button.

I add a dummy writeln after the button and trigger an update:
JASS:
    if bdw_text_button("click me", x, y, w, h, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, false, lmb) then
        call writeln("foo")
    endif

    call writeln("ABCD")

I get:
Code:
Updating function unitsliderstuff__draw_loop
Writing bytecode
Writing state file
Ok.
fun 2271 unitsliderstuff__draw_loop
lit integer -1 1 0
set integer 10 -1
gga integer -2 20 10
set integer 11 -2
gga integer -3 21 10
set integer 12 -3
gga integer -4 22 10
set integer 13 -4
lit integer -5 2 35
set integer 8 -5
lit integer -7 1 0
bind integer 1 -7
call nothing -6 2245 bd_draw_begin
lit integer -9 4 1000
bind integer 1 -9
call nothing -8 2256 bdw_auto_wid_base
lit integer -10 3 256
set integer 4 -10
lit integer -11 2 64
set integer 5 -11
lit integer -13 4 1024
neg integer -12 -13
set integer 2 -12
lit integer -14 3 768
set integer 3 -14
lit integer -15 2 48
set integer 6 -15
lit integer -17 3 128
neg integer -16 -17
set integer 7 -16
lit boolean -18 5 False
set boolean 14 -18
lit string -20 8 click me
lit integer -21 1 0
lit integer -22 1 0
lit integer -23 1 0
lit integer -24 3 255
lit integer -25 3 255
lit integer -26 3 255
lit integer -27 3 255
lit integer -28 3 255
lit boolean -29 5 False
bind string 1 -20
bind integer 2 2
bind integer 3 3
bind integer 4 4
bind integer 5 5
bind integer 6 -21
bind integer 7 -22
bind integer 8 -23
bind integer 9 -24
bind integer 10 -25
bind integer 11 -26
bind integer 12 -27
bind integer 13 -28
bind boolean 14 -29
bind integer 15 11
call boolean -19 2258 bdw_text_button
jmpt -19 2
jmp 3
label 2
lit string -31 3 foo
bind string 1 -31
call nothing -30 2269 unitsliderstuff__writeln
label 3
lit string -33 4 ABCD
bind string 1 -33
call nothing -32 2269 unitsliderstuff__writeln
call nothing -34 2246 bd_draw_end
ret nothing
In-game, now I can see the button and ABCD (ABCD is printing repeatedly because we are in a loop).

I delete the writeln, I comment out the button and uncomment the first 4 sliders:
JASS:
    // if bdw_text_button("click me", x, y, w, h, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, false, lmb) then
    //     call writeln("foo")
    // endif

    set y = y + dy
    call bd_text("R", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(1, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0xFF, 0x00, 0x00, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("G", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(2, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0x00, 0xFF, 0x00, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("B", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(3, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0x00, 0x00, 0xFF, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    set y = y + dy
    call bd_text("A", x - dx, y, font_size, 0xFF, 0xFF, 0xFF, 0xFF)
    if bdw_slider(4, /*
     */ 0, 255, 1, /*
     */ x, y, 512, 40, 0x00, 0x00, 0x00, 0xFF, /*
     */ 30, 60, 0xFF, 0xFF, 0xFF, 0xFF, /*
     */ lmb, m.move_x) then
        set any_changed = true
    endif

    if any_changed then
        call SetUnitVertexColor(g_my_unit, bdw_slider_val(1), bdw_slider_val(2), bdw_slider_val(3), bdw_slider_val(4))
    endif

I get:
Code:
Updating function unitsliderstuff__draw_loop
Writing bytecode
Writing state file
Ok.
fun 2271 unitsliderstuff__draw_loop
lit integer -1 1 0
set integer 10 -1
gga integer -2 20 10
set integer 11 -2
gga integer -3 21 10
set integer 12 -3
gga integer -4 22 10
set integer 13 -4
lit integer -5 2 35
set integer 8 -5
lit integer -7 1 0
bind integer 1 -7
call nothing -6 2245 bd_draw_begin
lit integer -9 4 1000
bind integer 1 -9
call nothing -8 2256 bdw_auto_wid_base
lit integer -10 3 256
set integer 4 -10
lit integer -11 2 64
set integer 5 -11
lit integer -13 4 1024
neg integer -12 -13
set integer 2 -12
lit integer -14 3 768
set integer 3 -14
lit integer -15 2 48
set integer 6 -15
lit integer -17 3 128
neg integer -16 -17
set integer 7 -16
lit boolean -18 5 False
set boolean 14 -18
add integer -19 3 7
set integer 3 -19
lit string -21 1 R
sub integer -22 2 6
lit integer -23 3 255
lit integer -24 3 255
lit integer -25 3 255
lit integer -26 3 255
bind string 1 -21
bind integer 2 -22
bind integer 3 3
bind integer 4 8
bind integer 5 -23
bind integer 6 -24
bind integer 7 -25
bind integer 8 -26
call nothing -20 2242 bd_text
lit integer -28 1 1
lit integer -29 1 0
lit integer -30 3 255
lit integer -31 1 1
lit integer -32 3 512
lit integer -33 2 40
lit integer -34 1 0
lit integer -35 1 0
lit integer -36 1 0
lit integer -37 3 255
lit integer -38 2 30
lit integer -39 2 60
lit integer -40 3 255
lit integer -41 1 0
lit integer -42 1 0
lit integer -43 3 255
gga integer -44 23 10
bind integer 1 -28
bind integer 2 -29
bind integer 3 -30
bind integer 4 -31
bind integer 5 2
bind integer 6 3
bind integer 7 -32
bind integer 8 -33
bind integer 9 -34
bind integer 10 -35
bind integer 11 -36
bind integer 12 -37
bind integer 13 -38
bind integer 14 -39
bind integer 15 -40
bind integer 16 -41
bind integer 17 -42
bind integer 18 -43
bind integer 19 11
bind integer 20 -44
call boolean -27 2265 bdw_slider
jmpt -27 2
jmp 3
label 2
lit boolean -45 4 True
set boolean 14 -45
label 3
add integer -46 3 7
set integer 3 -46
lit string -48 1 G
sub integer -49 2 6
lit integer -50 3 255
lit integer -51 3 255
lit integer -52 3 255
lit integer -53 3 255
bind string 1 -48
bind integer 2 -49
bind integer 3 3
bind integer 4 8
bind integer 5 -50
bind integer 6 -51
bind integer 7 -52
bind integer 8 -53
call nothing -47 2242 bd_text
lit integer -55 1 2
lit integer -56 1 0
lit integer -57 3 255
lit integer -58 1 1
lit integer -59 3 512
lit integer -60 2 40
lit integer -61 1 0
lit integer -62 1 0
lit integer -63 1 0
lit integer -64 3 255
lit integer -65 2 30
lit integer -66 2 60
lit integer -67 1 0
lit integer -68 3 255
lit integer -69 1 0
lit integer -70 3 255
gga integer -71 23 10
bind integer 1 -55
bind integer 2 -56
bind integer 3 -57
bind integer 4 -58
bind integer 5 2
bind integer 6 3
bind integer 7 -59
bind integer 8 -60
bind integer 9 -61
bind integer 10 -62
bind integer 11 -63
bind integer 12 -64
bind integer 13 -65
bind integer 14 -66
bind integer 15 -67
bind integer 16 -68
bind integer 17 -69
bind integer 18 -70
bind integer 19 11
bind integer 20 -71
call boolean -54 2265 bdw_slider
jmpt -54 4
jmp 5
label 4
lit boolean -72 4 True
set boolean 14 -72
label 5
add integer -73 3 7
set integer 3 -73
lit string -75 1 B
sub integer -76 2 6
lit integer -77 3 255
lit integer -78 3 255
lit integer -79 3 255
lit integer -80 3 255
bind string 1 -75
bind integer 2 -76
bind integer 3 3
bind integer 4 8
bind integer 5 -77
bind integer 6 -78
bind integer 7 -79
bind integer 8 -80
call nothing -74 2242 bd_text
lit integer -82 1 3
lit integer -83 1 0
lit integer -84 3 255
lit integer -85 1 1
lit integer -86 3 512
lit integer -87 2 40
lit integer -88 1 0
lit integer -89 1 0
lit integer -90 1 0
lit integer -91 3 255
lit integer -92 2 30
lit integer -93 2 60
lit integer -94 1 0
lit integer -95 1 0
lit integer -96 3 255
lit integer -97 3 255
gga integer -98 23 10
bind integer 1 -82
bind integer 2 -83
bind integer 3 -84
bind integer 4 -85
bind integer 5 2
bind integer 6 3
bind integer 7 -86
bind integer 8 -87
bind integer 9 -88
bind integer 10 -89
bind integer 11 -90
bind integer 12 -91
bind integer 13 -92
bind integer 14 -93
bind integer 15 -94
bind integer 16 -95
bind integer 17 -96
bind integer 18 -97
bind integer 19 11
bind integer 20 -98
call boolean -81 2265 bdw_slider
jmpt -81 6
jmp 7
label 6
lit boolean -99 4 True
set boolean 14 -99
label 7
add integer -100 3 7
set integer 3 -100
lit string -102 1 A
sub integer -103 2 6
lit integer -104 3 255
lit integer -105 3 255
lit integer -106 3 255
lit integer -107 3 255
bind string 1 -102
bind integer 2 -103
bind integer 3 3
bind integer 4 8
bind integer 5 -104
bind integer 6 -105
bind integer 7 -106
bind integer 8 -107
call nothing -101 2242 bd_text
lit integer -109 1 4
lit integer -110 1 0
lit integer -111 3 255
lit integer -112 1 1
lit integer -113 3 512
lit integer -114 2 40
lit integer -115 1 0
lit integer -116 1 0
lit integer -117 1 0
lit integer -118 3 255
lit integer -119 2 30
lit integer -120 2 60
lit integer -121 3 255
lit integer -122 3 255
lit integer -123 3 255
lit integer -124 3 255
gga integer -125 23 10
bind integer 1 -109
bind integer 2 -110
bind integer 3 -111
bind integer 4 -112
bind integer 5 2
bind integer 6 3
bind integer 7 -113
bind integer 8 -114
bind integer 9 -115
bind integer 10 -116
bind integer 11 -117
bind integer 12 -118
bind integer 13 -119
bind integer 14 -120
bind integer 15 -121
bind integer 16 -122
bind integer 17 -123
bind integer 18 -124
bind integer 19 11
bind integer 20 -125
call boolean -108 2265 bdw_slider
jmpt -108 8
jmp 9
label 8
lit boolean -126 4 True
set boolean 14 -126
label 9
jmpt 14 10
jmp 11
label 10
gg unit -128 8
gg hashtable -130 5
gg integer -131 274
lit integer -132 1 1
bind hashtable 1 -130
bind integer 2 -131
bind integer 3 -132
call integer -129 772 LoadInteger
gg hashtable -134 5
gg integer -135 274
lit integer -136 1 2
bind hashtable 1 -134
bind integer 2 -135
bind integer 3 -136
call integer -133 772 LoadInteger
gg hashtable -138 5
gg integer -139 274
lit integer -140 1 3
bind hashtable 1 -138
bind integer 2 -139
bind integer 3 -140
call integer -137 772 LoadInteger
gg hashtable -142 5
gg integer -143 274
lit integer -144 1 4
bind hashtable 1 -142
bind integer 2 -143
bind integer 3 -144
call integer -141 772 LoadInteger
bind unit 1 -128
bind integer 2 -129
bind integer 3 -133
bind integer 4 -137
bind integer 5 -141
call nothing -127 441 SetUnitVertexColor
label 11
call nothing -145 2246 bd_draw_end
ret nothing
In-game, I can still see the button and the ABCD is still printing, I can't see the sliders.
 

Attachments

  • main.j
    14.5 KB · Views: 53

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
@Aniki
I have updated the first post with a new version.
I don't know what caused updating all the functions even if nothing had changed but it seems to be fixed now. Also i don't know how you compile your map but atleast with the way i do it your script seems to be working for me; can add/remove sliders and button.
I think the problem was that you've hit the limits on preload. It was six abilities with 500 bytes per ability. Now i've upped the limits to 24 abilities with 700bytes per ability. Also it should now report an error if you hit the limit.
Also nice widget library, i like it.

If you still have errors please give an exact description on how to compile your map.

It should not work due to type mismatch however. Code is not an integer.
Oh yes, don't know how i missed that. My compiler should replace all function literals with integer literals. If pjass doesn't accept the resulting .j-file it's a bug.
 
Level 13
Joined
Nov 7, 2014
Messages
570
I don't know what caused updating all the functions even if nothing had changed but it seems to be fixed now. Also i don't know how you compile your map but atleast with the way i do it your script seems to be working for me; can add/remove sliders and button.
I was 'init'ing using jasshelper's '--debug' flag but 'update'ing without it. In 'non-debug'/'release' mode jasshelper does some inlining which Jhcr picked up as changes... sorry about that. Everything seems to work now. Thanks!

I think it still not too difficult to reach the reloadable bytecode limit though:
You've reached the limit of reloadable bytecode
CallStack (from HasCallStack):
error, called at Main.hs:247:30 in main:Main
Can the 'reloadable bytecode limit' be removed?

Also nice widget library, i like it.
The ridiculously long argument lists though... I am going to use structs to pack some of the arguments of some of the functions.

I can't help but notice (with the in-game command '/fps') the "slight" fps drop when the switch to running bytecode happens (in the 'draw_loop' function with the sliders). My guess is that I won't be able to do many many draw calls at the same time (ignoring the 'reloadable bytecode limit'). Can't really complain though, because it's pretty much impossible to write anything fast in Jass, let alone a VM!

PS: Main.hs... uh?
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
I think it still not too difficult to reach the reloadable bytecode limit though:

Can the 'reloadable bytecode limit' be removed?

Well at one point we would reach the oplimit again. Also it would be nice to know the exact limit on BlzSetAbilityTooltip because at one point it simply crashes wc3. I have reduced the encoded bytecode a bit already but nothing huge.

Also it now displays that error better. I just wanted to release a version for people to test.

I can't help but notice (with the in-game command '/fps') the "slight" fps drop when the switch to running bytecode happens (in the 'draw_loop' function with the sliders). My guess is that I won't be able to do many many draw calls at the same time (ignoring the 'reloadable bytecode limit'). Can't really complain though, because it's pretty much impossible to write anything fast in Jass, let alone a VM!

Personally i don't see much fps drops but it's in the nature of this to have a performance hit.
Also the resulting bytecode is very naively generated. One could def write an optimizer for it. It just isn't a priority right now.

PS: Main.hs... uh?
λ
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
As you may or may not have noticed, i have regularly uploaded new versions. Most of them were just bug fixes but two changes are of note:
1. ExecuteFunc now works correctly (with old or new functions)
2. globals are now correctly initialized. Once. That means only the value of the global when it is first loaded into the map is saved. After that the interpreter takes the value saved in the state.


TriggerRegisterVariableEvent doesn't work on newly defined globals yet though.
 
Level 13
Joined
Nov 7, 2014
Messages
570
vJass' array fields implementation seems to trip jhcr:

JASS:
struct Foo
    string array bar[5]
endstruct

// => jasshelper's output:
//JASSHelper struct globals:
constant integer si__Foo=1
integer si__Foo_F=0
integer si__Foo_I=0
integer array si__Foo_V
string array s___Foo_bar // <-- 3 '_' here
constant integer s___Foo_bar_size=5
integer array s__Foo_bar

// => jhcr' output
constant integer si__Foo = 1
integer si__Foo_F = 0
integer si__Foo_I = 0
integer array si__Foo_V
string array s__Foo_bar // <-- only 2 '_' here
constant integer s__Foo_bar_size = 5
integer array s__Foo_bar
 
Level 13
Joined
Nov 7, 2014
Messages
570
Using GUI triggers with JHCR

It is possible to use GUI triggers and JHCR because GUI triggers are really just Jass generated by the World Editor. It's not as easy and convenient as using [v]Jass directly, because there's some clicking involved, but it is possible.


Our setup uses 3 ".bat" (windows cmd.exe/shell) scripts. Also these 3 tools are required/invoked by the ".bat" scripts:
* JHCR (obviously) by LeP
* MPQEditor by zezula
* close-map-handle-war3

Our setup uses the following directory structure:
Code:
path\to\test-map-directory
|
|- 1-run-after-save-map-before-game-is-running.bat
|- 2-run-once-after-map-loads-ingame.bat
|- 3-run-after-save-map-while-game-is-running.bat
|- test-map.w3x
|- map-tools
    |
    |- jhcr.exe
    |- MPQEditor.exe
    |- close-map-handle-war3.exe
    |- files
        |
        |- Blizzard.j
        |- common.j

"path\to\test-map-directory" is an arbitrary directory.
"1-run-after-save-map-before-game-is-running.bat", "2-run-once-after-map-loads-ingame.bat", "3-run-after-save-map-while-game-is-running.bat" are the 3 ".bat" scripts.
"test-map.w3x" is the name we use for our test map.


We need to edit the "1-run-after-save-map-before-game-is-running.bat" file. Specifically we need to edit the path to "Warcraft III.exe" and the path to "test-map.w3x".

We don't have to edit the "2-run-once-after-map-loads-ingame.bat" file.

We need to edit the "3-run-after-save-map-while-game-is-running.bat" file. Specifically we need to edit the "--preload-path" (unless your user name is also Aniki).


We can now start the World Editor and create a new map.

We need to add the mandatory trigger to initialize JHCR, (by calling ExecuteFunc("JHCR_Init_init")).
jhcr-init-png.320625


We also need a way to tell JHCR that we've made changes to our map, and we would like these changes to take effect. We can do it with a trigger similar to this one:
jhcr-update-png.320627


Now we can save our map as "path\to\test-map-directory\test-map.w3x". Then we run the "1-run-after-save-map-before-game-is-running.bat" script (from the console, or clicking on it). It should start Warcraft III and load our test map.

Our test map doesn't do anything at the moment, so we need to close the game and make it do something. We could add a hero and a trigger that changes its armor by some amount X when we press the escape key. After we add the "something", we again save the map and run the "1-run-after-save-map-before-game-is-running.bat". After the map loads in-game we also run the "2-run-once-after-map-loads-ingame.bat".

Now instead of closing the game, changing the triggers and then restarting the game we could instead: make changes to our trigger (say change X to X + 1), save the map, run the "3-run-after-save-map-while-game-is-running.bat", in-game: press the escape key (or however we've decided to call ExecuteFunc("JHCR_Init_parse")), and observe our change.


The attached zip file has the ".bat" scripts, "map-tools", and the necessary directory structure.
 

Attachments

  • jhcr-init.png
    jhcr-init.png
    9 KB · Views: 671
  • jhcr-update.png
    jhcr-update.png
    12.2 KB · Views: 741
  • jhcr-gui-setup.zip
    3.3 MB · Views: 220
Last edited:
  • Like
Reactions: LeP

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
vJass' array fields implementation seems to trip jhcr:

JASS:
struct Foo
    string array bar[5]
endstruct

// => jasshelper's output:
//JASSHelper struct globals:
constant integer si__Foo=1
integer si__Foo_F=0
integer si__Foo_I=0
integer array si__Foo_V
string array s___Foo_bar // <-- 3 '_' here
constant integer s___Foo_bar_size=5
integer array s__Foo_bar

// => jhcr' output
constant integer si__Foo = 1
integer si__Foo_F = 0
integer si__Foo_I = 0
integer array si__Foo_V
string array s__Foo_bar // <-- only 2 '_' here
constant integer s__Foo_bar_size = 5
integer array s__Foo_bar

Unless it breaks something that is essential and what the --jasshelper flag does. Since jasshelper inserts a random number of underscores but we need stable names for jhcr to work correctly.

Using GUI triggers with JHCR

It is possible to use GUI triggers and JHCR because GUI triggers are really just Jass generated by the World Editor. It's not as easy and convenient as using [v]Jass directly, because there's some clicking involved, but it is possible.


Our setup uses 3 ".bat" (windows cmd.exe/shell) scripts. Also these 3 tools are required/invoked by the ".bat" scripts:
* JHCR (obviously) by LeP
* MPQEditor by zezula
* close-map-handle-war3

Our setup uses the following directory structure:
Code:
path\to\test-map-directory
|
|- 0-run-after-save-map-before-game-is-running.bat
|- 1-run-before-save-map-while-game-is-running.bat
|- 3-run-after-save-map-while-game-is-running.bat
|- test-map.w3x
|- map-tools
    |
    |- jhcr.exe
    |- MPQEditor.exe
    |- close-map-handle-war3.exe
    |- files
        |
        |- Blizzard.j
        |- common.j

"path\to\test-map-directory" is an arbitrary directory.
"0-run-after-save-map-before-game-is-running.bat", "1-run-before-save-map-while-game-is-running.bat", "3-run-after-save-map-while-game-is-running.bat" are the 3 ".bat" scripts.
"test-map.w3x" is the name we use for our test map.


We need to edit the "0-run-after-save-map-before-game-is-running.bat" file. Specifically we need to edit the path to "Warcraft III.exe" and the path to "test-map.w3x".

We don't have to edit the "1-run-before-save-map-while-game-is-running.bat" file.

We need to edit the "3-run-after-save-map-while-game-is-running.bat" file. Specifically we need to edit the "--preload-path" (unless your user name is also Aniki).


We can now start the World Editor and create a new map.

We need to add the mandatory trigger to initialize JHCR, (by calling ExecuteFunc("JHCR_Init_init")).
jhcr-init-png.320625


We also need a way to tell JHCR that we've made changes to our map, and we would like these changes to take effect. We can do it with a trigger similar to this one:
jhcr-update-png.320627


Now we can save our map as "path\to\test-map-directory\test-map.w3x". Then we run the "0-run-after-save-map-before-game-is-running.bat" script (from the console, or clicking on it). It should start Warcraft III and load our test map.

Our test map doesn't do anything at the moment, so we need to close the game and make it do something. We could add a hero and a trigger that changes its armor by some amount X when we press the escape key. After we add the "something", we again save the map and run the "0-run-after-save-map-before-game-is-running.bat".

Now instead of closing the game, changing the triggers and then restarting the game we could instead: run the "1-run-before-save-map-while-game-is-running.bat", make changes to our trigger (say change X to X + 1), save the map, run the "3-run-after-save-map-while-game-is-running.bat", in-game: press the escape key (or however we've decided to call ExecuteFunc("JHCR_Init_parse")), and observe our change.


The attached zip file has the ".bat" scripts, "map-tools", and the necessary directory structure.

Thanks for the guide and batch scripts (also i haven't looked at them yet).
We also have a guide for Wurst.
I would really enjoy if more people would be using this.
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Is there gonna be JHCR for 1.31 PTR anytime soon?
Oh sure, i didn't know people wanted it.
Here is jhcr for 1.31 just for you.
Included in this are also a bunch of optimisations for the generated bytecode, so you're able to load a bit more code into the map.
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
I have updated jhcr in the main post for patch 1.31. There was a bug in which reload didnt work. I think it was related to the now changed stuff in the next ptr of zero based indexing in natives as i use BlzSetAbilityTooltip to get the data into the game. I don't know if i have to revert that change once this ptr becomes the next official patch.
 
Last edited:

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Just wanted to update the attached exe since i fixed some bugs since last release.
And you don't have to call JHCR_Init_init anymore; it's now handled automatically but it also doesn't cause any harm.

Some bugs that were fixed:
Code:
      Fixed bug in local array handling
      Correctly shadow globals
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Thanks for geat tool, but it seem not working on Reforge.

Thanks. I don't have access to reforge but i assume it is because they modified common.j
Unfortunately i have to compile the program for each different common.j


There is no reason for it to not work, since JASS is supported in reforged as it is in the original client.
If it doesn't work for you, then specify *what* isn't working.
I simply don't get ppl just writing "doesn't work" without any explanation. What do you expect to happen from that?
Yeah i guess. Although jhcr currently doesnt provide the best error messages. But saying which step doesnt work would atleast provide some insight.
 
Level 1
Joined
Dec 8, 2019
Messages
2
There is no reason for it to not work, since JASS is supported in reforged as it is in the original client.
If it doesn't work for you, then specify *what* isn't working.
I simply don't get ppl just writing "doesn't work" without any explanation. What do you expect to happen from that?

Sorry, i think the author already know this issue.
I'm using wusrt and hot-reload command form vscode, there is no compilation or runtime error with jhcr but nothing happend when press ESC key(only print debug message). In W3 classic, everything work fine!
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Sorry, i think the author already know this issue.
I'm using wusrt and hot-reload command form vscode, there is no compilation or runtime error with jhcr but nothing happend when press ESC key(only print debug message). In W3 classic, everything work fine!

Nice to hear! Once the reforge patch hits classic you can expect a working version :)
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Updated first post to make jhcr compatible with patch 1.32
If you encouter any bugs please do tell me since i don't want to run 1.32 myself.
But i only added the two new types (minimapicon and commandbuttoneffect) so it shouldn't cause any trouble.
 
  • Like
Reactions: pyf
Level 1
Joined
Apr 8, 2020
Messages
111
How do I actually use this tool? I clicked on the application and then the command prompt appears and disappears instantly. Nothing happens afterward.
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
How do I actually use this tool? I clicked on the application and then the command prompt appears and disappears instantly. Nothing happens afterward.
Well i wrote down how to use this in the first post but i will try to break it down a bit more.
First of all: this is a command-line application so would run it from some kind of console prompt.
Also it doesn't work with the map itself but only with the mapscript so you would have to extract and re-insert the mapscript. We can use ladiks mpq-editor to do this.

So to get started we should prepare the map like this:
Code:
# copy the our map to some temporary file for safety reasons
cp our-map.w3x tmp.w3x

# then we extract the maps script file
MPQEditor extract tmp.w3x war3map.j

# actually run jhcr for the init phase
# you need the common.j and Blizzard.j files in the same directory as your map
# this step produces the file jhcr_war3map.j
jhcr init common.j Blizzard.j war3map.j --jasshelper

# now we insert jhcr_war3map.j as war3map.j into tmp.w3x
MPQEditor add tmp.w3x jhcr_war3map.j war3map.j

# now we can start the map. but we copy it again to make sure we can later overwrite tmp.w3x again
# the path is ofcourse different for most of you
# also i don't know if -loadfile still works, might have changed with later patches
cp tmp.w3x run.w3x
/path/to/WarCraft\ 3.exe -loadfile run.w3x

The above steps are neccessary when you freshly start the map.
Now you edit your map in the worldeditor and at some point press save.
If you didn't have any syntax errors you can now reload the changes to the script for example as follows:

Code:
# copy our map again to tmp.w3x
cp our-map.w3x tmp.w3x

# extract the new script
MPQEditor extract tmp.w3x war3map.j

# now we use jhcr update instead of jhcr init
jhcr update war3map.j --jasshelper --preload-path='/path/to/Documents/Warcarft\ III/CustomMapData/'

If all went well you can now reload the changes in your map (however you set it up. see first post).

This is actually quite close to my setup. I run pjass over jhcr_war3map.j to make doubly sure that it should actually work and i use a Makefile to seperate those steps. But except that it's mostly the same.

----------

To be honest with you i don't know if this still works with the latest patch as i don't have it installed. There shouldn't be any inherent flaw why it shouldn't work anymore but small things might need to be fixed. If it doesn't work for you i am glad to fix it if you provide enough guidance.
 
Last edited:
Level 1
Joined
Apr 17, 2020
Messages
1
Hello, i also have a problem with JHCR.exe.
I'm using Wurst as described on this Website here: https://wurstlang.org/.
I installed Java 1.8.0 and enabled the Wurst VSCode Extension. Naturally, i installed WurstSetup.jar, used the command <java -jar WurstSetup.jar install wurstscript> and added the wurst folder to the environment variables. To generate the Project i used <grill generate <projectName>> on the command line. Hope I didn't forget something.

Now, I tried the wurst-extension command which runs the projects standard wurst map example in it. This will start Wc3 and load the map as expected. There are no compilation errors, Wurst is succesfully converted to Jass. Looks good, until i stumbled over the hot-reloading section of the website. It links to this post with your JHCR.exe. I downloaded and set it up accordingly, meaning I added following to the settings.json in the projects root like described there:

"wurst.jhrcExe": "C:\\<Path>\\jhcr.exe" and
"wurst.customMapDataPath": "C:\\<Path>\Warcraft III"

Finally, i pressed F1 in VSCode (to run the command provided by wurst to compile the map with potential hot-reloading) and watched the show. Unfortunately, it failed and the wurst extension only throws the same exception over and over again. It states practically nothing to me, only that the process failed "when running external tool":

An exception was thrown when running the map:
java.io.IOException: Failure when running external tool.
Source: Wurst language support (Extension).


This is all. I can't figure out what exactly fails in there. I would really appreciate it if you were to look after or even fix this. It may as well be just an user error, i.e. my fault. But I can only use what is documented on the website, as this thread dosn't cover jhrc combined with a generated wurst-project with VSCode. At least that is how i understood it, did not read everything here, sorry if that is not the case.

Edit: I have the newest Warcraft 3 version installed.
 
Last edited:

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
Hello, i also have a problem with JHCR.exe.
I'm using Wurst as described on this Website here: https://wurstlang.org/.
I installed Java 1.8.0 and enabled the Wurst VSCode Extension. Naturally, i installed WurstSetup.jar, used the command <java -jar WurstSetup.jar install wurstscript> and added the wurst folder to the environment variables. To generate the Project i used <grill generate <projectName>> on the command line. Hope I didn't forget something.

Now, I tried the wurst-extension command which runs the projects standard wurst map example in it. This will start Wc3 and load the map as expected. There are no compilation errors, Wurst is succesfully converted to Jass. Looks good, until i stumbled over the hot-reloading section of the website. It links to this post with your JHCR.exe. I downloaded and set it up accordingly, meaning I added following to the settings.json in the projects root like described there:

"wurst.jhrcExe": "C:\\<Path>\\jhcr.exe" and
"wurst.customMapDataPath": "C:\\<Path>\Warcraft III"

Finally, i pressed F1 in VSCode (to run the command provided by wurst to compile the map with potential hot-reloading) and watched the show. Unfortunately, it failed and the wurst extension only throws the same exception over and over again. It states practically nothing to me, only that the process failed "when running external tool":

An exception was thrown when running the map:
java.io.IOException: Failure when running external tool.
Source: Wurst language support (Extension).


This is all. I can't figure out what exactly fails in there. I would really appreciate it if you were to look after or even fix this. It may as well be just an user error, i.e. my fault. But I can only use what is documented on the website, as this thread dosn't cover jhrc combined with a generated wurst-project with VSCode. At least that is how i understood it, did not read everything here, sorry if that is not the case.

Edit: I have the newest Warcraft 3 version installed.

Personally i don't use much wurst so i don't know if they have fixed this but in the past they didn't provide the error message of jhcr so i can't tell from afar what's going wrong. Normally wurst has more problems with updating the code and not with the initial compile step so there might be an interesting error at play here. The best course of action is to reach out to me on our channel #inwc.de-maps on quakenet.org (in reasonable europe hours).

The problem could be that i haven't compiled jhcr with the newest common.j as i think they added some new natives with the newest patch?
In any case i need the jass file fed to jhcr.
 

LeP

LeP

Level 10
Joined
Feb 13, 2008
Messages
497
I was thinking about writing some blog-like posts to explain how JHCR actually works.
This first one will give a high-level overview. If you like this and want me to write
more please tell me.

The Problem


Modifying WarCraft 3 maps has a long iteration period due to closing WC3,
changing your code and restarting WC3. JHCR (Jass hot code reload) tries to
improve this cycle by replacing the slow actions of closing and starting WC3
with a hopefully faster compiler which pushes your code changes directly into
an already running WC3.

But how could something like that even work? The answer is multi faceted.
So let's start with some very basic mockups.

The most simple Idea


Let's say we want to reload a single function, how would that look like in code?

JASS:
function foo takes nothing returns nothing
    if function_was_reloaded("foo") then
        // somehow execute the new body
        // call BJDebugMsg("New Foo")
    else
        // old body of foo
        call BJDebugMsg("Foo")
    endif
endfunction

While this has many holes it's conceptually correct already. But for the sake
of easy implementation we split up the function into two functions, the original
function but with a new name and a new function but with the old name:

JASS:
function JHCR_foo takes nothing returns nothing
    call BJDebugMsg("Foo")
endfunction

function foo takes nothing returns nothing
    if function_was_reloaded("foo") then
        // somehow execute the new body
        // call BJDebugMsg("New Foo")
    else
        call JHCR_foo()
    endif
endfunction

This way we don't have to change other functions calling foo when we update it.
Everything from a simple call-statement over trigger-actions to such things as
ExecuteFunc still work just fine.

The harder part is now how to actually execute the new body of foo.
To achieve this JHCR provides both a compiler to- and an interpreter for a
custom bytecode language which will be superficially introduced in the next
section.

The Bytecode


The bytecode was carefully designed to strike a balance between size, parsing
speed and execution speed as all of these factors play a big role in our limited
WC3 environment.

For example the original foo would be translated to this bytecode:

Code:
fun 3 foo
lit string -2 Foo
bind string 1 -2
call -1 2 BJDebugMsg
ret nothing

But this is of course way too verbose and also not very fast to parse.
So this format is only used for human consumption; to communicate with WC3
JHCR provides another format for this exact bytecode which looks like this:

Code:
| fun  |                       |       bind          |                 |ret|
263.....21136-2.......3.....Foo201361........-2.......22-1.......2.....29139
        |         lit         |                       |     call      |

I've annotated the different parts to show the opcode they represent.

This is way easier to parse programatically and takes up less space.
Every bytecode uses a fixed number and we encode numbers in a fixed width
with added padding to achieve a good tradeoff between size and parsing speed.
For fast parsing we use the fact that S2I("123asdf") == S2I(123) so we can move
in well defined chunks over the input string.

There are plenty more opcodes ofcourse but we'll save them for a future post.

Integrating


As we've seen previously we took special care to preserve the original name
of our function but this was mostly to integrate our system into the maps script.
Now we will talk about integrating the maps script into JHCR.

Functions


Take the aboves bytecode as an example: we call the BJ-function BJDebugMsg in
there. How do we actually achieve this?
As JHCR takes both common.j and Blizzard.j at compile time we assign each
function an unique id (BJDebugMsg has id 2 in our example). Now, to be able to
call this function we generate a huge if-then-else block for each id at compile
time to call out to different functions. If we limit ourself to only three functions,
that is DisplayTimedTextToPlayer, BJDebugMsg and foo we can look at the code
JHCR generates below.

JASS:
function JHCR_Auto_call_predefined takes integer JHCR_reg,integer JHCR_i,integer JHCR_ctx returns nothing
    if (JHCR_i < 2) then
        call DisplayTimedTextToPlayer (JHCR_Table_get_player (JHCR_Context_bindings[JHCR_ctx],1),JHCR_Table_get_real (JHCR_Context_bindings[JHCR_ctx],2),JHCR_Table_get_real (JHCR_Context_bindings[JHCR_ctx],3),JHCR_Table_get_real (JHCR_Context_bindings[JHCR_ctx],4),JHCR_Table_get_string (JHCR_Context_bindings[JHCR_ctx],5))
    else
        if (JHCR_i < 3) then
            call BJDebugMsg (JHCR_Table_get_string (JHCR_Context_bindings[JHCR_ctx],1))
        else
            call foo ()
        endif
    endif
endfunction

This is ofcourse quite a mouthfull but it all follows the same pattern.
The first parameter is the register the return-value of the called function is
stored in. but since all our functions return nothing it wont be used here.
The next parameter is the id of the function we want to call. As you can see
we do a bunch of comparisons to get to the correct function.
The next parameter is a struct called context in which we store a bunch of
information for the interpreter, most importantly here the parameters we want
passed to the function we want to call.
Those parameters are passed via the bind opcode which you can see just before
the call opcode in our small example above.


Globals


We also need to be able to read and write globals from our interpreter and we
deploy a similar technique as we did for functions.
For each available type in JASS (handle, integer, unit, etc.) we generate
two functions to read and write a specific global variable of that type.
As with functions we assign each global variable a per-type unique id on which
we do a bunch of comparisons to set or read the correct value.
Let's again only look at a very small input script with only two global variables
bj_MAX_PLAYERS and bj_forLoopAIndex where bj_MAX_PLAYERS is declared constant.
JHCR will generate the following functions:

JASS:
function JHCR_Auto_get_global_integer takes integer JHCR_i returns integer
    if (JHCR_i < 2) then
        return bj_MAX_PLAYERS
    else
        return bj_forLoopAIndex
    endif
endfunction

function JHCR_Auto_set_global_integer takes integer JHCR_i,integer JHCR_v returns nothing
    if (JHCR_i < 2) then
        return
    else
        set bj_forLoopAIndex = JHCR_v
    endif
endfunction

Note the then-case in JHCR_Auto_set_global_integer: since bj_MAX_PLAYERS is
constant we actually can't set it. But both can of course be read.
A similar approach is used for global arrays.

Update


To actually reload changed functions JHCR read the new script file and checks
for each function if the hash of the function has changed. If that happens
the bytecode like above is created and written to a file to be loaded by the
Preload-native. For example the changed foo function would generate the
following preload-file:

JASS:
function foo takes nothing returns nothing
    call BJDebugMsg("New foo")
endfunction

function PreloadFiles takes nothing returns nothing
    call SetPlayerTechMaxAllowed (Player (0),1,1)
    call BlzSetAbilityTooltip ('Agyv',"263.....21136-2.......7.....New foo201361........-2.......22-1.......2.....29139",0)
    call SetPlayerTechMaxAllowed (Player (0),2,0)
endfunction

The generated bytecode is of course very similar to the previously shown since
we only changed one string literal after all.
Now what happens with this next is topic of a future post.
We will end this little post here even though it only scratches the surface of
what JHCR has to do.
I hope you enjoyed this quick overview of JHCR.
 
Top