Warcraft III: MapsModelsSkinsIconsSpellsToolsTutorials
WC3 JASS: Code SnippetsJASS and vJASS Spells and SystemsJass Tutorials
Chat @Discord

Author Topic: Structs For Dummies  (Read 8438 times)

  • Awesome Global Code Moderator
  • Recognized User
  • Rookie - level 2
  • *
  • Posts: 84
  • WC3 Models: 0
  • WC3 Tutorials: 0
  • WC3 Tools: 0
  • WC3 Maps: 0
  • WC3 Skins: 0
  • WC3 Icons: 0
  • WC3 Spells: 0
  • Reputation: 516
  • vJass Incarnate
    • View Profile
Structs For Dummies
« on: December 28, 2011, 09:38:29 AM »
Structs For Dummies

In this tutorial, I'm going to try to explain the concept of exactly What a struct is and why we use structs.

Table of Contents
  • What is a struct
  • Why do we use structs
  • Important methods
  • Difference between Static Methods and Methods
  • Struct extends array
  • Struct Variables
  • Thistype Keyword
  • Operators
  • Allocation/Deallocation
  • Modules
  • Initialization
  • Prevent Variable Modification
  • Keyword Keyword
  • Misc Tips

What is a struct
I'm going to be as basic as possible:
  • A struct defines a new type
  • A struct can have 8190 instances (Will explain later)
  • A struct can be treated like any other type (unit, location, etc...)
Just think of structs as virtual objects you can create and destroy.

Why do we use structs
Structs make everything easier.
I'm assuming you're a GUIer. Have you ever created a spell that uses index recycling methods?
If yes, you'd know how unreadable and ugly index recycling methods are.

With structs, you can think of spell instances as simple "objects".
These objects can contain data, and they can manage that data with
functions known as "methods".

Important methods
There are only 5 methods I want you to know:

The Allocate method is used to allocate an instance:
Code: jass  [Select]
  1. static method allocate takes nothing returns <Name_of_Struct>
  2.     // bla bla bla
  3.     // This method is automatically generated in each struct by JassHelper
  4.     // You just need to call it once inside the "create" method.
  5. endmethod

The Create method is like an extension to the allocate method. It calls allocate, then initializes some data.
You have to create this method yourselves.
Code: jass  [Select]
  1. static method create takes nothing returns <Name_of_struct>
  2.     local <Name_of_Struct> this = <Name_of_Struct>.allocate()
  3.     // do some init stuff
  4.     return this
  5. endmethod

The onInit method can be very useful
I'll explain later
Code: jass  [Select]
  1. static method onInit takes nothing returns nothing
  2.     // anything you put inside here will run at map initialization
  3. endmethod

The deallocate method is... I already explained that below :P
Code: jass  [Select]
  1. method deallocate takes nothing returns nothing
  2.     // bla bla bla
  3.     // This method is like allocate, but it deallocates an instance
  4.     // it's automatically generated by JassHelper, so you don't need
  5.     // to write it. Only use it in the "destroy" method
  6. endmethod

The destroy method is a must. You should write it yourselves.
It's like an extension to deallocate. It nulls all the handles in the struct, and deallocates the instance
Code: jass  [Select]
  1. method destroy takes nothing returns nothing
  2.     // null your variables
  3.     call this.deallocate()
  4. endmethod

These are very basic concepts.
destroy is always a method
create is always a static method
onInit is always a static method

There's another known method, but please, if you ever see someone use it, you
should flame them give them a link to this tutorial:

Code: jass  [Select]
  1. method onDestroy takes nothing returns nothing
  2.     // do stuff when you destroy
  3. endmethod

This method is bad because it generates useless functions.
Instead of creating this method that runs when you call destroy,
why don't you just write the destroy method?
Isn't that logical?

As you've noticed, there are 2 types of methods: static methods and methods
If you're curious to know the differences, scroll down.

Difference between Static methods and Methods
Static methods and methods have some differences:

Take the following example:

