• 🏆 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 Fundamentals For Programmers

The Fundamentals of vJass

The purpose of this tutorial
- We have lots of people coming to the hive nowadays who know how to program in languages like
C++, C# or Java, but they don't know the fundamentals of vJass. This leads them to create spaghetti-code.

Convention

globalVariablesAreWrittenLikeThis
localVariablesAreWrittenLikeThis
structMembersAreLikeThis
structMethodsAreLikeThis
CONSTANT_VARIABLES_ARE_WRITTEN_LIKE_THIS
FunctionsAreWrittenLikeThis

Struct names can be written any way you like EXCEPT_LIKE_THIS.
Module names are just like Struct names.

This is not C#, so your create methods shouldn't be called "New", they should be called "create".
The recycling/deallocation method should be called "destroy".

If globals are only one-character long, feel free to call them something like: "W", "Q", "G", etc..

Inside a struct, when you create a local thistype called this, when referring to it's members, you could omit the this.

Concepts

Structs in vJass are compiled to create very ugly spaghetti-code.

They generate 2 functions:

JASS:
function Struct_allocate takes nothing returns integer
    // allocate some instance
    // return it
endmethod

JASS:
function Struct_deallocate takes integer i returns nothing
    // useless locals
    // useless Double-free protection
    // deallocate some instance
    // recycle it
endfunction

Showing you the actual code would make you cry..

Here's a way to avoid this.
Make a struct extend an array:
struct myStruct extends array
And add these 3 members:
JASS:
private static integer ic = 0 // instanceCount
private static integer ir = 0 // instanceToRecycle
private thistype rn // Recycle Stack

Allocate like this:
JASS:
local thistype this
// if we aren't going to recycle anything
if 0==ir then
    set ic=ic+1 // increase instance count
    set this=ic
else
    // we have an instance to recycle
    // we set the current instance to the recycled one
    set this=ir
    // we set the recycled instance to the one that proceeds it in the recycle stack
    set ir=ir.rn
endif

And Deallocate like this:
JASS:
// Add recycled instance to stack 
set .rn=ir
// Make next recycled instance the current one
set ir = this

Some people may argue against this and say that the speed difference is marginal, but I would have to disagree because you'd be losing:
- 2 function calls
- useless double free protection
- useless generated globals (trigger arrays, etc..)

________________________________
Now, let's move on to another concept:

vJass is very slow and function calls are very costly.
If you ever have the oppurtunity to write a one-line function, do it.
JassHelper inlines one-liners.

Also, to increase performance inside functions, follow these rules:
- If you have a function that returns a primitive type (integer, boolean, string), if you're using it more than once in a function, create a local to cache it.
- If you have a function that returns a handle type (unit, player, widget, etc...), if you're using it more than twice in a function, create a local to cache it.

________________________________
Here's another concept:

- When you pass a variable as a parameter, you will be passing a reference to that variable and not the actual variable itself.

In other programming languages such as C++, something like this:

Code:
void setToFive(int i) {
    i = 5;
}

would affect the variable i from where it was passed.
In vJass, the passed parameter will be unaffected.

Encapsulation

In vJass, we use the keywords:
readonly
and
private
for encapsulation.

Encapsulation is always important. If a user shouldn't have access to something, make it private.
If you want the user to access something but you don't want him to modify it, make it readonly (only applies to variables).

What to avoid

Most programmers sometimes rely on things like recursion to create algorithms.
Unfortunately, stuff like this is way too slow for Jass.

1- Never use recursion
2- Never iterate over tens of thousands of indicies.

If you want to iterate over tens of thousands of indicies, or even millions of indicies, use TriggerEvaluations:

JASS:
local integer i = 0
loop
    exitwhen i>100000
    call TriggerEvaluate(SomeTrigger)
endloop

Threads in Warcraft III have an operation limit (Known as the op-limit).
TriggerEvaluate resets this operation limit for the entire thread though.

Scopes vs. Libraries

Most people do this:
- Use scopes for spells
- Use libraries for systems

What I'd recommend is to do this:
- Use libraries everywhere
- Never use scopes

Scopes allow you to make implicit requirements which is a bad programming practice.

________________________________
That's pretty much it.
This is all you need to know about the fundamentals of vJass.

Thank you for reading.
~Magtheridon96
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
I have read this all in about 3mins, but went for a walk with dog and decided to re-read this once again. I must say that even if it's not big tutorial containing vast amount of informations, it has what it should to be approved and what more important: that stuff should help those who aren't experienced in vJass. If you're new, it's great if you start from fundaments since the further you get, the more things (requiring knowledge from previous level) appears. For sure thats knowledge which you should own before you move further (meaby not the part with increasing struct speed, negating the safety).

I was using libraries everywhere since I just loved fact to have everything up and sorted automaticaly. Thats fantastic that I'm not a anti-scope dumb, but there is something good in it ;S
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Inside a struct, when you create a local thistype called this, when referring to it's members, you could omit the this.

