• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

vJass: Advanced Initialization

vJass: Advanced Initialization


Table of Contents
  1. Introduction
  2. Main and Config
  3. Manual Initialization
  4. Notes

Introduction
This tutorial will teach you how to control the order and manner in which code is initialized.

Generally vJass initializers are ran first, then custom triggers are ran after. However you can't pick the order by default.

I will show you how this is changeable and how you can also avoid relatively slow ExecuteFunc calls.

JassHelper (or JNGP) is required.

Main and Config
Every map has two core functions it runs on initialization (main and config).

Inside these functions contains a bunch of generated code that initializes things in your map like custom triggers (InitTrig_Function).

Typically in vJass you initialize functions by using theinitializerkeyword or by a structs onInit method.

Insidefunction mainis where JassHelper handles custom initializers by placing anExecuteFunccall for each one. For example

JASS:
library Lib initializer onInit
    private function onInit takes nothing returns nothing
    endfunction
endlibrary

// adds this to your scripts main function

function main takes nothing returns nothing
    //..
    call ExecuteFunc("Lib___onInit")
endfunction
Now, there's nothing wrong with this however maybe you want more control over how things are handled. Not to mention ExecuteFunc calls are significantly slower than normal function calls (*).

To do this we use the very convenient//! injectfeature which allows you to modify the config and main functions of your maps script. You can read more about the inject command in the JassHelper Manual.

Before we do anything, lets go over each function.

Main
  • This function is ran after the config function.
  • If you don't place dovjassinit inside the inject block then JassHelper will throw an error.
  • Responsible for calling functions that create the editors preplaced objects in your map (units, cameras, regions).

    JASS:
    //! inject main
    
        /* 
        *  dovjassinit places all vJass initializers. 
        *  This way you can control what's initialized before or after them.
        */
        //! dovjassinit
    
        // These initialize preplaced objects in the editor
        call CreateRegions()
        call CreateCameras()
        call CreateAllItems()
        call CreateAllUnits()
    
        // call InitTrig's
        call InitCustomTriggers()
    //! endinject

Config
  • This function is ran before the main function.
  • Does not allow dovjassinit (will throw error).
  • Requires SetPlayersandInitCustomPlayerSlotsfunction calls otherwise your map won't load. Normally the first argument of SetPlayers is the amount of players in the map, however it seems that any number works.
  • Responsible for defining start locations. If the function is overwritten and noDefineStartLocationcalls are made, thenGetPlayerStartLocationwill return null.

    JASS:
    //! inject config
        call SetPlayers(0)
       
        call DefineStartLocation(0, 704.0, - 832.0)
    
        call InitCustomPlayerSlots()
    //! endinject

Manual Initialization
We are going to modify "main", not "config" because injecting config doesn't allow dovjassinit.

For the sake of example, lets say you have a struct

JASS:
struct Data
    static method customInit takes nothing returns nothing
    endmethod
endstruct
and you want it initialized before everything else. We can do so like this:

JASS:
//! inject main
    call Data.customInit()
    //! dovjassinit
//! endinject
This proves useful for code optimization (faster execution time) and management.

If the function is scoped you can simply use the compilation name. You're also likely going to need to call the World Editor generated functions.

JASS:
library Lib
    private function onInit takes nothing returns nothing
    endfunction
    public function Init takes nothing returns nothing
    endfunction
endlibrary

//! inject main
    call Lib___onInit() // ___ private
    call Lib_Init() // _ public

    //! dovjassinit

    // These initialize preplaced objects in the editor
    call CreateRegions()
    call CreateCameras()
    call CreateAllItems()
    call CreateAllUnits()

    // call (non-vjass) custom triggers initialization functions
    call InitCustomTriggers()
//! endinject
There, you have full control over initialization.

Notes
  • JassHelper likely uses ExecuteFunc to avoid the thread crashing (OP Limit) or to be able to call functions from anywhere in the script.
  • There can only be one inject main/config block per map, so don't include it in public resources. It's meant to allow the map maker to have more control, not give resources initialization priority.
  • If you'd like you can use the config block instead of main for whatever reason.
  • If you're hitting the OP limit with your custom initialization you could split them up across each function.
 
Last edited:
Level 26
Joined
Aug 18, 2009
Messages
4,097
Do not combine inits of different features in the same thread. Splitting them up is entirely the point to render them independent. Not only because of the operation limit but there are other termination reasons. Else you will change one thing in the future and wonder why something else stops working. Yes, you can write custom initializers and a tutorial could show how to encapsulate the function calls in order to check what has been completed/failed to do so for example.
 
Level 18
Joined
May 11, 2012
Messages
2,103
I myself have no idea how to manipulate this trick(s).
But as Dysfunctional mentioned, I eould like to know how can I play music when the game is created (e.g. In lobby) and during loading time. Maybe play a sound when player joins?
Is this possible?
 
Level 3
Joined
Nov 18, 2013
Messages
21
JASS:
//! inject config
    call SetPlayers(1)
  
    call DefineStartLocation(0, 704.0, - 832.0)

    call InitCustomPlayerSlots()

    call PlayMusic("Sound\\Music\\mp3Music\\PH1.mp3")
//! endinject

Iirc, this can crash sometimes though.

I'm trying to play music in the lobby and it is working, but the problem is: for some reason, it is removing one force from the lobby. My map has 2 forces, and all the players from the second force are moved to the first force and thus the second force is removed. Why is this happening? How can I fix it?
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
The config is also controlling the player setup, in conjunction with the war3map.w3i that is. So just copying the above snippet would overwrite it. Take a look at the war3map.j script output that the WE generates without the custom injection, then take that version of the config (with dependencies ofc) and add the sound line.
 
Level 3
Joined
Nov 18, 2013
Messages
21
The config is also controlling the player setup, in conjunction with the war3map.w3i that is. So just copying the above snippet would overwrite it. Take a look at the war3map.j script output that the WE generates without the custom injection, then take that version of the config (with dependencies ofc) and add the sound line.
Worked. Thanks!
 

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,864
Apparently this doesn't work with cyrillic alphabet, with latest patch and local files enabled ofcourse.
Not that I have these issues, but I reported them from a friend.

Edit: I just realised I posted on the wrong thread... wtf. I meant to post in "Codeless Save and Load" by TriggerHappy lol
 
Last edited:
Level 2
Joined
May 30, 2021
Messages
5
hello, i am trying also to play custom music on lobby, and been experimentating for a few hours so far i found out that with:
call PlayMusic(preexisting war3 theme) it plays that theme on repeat
call PlayMusic("replaced preexisting war3 theme") plays the replaced war3 theme once
call PlayMusic("war3mapImported\\example1.mp3") plays example1.mp3 once (wich is an imported mp3)
call PlayMusic("war3mapImported\\example1.mp3;war3mapImported\\example1.mp") plays example1.mp3 once
call PlayMusic("war3mapImported\\example1.mp3;war3mapImported\\example2.mp") plays a random example once
is there a way to play an imported theme on repeat? or the only way would be to use a single and looped mp3 of what i wanna use? (wich would make the mp3 file a lot bigger and thats what i want to avoid)
edit: all of this is done / tested in patch 1.27b
 
Last edited:
Top