Code: jass  [Select]
  1. struct Pos
  2.     real x
  3.     real y
  4.     real z
  5.     method moveUnit takes unit u returns nothing
  6.         call SetUnitX(u,this.x)
  7.         call SetUnitY(u,this.y)
  8.         call SetUnitFlyHeight(u,this.z,0)
  9.     endmethod
  10.     static method create takes real X, real Y, real Z returns Pos
  11.         local Pos this = Pos.allocate()
  12.         set this.x = X
  13.         set this.y = Y
  14.         set this.z = Z
  15.         return this
  16.     endmethod
  17.     method destroy takes nothing returns nothing
  18.         // nulling
  19.         call this.deallocate()
  20.     endmethod
  21. endstruct

Difference #1:
The way they're used:
Code: jass  [Select]
  1. function hi takes nothing returns nothing
  2.     local Pos somePos = Pos.create(3,5,2)
  3.     call somePos.moveUnit(GetTriggerUnit())
  4.     call somePos.destroy()
  5. endfunction

As you can see, these are the formats:
Call static method:
Code: jass  [Select]
  1. call <NameStruct>.<NameStaticMethod>
Call methods:
Code: jass  [Select]
  1. call <Instance>.<NameMethod>

These are very simple concepts.

Difference #2:
How they work.
Static methods can be used directly referring to the name of the struct.
This means that they have to find the instance they are required to work with INSIDE the struct.
Methods are used referring directly to the instance. This means you don't need to specify the instance since it's already given.

While working with the instance inside a method, always assume it's called "this".

Static methods are not required to work with instances. They can just perform
simple procedures. On the other hand, since methods must take
instances outside the struct, they are REQUIRED to do something concerning
that instance.

Struct extends array
Here's a simple concept:
If a struct has no methods, just make it extend an array.
You can make absolutely ANY struct extend an array, but that would
require you to implement a dynamic indexing and recycling snippet.

Example:
Code: jass  [Select]
  1. struct someObj extends array
  2.     static unit u
  3.     static unit v
  4. endstruct

Structs that extend arrays are good because normal structs generate lots of trash code.
A struct that extends an array doesn't have an allocate/deallocate method.

To learn about writing efficient structs with dynamic indexing and recycling, click here.

Struct Variables
There are 2 types of variables in a struct:
Static variables and normal variables.

The differences between static variables and normal variables are exactly
the same as the differences between static methods and normal methods.

One new and important difference is that static variables are not unique to
each struct instance. In the following struct:

Code: jass  [Select]
  1. struct bla extends array
  2.     integer i
  3.     static integer j
  4. endstruct

Changing the variable j will affect ALL instances of the struct, while changing the variable i will only affect one instance.
You can think of j as a global that can only be accessed through the struct. (Because it is)
It isn't associated with any specific instance, but the actual struct itself.

Thistype keyword
What?
  Inside a struct, instead of referring to the name, you could just use the keyword "thistype"
Why?
  Because highlighting is awesome 8)
  Also, when you read about Modules later in this tutorial, you'll know that using this keyword
  is important because you're not always going to know the name of the struct that the module
  is going to be implemented into. If you don't get it, then come back to this section after you
  finish reading about modules, and I can guarentee that you'll understand it.


All of the very basic concepts have been covered.
If you're not THAT much of a dummy, you may continue to the bottom.



Operators

I'm sure you've seen people do something like this:

Code: jass  [Select]
  1. static method operator [] takes <something> returns thistype
  2.     return <Some_data_reffering_to_an_instance_of_the_struct>
  3. endmethod

Static method operators have 4 types:

[]       -> Takes a variable inside the brackets
[]=     -> Takes a variable inside the brackets and a variable after the "=" sign
==     -> Takes a different instance, and allows you to return a boolean for the evaluation
<       -> Same concept as the above

Assume the above static method operator is used:
Code: jass  [Select]
  1. function kill takes nothing returns nothing
  2.     local <Name_of_Struct> something = <Name_of_Struct>.allocate()
  3.     call <Name_of_Struct>[GetUnitId(GetTriggerUnit())].killHim()
  4. endfunction