Even if i use it intensively i have to admit that's not really a good practice, that doesn't really make sense.
I wouldn't include it as a standard, more like an ugly personal convention in order to write code quickly.

Structs in vJass are compiled to create very ugly spaghetti-code.

Sure, but most of time that doesn't matter, plus the number of jass code lines, nor the number of "static" created objects (handles) don't matter in the script efficiency, just about the script map size (which is negligible in most cases).
So you make your code uglier and open the door for more bugs for quite nothing.

Here's a way to avoid this.
Make a struct extend an array:

You should say that forbid you to use array members (sure you can use a Table or whatever but it's less efficient).

Some people may argue against this and say that the speed difference is marginal, but I would have to disagree because you'd be losing:
- 2 function calls
- useless double free protection
- useless generated globals (trigger arrays, etc..)

Of course, but that doesn't change the fact that it doesn't matter in most cases.
And jasshelper should be edited, not vJass code of users ...

vJass is very slow and function calls are very costly.

Again, in most cases it doesn't matter.
Plz use a function if it's necessary (code modularity, duplication, readability)

If you ever have the oppurtunity to write a one-line function, do it.
JassHelper inlines one-liners.

Ok for the inlining (if you don't have to place the functions argument in a weird order) but the vJass inlining rule is really more specific, for example every argument must be use only one time and in the same order of their declaration, also the inlining occurs only when the debug mode is off.
Read the jasshelper documentation.

- If you have a function that returns a handle type (unit, player, widget, etc...), if you're using it more than twice in a function, create a local to cache it.

And then you will have an handle id leak when the handle will be destroyed (the function arguments are not concerned by the bug).
http://www.thehelper.net/forums/sho...n-the-native-CreateUnit?p=1373796#post1373796
It could be fixed with a preprocessor though, any volonteer ? xD


- When you pass a variable as a parameter, you will be passing a reference to that variable and not the actual variable itself.

In other programming languages such as C++, something like this:

Code:
void setToFive(int i) {
    i = 5;
}

would affect the variable i from where it was passed.
In vJass, the passed parameter will be unaffected.

No, no no ...

It all depends the nature of the argument, in jass integers are passed by value, and in C it's also the case by default (even if you can pass it by reference if you specify it with *).
I can't say it about C++, but i think that would be werid if integers are passed by reference by default.

While handles are passed by reference, even if we have some exceptions like handles when they are registred with a TriggerRegister... function, or a rect with native Region(Add/Remove)Rect.


If you want the user to access something but you don't want him to modify it, make it readonly (only applies to variables).

Sometimes an inlined function is better, for example it makes easier to add a debug later, you don't have to edit your whole code, just the function.
Also readonly doesn't forbid to destroy the object outside the scope (i mean the user should have this in mind)



JASS:
local integer i = 0
loop
    exitwhen i>100000
    call TriggerEvaluate(SomeTrigger)
endloop

Or use .evaluate() ?

Also you should add that jass is not multi threaded, we can open a new thread but it halts the current one.

Scopes allow you to make implicit requirements which is a bad programming practice.

I'm agree that is lame for public ressources but it's good enough for personal stuff.
 
Level 5
Joined
Jun 30, 2004
Messages
170
I don't get it. If you're so unhappy about how slow and ugly vJass is, why don't you just make your own modified script of Jass? That's why ZINC and stuff was created, wasn't it?
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
If you name globals/static variables, local variables and instance variables the same way, how are you gonna differentiate them?

Often, in a function, I want to precalculate a value before storing it in the struct like

JASS:
method bla takes integer a returns integer
    local integer b = ((a - 5) * a)

    if (b > 1000) then
        set this.b = b
    endif

    return (a - b)
endmethod

Now if I had not written this.b but b to set, the local variable would have been called. Do not want to search through the function for a variable with the same name. Only using the struct variable is bad, slower in reading and setting and as shown in the method above, you might not even want to set the struct var in the end/reset it.

I think I would want to know rather whether a variable is global than if it is constant, that is why I am identifying ALL_GLOBAL_VARIABLES_LIKE_THIS. Most variables are not constant anyway, it's not really relevant and a compiler could take care. There is no reason for me to manually declare constants.

Variables, functions/methods and scopes/libraries/structs/modules differ in naming context/word class, so it's okay if they bear the same case-form.

Not sure why you want create/destroy.

In contrast, the identifiers for your allocating are very ugly and it is no problem to enwrap this in a module/textmacro and implement it or make it a standard module by changing the compiler.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
This tutorial missed the main point of what I wanted to impress about "standards".

Controversial things are not to be included as standards.

What I meant by standards is more to do with presentation than efficiency.

After reading your tutorial, I wrote this up because this is what I meant:

JPAG
JPAG - JASS Priciples and Guidelines

If you are writing a script for yourself and have no intention of ever
letting the public have a look at it, then you can ignore this thread
and skip all of these principles and guidlines.

However, if you are building a map you want to submit for review,
building a spell or system to submit for public use, the following
guidelines will ensure that we have a uniform standard to adhere
to general readability.

The English language has certain syntax like sentences, commas
and paragraphs, and tends to follow a general syntax pattern in
printed works. The content of the writing is obviously very diverse
but the way it is presented is the same, even though there is tech-
nically "nothing to stop you" from screwing up your grammar and
writing your message anyway. It's just no guarantee that everyone
can make sense of it and/or be able to take you seriously, specially
if u rite lyk thiz.

These principles and guidelines help to do something similar for
writing your JASS code. There has been a standard in the past
for JESP, but this revives the topic and makes points more clear.
Henceforth we can refer to this tutorial format as the JPAG format.

Naming Conventions

Capitalization formatting was established by Blizzard when they
created the first seeds of JASS long ago. Since they have already
made so many natives in common.j and so many functions in
Blizzard.j it makes the most sense to adhere to their syntax.

FunctionNames start with an uppercase and each new word is
indicated by a new capitalitzed letter. FunctionName.
StructNames ModuleNames, LibraryNames and
ScopeNames should also follow this format.

CONSTANT_NAMES are all capital letters and each new word
is indicated by being seperated from the rest by an underscore.
TEXTMACROS are good to write in this format, though it is not
a requirement. "constant <type> CONSTANT_NAME", however,
must follow this format. Constant functions are an exception, as
they should still adhere to the FunctionName syntax.

variableNames and methodNames should begin with a
lowercase letter and each new word seperated by a new capitalized
letter. This is called camelCasing.

Also, short variable names are generally discouraged. A variable
should have a name that has some semblence to its purpose. If
it is for general purpose, simply writing "data" or "value" is only
fine if there is no other "general purpose" variable that might also
share such a name.

Indentation

Indentation is four spaces in JASS and every block is required
to be indented, with the rare exception of indenting the contents of
an all-encompassing library or scope because it's usually obvious
then.

Sometimes people make "exitwhen" on the same indentation as
"loop", or make "local" on the same indentation as "function". Both
ways are absolutely horrible and will be laughed at upon viewing.
Your resource won't be approved by me if it employs such silliness
or similar.

Documentation

A resource should document all of its public interface at the top
of the script, and if it has library requirements it should provide
a link to those requirements.

Obviously, in the JassHelper manual there are established
"scope" and "library" keywords. because it is controversial and
this is a global standard, I will only make the following clear:

If it is a resource that will not be required by others, for example
a spell or a fully-automatic system are not required by any other
library, it is OK if it is a scope as long as its requirements are
still linked to and declared
.

Obviously if it is to be required by others, containing any form
of public API, it needs to be a library.

Use your best judgment with outlining the public API at the top
of your library.

Configuration

If your resource has things that are able to be configured, like
spell duration or FX art path, damage amount or whether to even
deal damage, they need to be easily configurable either through
dedicated functions or through dedicated constants. Some other
creative approaches to configuring your resource are sometimes
also allowed, depending on a few factors like presentation, ease
of use and with a lesser emphasis on efficiency.

Initialization Priorities

This is not clear enough to most, but here is how the order of
initialization fires in the realm of JASS:

  1. Module Initializers
    JASS:
    module M
        private static method onInit takes nothing returns nothing
        endmethod
    endmodule
  2. Struct Initializers
    JASS:
    struct S extends array
        private static method onInit takes nothing returns nothing
        endmethod
    endstruct
  3. Library Initializers
    JASS:
    library L initializer Init
        private function Init takes nothing returns nothing
        endfunction
    endlibrary
  4. Scope Initializers
    JASS:
    scope S initializer Init
        private function Init takes nothing returns nothing
        endfunction
    endscope
  5. InitTrig_ Initializers
    JASS:
    function InitTrig_MyTrigger takes nothing returns nothing
    endfunction
  6. "Event - Map Initialization" GUI Initializers
    • MyTrigger
      • Events
        • Map Initialization
      • Conditions
      • Actions
    .
  7. 0-seconds game time (happens after map has finished loading)
    JASS:
    call TimerStart(CreateTimer(), 0, false, function OnLoad)

Rather than letting all initializers in one library or scope fire first,
initializers fire in that above sequence.

Because a modules initialize first, you should try to do everything
from within a module initializer if it initializes public API. Spells,
however, can get away library, scope, struct, InitTrig_ or Map
Initialization. A unit casting a spell from within an initializer may
bug your resource but it is too much to ask for everyone to use
module initializers for everything.

However, systems that create things which might be used by the
the public during initialization (such as a hashtable) need to use
modules to initialize, just in case. Its API is horrendous but it is
reliable.
 
Top