1. The Melee Mapping Contest #4: 2v2 - Results are out! Step by to congratulate the winners!
    Dismiss Notice
  2. We're hosting the 15th Mini-Mapping Contest with YouTuber Abelhawk! The contestants are to create a custom map that uses the hidden content within Warcraft 3 or is inspired by any of the many secrets within the game.
    Dismiss Notice
  3. The 20th iteration of the Terraining Contest is upon us! Join and create exquisite Water Structures for it.
    Dismiss Notice
  4. Check out the Staff job openings thread.
    Dismiss Notice

Bridging GUI with vJass - Initialization Issues

Discussion in 'The Lab' started by Bribe, Nov 15, 2018.

  1. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,819
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Hi all.

    I've been working on a project getting all my major GUI systems compatible with vJass without breaking ANY of the GUI compatibility that they are known for. Stuff like Table just cannot be ported to GUI, but the rest certainly can.

    However, there's been a pretty big hurdle I've been trying to work against, which is to do with the initialization priorities in GUI VS what JassHelper compiles.

    Problem 1: GUI InitTrigs run after ALL vJass systems have initialized. If a system like GUI Unit Event initializes from a vJass initializer, any preplaced units will not fire off any of the "UnitIndexEvent" triggers that GUI might use.

    Problem 2: vJass Initializes before the "InitGlobals" line is called. This means that if I use a GUI Configuration trigger (like in Damage Engine or Spell System) from a vJass initializer, all of those configurable udg_ values will be overwritten (not to mention the gg_trg values will also be overwritten, because they haven't been created yet).

    Possible solution to problem 1) Force GUI users to use an interface that requires them to run their triggers manually from a bulk trigger. A very annoying solution which doesn't addess Problem 2 at all.

    Possible solution to problem 2) Force vJass users to call to a function that basically makes them "wait" until the GUI stuff has initialized. This has the problem of timing - should I then call trigger from an InitTrig, or from a Map Initialization trigger? If it's the former, it's prone to errors with problem 1. But if it's the latter, any Map Initialization triggers might not have their shit together yet (in case they depend on those systems).

    So I've found a weird "solution" to both problems, by using the "inject" feature with replacing every possible GUI Initialization function call wrapped in "ExecuteFunc" so as to not cause syntax errors. I then force the initialization to cooperate with both GUI and vJass as well as possible. This then introduces, however, a problem where InitTrigs will initialize before vJass - but this isn't a problem in GUI - only in custom vanilla JASS script. The solution is for any JASS initializer using an InitTrig to just switch to a scope/struct/library initializer.

    So, here's what I've got (along with a cute description):

    Code (vJASS):

        // The next lines of code exist because vJass initialization is an absolute pain in the ass
        // So this overwrites all calls in "main" instead of adding to them, so I have to use ExecuteFunc
        // I'm pretty sure I covered all the possible initializers which would be added to "main"
        // Any settings done in the "Map Properties - Options" will be overwritten, but there is a way to save them
        // Contact Bribe @ Hiveworkshop.com if you need any help or run into issues implementing this into your map.
        //! inject main
        call ExecuteFunc("InitSounds")
        call ExecuteFunc("CreateRegions")
        call ExecuteFunc("CreateCameras")
        call ExecuteFunc("CreateAllDestructables")
        call ExecuteFunc("CreateAllUnits")
        call ExecuteFunc("CreateAllItems")
        call ExecuteFunc("InitUpgrades")
        call ExecuteFunc("InitTechTree")
        call InitBlizzard()
        call ExecuteFunc("InitGlobals")
        call ExecuteFunc("InitCustomTriggers")
        //! dovjassinit
        call ExecuteFunc("RunInitializationTriggers")
        //! endinject
     


    Of course the user has to then extract any kind of Map Options that get killed by the //! inject call, so I'm going to offer mapmakers the self-service of me tooling their Map Options into raw JASS for them to circumvent that issue.
     
    Last edited: Nov 15, 2018
  2. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    895
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    Hey Bribe!
    I don't fully understand what you're trying to ahieve. However initialization order is
    Code (vJASS):

    function main takes nothing returns nothing
        call SetCameraBounds(- 3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), - 3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), - 3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), - 3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM))
        call SetDayNightModels("Environment\\DNC\\DNCLordaeron\\DNCLordaeronTerrain\\DNCLordaeronTerrain.mdl", "Environment\\DNC\\DNCLordaeron\\DNCLordaeronUnit\\DNCLordaeronUnit.mdl")
        call NewSoundEnvironment("Default")
        call SetAmbientDaySound("LordaeronSummerDay")
        call SetAmbientNightSound("LordaeronSummerNight")
        call SetMapMusic("Music", true, 0)
        call CreateAllUnits() //preplaced units created, vars like gg_unit_Hgam_001 are set up.
        call InitBlizzard()

        call ExecuteFunc("TEST___Init") //library initialization "Init" function

        call InitGlobals() //sets GUI globals acording to "Variables" Ctrl+B
        call InitCustomTriggers() // sets trigger vars: gg_trg_xx, adding actions to the gg_trg_xx trigger
                                    // may be triggers with event "UnitIndexEvent"
        call RunInitializationTriggers() //runs trigger actions with GUI event "map initialization"

    endfunction
     


    if you want to fire library Init function after
    RunInitializationTriggers()
    there's a way (just tested)
    Code (vJASS):

    library LIBTEST initializer Init
       
        private function UnitEventInit2 takes nothing returns nothing
            call BJDebugMsg(".library_proceed=true. "+I2S(R2I(udg_GUI_Real)))//prints "5"
        endfunction
       
        private function RunAtMapInitGUITrigger takes nothing returns nothing
            local timer t=GetExpiredTimer()
            if gg_trg_t4Config==null then
                call BJDebugMsg("waiting..")
            else
                call PauseTimer(t)
                call DestroyTimer(t)
                call UnitEventInit2()
            endif
            set t=null
        endfunction

        private function Init takes nothing returns nothing
            call TimerStart(CreateTimer(), 0.00, true, function RunAtMapInitGUITrigger)
        endfunction
       
    endlibrary
     

    t4Config.jpg
    maybe 0sec timer without checking if gg_trg exists will work. It didn't print "waiting.." even once for me.
     
  3. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,819
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    So the biggest issue is that the InitGlobals and InitTriggers lines are placed after the //! dovjassinit line which is what runs things like your library init. This means using GUI configuration is only possible if one either:

    1) uses ExecuteFunc to run the configuration action code

    2) waits until the Init code has run, a la Map Initialization (ZiB this runs before the zero second timer has expired).

    The issue with option 1 is that the InitGlobals call will later wipe out the GUI settings if called directly from a vJass trigger.

    Option 2 has the problem that all vJass Initialization would have to change their code.

    I have made GUI Unit Event fully vJass with a GUI Configuration, but it's difficult to figure out when to initialize, because launching UnitIndexEvent before InitTrigs have run means every single one won't fire.

    So really a HYBRID solution is needed. Initializing exactly the way vJass indexers do via a phase 1, using ExecuteFunc to configure things like the unit type, but then also running a phase 2 initialization for those GUI triggers or whatever else that missed phase 1.

    See how screwed up this all is? JassHelper was never written with the intention of supporting GUI, and goes out of its way to screw it up during Initialization. It's fixable with this //! injectmain, but requires tweaking to get back global fogmodifiers, music, sound, etc. So it puts a lot of probably too complicated work on the map maker, also breaking my philosophy of bare minimum implementation steps.

    So ultimately this resource will not be used. I just had to get my thoughts out. I'm doing the leg work to make sure no one else has to screw around with their code. My new releases will be almost entirely backwards compatible, despite changing a lot behind the scenes.

    This means:

    1. fewer variables clogging up the variable manager.

    2. Knowledge of which variables can be accessed in GUI (means no more burying variables with obscure names to keep people away).

    3. Unit Event can actually supplant all vJass unit indexers from day 1, meaning not even vJass people have to change their code.

    4. Finally GUI and vJass can have the same dependencies without breaking anything.

    5. You know I like compatibility. I've made Damage Engine compatible with all other GUI damage systems. I made an AutoEvents and AIDS plugin for Nestharus' Unit Indexer.

    6. I'm taking the release very slowly. Whenever I'm conflicted, I stop what I'm doing for a day or 2 and look at it again with a fresh mindset. This may get done in December, or it may get done next year.

    7. Whatever improvements come with War3 Reforged, I intend to have everything working for the systems which currently could benefit. We'll see what the future holds.
     
  4. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,420
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    //! inject sounds too painful.

    In your case, I'd go with solution 2. vJass users are a lot more used to reading docs and figuring out how to set up a system. Make it easiest for GUI. About "when" to run those triggers--my memory is suuuuper foggy but can't you do something like this?

    GUI - Map Initialization
    -> start thread to a separate function (e.g.
    ExecuteFunc
    or
    ForForce
    )
    -> in that function, fire some sort of event that vJass users can use to do initial setup

    iirc, when an event like map initialization fires, it goes through each trigger in the queue and evaluates it. That queue is blocking, so when you start a new thread, it gets scheduled at the end of that queue. That way you'll know it comes after all map initialization triggers have finished, but without having to use a 0-second timer.

    ... i could be totally wrong though.
     
  5. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,819
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Any sort of queued wait would actually be tacked on at the 0 second mark. Once the "main" thread completes, the map loading is done and those threads are long gone.

    If one does ExecuteFunc, TriggerEvaluate, TriggerExecute or ForForce, it is going to run instantly (that is, holds the current thread in order to complete). The difference with ExecuteFunc/TriggerExecute is that if you do a TriggerSleepAction, it zaps you back to the original thread instead of making all threads wait.

    I think this all worked differently in the distant past, but by the time I got into modding (a couple months before joining Hive) things like the returm bug were pretty much done.
     
  6. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    895
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    Does it mean you will split UnitEvent into 2 parts?
    By the way: having pre-placed units indexed as soon as possible will be nice change (at the moment when library Init function runs). And, maybe, firing UnitIndexEvent should happend when all "MapInitialization" triggers executed.

    While testing I realized that DeathEvent==1 fires twice (don't know why)
    Also it doesn't fire in some circumstances:
    killevent.jpg
    This peasant won't fire DeathEvent cause UnitEventConfig is executed after.
    Do you know a way to detect when all "Map Initialization" triggers ran?

    As for //!inject I share Purge's opinion.

    edit: If I understand it right: UnitEvent is independant in position where it is placed vs other "Map Init" triggers by:
    using
    Filter
    for Unit Enters Region, and Unit Issued Order which fires before an event itself.
    But this doesnt work for Unit Dies event (as you're using Trigger Condition). Probably that's why DeathEvent does't fire in above example.
     
    Last edited: Nov 22, 2018
  7. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,819
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    In the trigger you showed you have to use the UnitIndexEvent Equal to 3.00 event. It is in the disclaimer that Map Initialization doesn't guarantee it has run.