GetUnitId is nothing really important for you to know about right now (It's a function in the UnitIndexer library)
killHim happens to be a method. You may have noticed that we referred to it using the Name of the Struct. This is because the static method
operator returns an instance of the struct.

These operators allow us to use not only integers, but units, locations, handles, etc....

This makes them VERY useful.

Method operators are the same as static method operators.
The differences between method operators and static method operators
are the same as the differences between static methods and methods.

Here are some examples:

Code: jass  [Select]
  1. struct Unit extends array
  2.     unit u
  3.     method operator hp takes nothing returns real
  4.         return GetWidgetLife(this.u)
  5.     endmethod
  6.     method operator hp= takes real r returns nothing
  7.         call SetWidgetLife(this.u,r)
  8.     endmethod
  9.     // allocation/deallocation and other shit down here
  10. endstruct

You're probably asking this: Why don't we just use variables instead of method operators?

Keeping a variable like hp inside the struct and constantly updating it is really stupid.
The method operator allows you to directly return the "GetWidgetLife" native.

"hp=" is a useful method operator too.

The above example is basically a simple API (Alternate Programming Interface)
Using things like "hp" or "hp=" makes your code look pretty good :P


Code: jass  [Select]
  1. struct someOtherThing extends array
  2.     unit u
  3.     static method operator [] takes unit v returns thistype
  4.         return GetUnitId(v)
  5.     endmethod
  6.     static method create takes unit v returns thistype
  7.         local thistype this = GetUnitId(v)
  8.         set this.u = v
  9.         return this
  10.     endmethod
  11. endstruct

Just assume the presence of GetUnitId
This small template can be very useful when you're referring to instances
in a struct that are unique to each unit on the map.

[]= is also a very important operator.
Look at this Unit Data Struct:

Code: jass  [Select]
  1. struct UnitData extends array
  2.     unit u
  3.     integer data
  4.     static method operator [] takes unit v returns integer
  5.         return thistype(GetUnitId(v)).data
  6.     endmethod
  7.     static method operator []= takes unit v, integer i returns nothing
  8.         set thistype(GetUnitId(v)).data = i
  9.     endmethod
  10.     static method create takes unit v returns thistype
  11.         local thistype this = GetUnitId(v)
  12.         set this.u = v
  13.         set this.data = 0
  14.         return this
  15.     endmethod
  16. endstruct

This struct uses [] and []= operators to refer to unit data rather than the instances.
Why? I don't know, it all comes down to what looks good and what you prefer.

Allocation/Deallocation
This information can be very useful.
As I've said before, JassHelper generates 2 methods:
Code: jass  [Select]
  1. static method allocate takes nothing returns Name_of_Struct
and
Code: jass  [Select]
  1. method deallocate takes nothing returns nothing

Sometimes, we want to know if a certain struct instance already exists.
If yes, most of the time, we wouldn't want to create it again.
But how can someone detect a null instance?

Easy: A null struct instance equals 0.

Consider the following struct:

Code: jass  [Select]
  1. struct Blekh extends array
  2.     static method operator [] takes unit u returns Blekh
  3.         return GetUnitId(u)
  4.     endmethod
  5. endstruct

Assume each unit has a specific instance of this struct.

In order to check if an instance for that unit exists, we do this:

Code: jass  [Select]
  1. if Blekh[unit] == 0 then // is it null?
  2.     call Blekh.create(unit)
  3. endif

Simple, huh?

You can deallocate instances by setting them to 0, but that's really bad since the instance won't be recycled :)
Never do that. I repeat, NEVER do that!

Sometimes, instead of deallocate/allocate, you could create your allocation/deallocation methods :)
Here's an example: Alloc by Sevion


Modules
This concept is very simple:
A module is just a piece of a struct...
It can contain methods, static methods and variables.
Example:
Code: jass  [Select]
  1. private module SomeMod
  2.     static unit randomDude
  3.     static unit otherRandomDude
  4.     static method onInit takes nothing returns nothing
  5.         set randomDude = ...
  6.         set otherRandomDude = ...
  7.     endmethod
  8. endmodule
  9. private struct someThing
  10.     implement SomeMod
  11.     static method create takes unit u, unit v returns <Name_of_Struct>
  12.         local <Name_of_struct> this = <Name_of_struct>.allocate()
  13.         set randomDude = u
  14.         set otherRandomDude = v
  15.         return this
  16.     endmethod
  17. endstruct

As you can see, modules are 'implemented' into structs.
You're probably asking yourself: "So?"
Well, when a module is implemented into a struct, all of it's contents are copied into it.

Modules have several uses. One very important and common use is Initialization:

This is the format of Module Initialization.

Code: jass  [Select]
  1. private module Init
  2.     private static method onInit takes nothing returns nothing
  3.         // some Init stuff
  4.     endmethod
  5. endmodule
  6. private struct Inits extends array
  7.     implement Init
  8. endstruct

Initialization
Take:

Code: jass  [Select]
  1. library temp initializer Init
  2.     private function Init takes nothing returns nothing
  3.         // bla
  4.     endfunction
  5. endlibrary

and

Code: jass  [Select]
  1. private module Init
  2.     private static method onInit takes nothing returns nothing
  3.         // some Init stuff
  4.     endmethod
  5. endmodule
  6. private struct Inits extends array
  7.     implement Init
  8. endstruct

What's the difference?
These 2 initialization methods run in different orders.
Using the second sample as a template is a requirement if you're initializing important variables because it is executed first.
In libraries like Ascii, using the second method is important because if the functions included were called
during map initialization and the library used a common initializer, null values would returned. Anytime you have a library
that requires you to configure variables like in Ascii, use the second method. The first method can be useful for initialization
that involves creating a trigger, registering an event, registering a condition, etc...


Preventing Variable Modification

If you want to prevent people from modifying variables outside of a given struct, just use the "readonly" keyword
This will prevent variables from being modified OUTSIDE the struct. I repeat, OUTSIDE!!

Code: jass  [Select]
  1. struct LOL
  2.     readonly static integer hi
  3.     static method create takes nothing returns thistype
  4.         local thistype this = thistype.allocate()
  5.         set thistype.hi = 2 // this is ok
  6.         return this
  7.     endmethod
  8. endstruct
  9.  
  10. function hi takes nothing returns nothing
  11.     set LOL.hi = 4 // This will popup a compile error
  12. endfunction

The difference between readonly and private is quite simple:
- "readonly" prevents modification outside the struct (reading is ok, writing is not allowed)
- "private" prevents both modification and access outside the struct (reading and writing are not allowed)

"readonly" variables do not add any overhead to your code. They only tell JassHelper to stop users from
modifying data outside of a certain struct.

Keyword Keyword
Did you know there's actually a "keyword" keyword?
It's important because it allows us to access functions and variables regardless of the position in the library.

For example:

Code: jass  [Select]
  1. function hi takes nothing returns nothing
  2.     call A.create()
  3. endfunction
  4.  
  5. struct A
  6.     static method create takes nothing returns thistype
  7.         return thistype.allocate()
  8.     endmethod
  9. endstruct

This would cause a syntax error since the struct A is positioned below the function.
But, if we were to do this:

Code: jass  [Select]
  1. private keyword A
  2.  
  3. function hi takes nothing returns nothing
  4.     call A.create()
  5. endfunction
  6.  
  7. struct A
  8.     static method create takes nothing returns thistype
  9.         return thistype.allocate()
  10.     endmethod
  11. endstruct

Then the syntax error would no longer popup!

Misc Tips

  • Always destroy struct instances since they're limited.
  • Structs can extend other structs, but if you do that, then those structs will share the same instances and thus, you have a greater chance of reaching the 8190 limit.
  • If Struct A extends struct B, then struct A will be able to use methods and variables inside struct B (except private ones).
  • Struct instances can be treated EXACTLY like integers.
  • Always use "thistype" inside a struct instead of the name.
  • Timers can only have static method callback functions, making TimerUtils by Vexorian a very useful library. You can use SetTimerData to store the "integer"/"instance" in the timer to load in the static method.
  • Always use module Initializers for standalone libraries with important data.
  • Always use "this" to refer to the instance inside a method.
  • Always remember to deallocate instances in your destroy methods.
  • Structs are compiled to arrays. This is why they can only have 8190 instances at once.
  • 0 refers to a null struct instance

********************************************

There are lots of other things for you to know about structs.
This tutorial only covers the basic concepts. There are other
tutorials involving structs, but this one is particularly aimed at
Dummies.

If you feel that this tutorial isn't sufficient, feel free to post.

Here's a list of things I'm intending not to cover:
- Delegates (Not very imperative)
- Super (vJass Trash)
- Interfaces (vJass Trash)
- Stub Methods (Better off without them)
« Last Edit: January 14, 2012, 10:09:14 PM by moyack »

  • Site Owner
  • Administrator
  • Starter - level 4
  • *
  • Posts: 977
  • WC3 Models: 59
  • WC3 Tutorials: 13
  • WC3 Tools: 10
  • WC3 Maps: 12
  • WC3 Skins: 6
  • WC3 Icons: 2
  • WC3 Spells: 6
  • Reputation: 1153
  • Site Admin - I love fix things
    • View Profile
    • Blizzard Modding Information Center
Re: Structs For Dummies
« Reply #1 on: December 28, 2011, 09:19:31 PM »
The only thing missing in this tutorial is the table of content. It's really awesome!!!!!11
My mistake... it just need to be working... I'll create a bbcode section to this site to have an easy to use posting exprience :)

Congratulations :)
« Last Edit: December 29, 2011, 06:28:23 AM by moyack »

We can give you full hosting for your projects, a complete page!!

A custom altered melee map where you can play Naga and Demons. Check it out!!
Use Dropbox...

  • Site Owner
  • Administrator
  • Starter - level 4
  • *
  • Posts: 977
  • WC3 Models: 59
  • WC3 Tutorials: 13
  • WC3 Tools: 10
  • WC3 Maps: 12
  • WC3 Skins: 6
  • WC3 Icons: 2
  • WC3 Spells: 6
  • Reputation: 1153
  • Site Admin - I love fix things
    • View Profile
    • Blizzard Modding Information Center
Re: Structs For Dummies
« Reply #2 on: December 29, 2011, 07:30:52 AM »
I'm working in a jass syntax highlighting, you can check this thread to see how it work with other languages

Input code:
Code: [Select]
[code=jass]private function test takes unit u returns boolean
    return IsUnitType(u, UNIT_TYPE_DEAD) == true
endfunction
[/code]

Code: jass  [Select]
  1. private function test takes unit u returns boolean
  2.     return IsUnitType(u, UNIT_TYPE_DEAD) == true
  3. endfunction
« Last Edit: December 29, 2011, 07:43:40 AM by moyack »

We can give you full hosting for your projects, a complete page!!

A custom altered melee map where you can play Naga and Demons. Check it out!!
Use Dropbox...

  • Awesome Global Code Moderator
  • Recognized User
  • Rookie - level 2
  • *
  • Posts: 84
  • WC3 Models: 0
  • WC3 Tutorials: 0
  • WC3 Tools: 0
  • WC3 Maps: 0
  • WC3 Skins: 0
  • WC3 Icons: 0
  • WC3 Spells: 0
  • Reputation: 516
  • vJass Incarnate
    • View Profile
Re: Structs For Dummies
« Reply #3 on: December 29, 2011, 09:34:13 PM »
Yay :D
Jass tags! Those code tags were pretty ugly for Jass ;P
I'll update this tutorial and the other one to make use of the new tags.

edit
Updated this tutorial.
« Last Edit: December 29, 2011, 10:10:03 PM by Magtheridon96 »

 

coding efficient vjass structs

Started by nestharusBoard Jass Tutorials

Replies: 0
Views: 38
Last post April 14, 2012, 09:05:37 AM
by nestharus
Blizzard Modding Information Center Starcraft II Modding Information Center Wacraft III Modding Information Center WC3JASS.com - The JASS Vault Chronicles of Darkness - A Warcraft III mod Jetcraft - A Starcraft II mod Troll Smash - A Warcraft III Arena
  Mod DB - Change the Game Power of Corruption - A Warcraft III altered melee map Chaos Realm - The world of Game modders and wc3 addicts Follow us on Facebook!!