• Check out the results of the Techtree Contest #19!
  • Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

Nbr Instance Structs Allocated MAX

Hi everyone,

I have a problem with the number of current Structs instance at the same moment

I create 511 instance (with allocate) and after all that if I want to create an other one (with allocate) it seems that it gives me every time the 511th

n°510 => new
n°511 => new
n°512 => n°511 given
n°513 => n°511 given
n°514 => n°511 given
...

Its a default struct not the one that extends array

that struct has 39 attributes. each (2 arrays, 1 nested object)

is there a way to change that, or is there a number max of instance per stucts fixed by the game

Have a nice day, thnaks for your future help
 
By default, the max number of active instances for a single struct is 8190 (due to the old warcraft 3 array limit being 8192).

However, that max limit gets reduced when you use array attributes. Take this as an example:
JASS:
struct Example
    real array x[100]
endstruct

Depending on the highest array size you put (e.g. 100), the limit gets reduced by that factor. If the highest array size you have is x, then you can calculate the max number of instances you can have with: floor((8190 - x) / x). So in this case, you would have a maximum of (8190 - 100) / 100 = 80 instances. This has to do with the fact that normal struct attributes are ultimately just array variables under the hood (the "instance" is the integer index), so to support array struct attributes, they have to divide the array into chunks.

Here are some other examples:
  • real array x[1] => 8189 instance limit
  • real array x[2] => 4094 instance limit
  • real array x[16] => 510 instance limit
I'm guessing one of your attributes has an array size of 15 or 16, so you're probably running into that limitation.

One simple option is to specify a custom size limit:
JASS:
struct Example[10000] // the limit now becomes 99
    real array x[100]
endstruct

But I don't really recommend this in your case given how complex your struct is. If you use that feature, then under the hood, it'll basically generate a copy of the struct and all its variables for each additional 8190 members you need. So if you chose to have a limit of say, 80000, then it would have to generate at least 10*39 = 390 variables to support your struct.

It's also not particularly ideal since you still have to deal with a fixed limit. Instead, I recommend using a library like Table:

Here is a sample of how you could tweak the example above to instead use a "Table" instance, and you can essentially get the same functionality:
JASS:
struct Example
    Table x

    static method create takes nothing returns thistype
        local thistype this = thistype.allocate()
        set x = Table.create()
        return this
    endmethod
endstruct

private function Init takes nothing returns nothing
    local Example example = Example.create()
    set example.x.real[0] = 3.14
    set example.x.real[1] = 6
    set example.x.real[2] = 9

    call BJDebugMsg(R2S(example.x.real[0])) // prints 3.140
    call BJDebugMsg(R2S(example.x.real[1])) // prints 6.000
    call BJDebugMsg(R2S(example.x.real[2])) // prints 9.000
endfunction

That way, you can slap as many values as you want and you don't have to really worry about the code that gets generated under the hood (and you get to keep the limit at 8190 :)).
 
You could also use custom allocators, such as Global Alloc, which was designed for newer patches, for your structs.
 
Thanks for your very precise answer. It was very interesting

Is it possible to store a struct instance in a hashtable (the handle)

local MyStruct m = MyStruct.allocate()

If I want to store 'm' in the example above.
yep! Structs references are actually just integers under-the-hood, so you can store them as you would a regular integer. Here is an example:
JASS:
globals
    hashtable ht = InitHashtable()
endglobals

struct MyStruct
    string name
endstruct

private function Init takes nothing returns nothing
    local MyStruct a
    local MyStruct b

    set a = MyStruct.create()
    set a.name = "Bob"

    // Save the struct the same way you would an integer
    call SaveInteger(ht, 0, 0, a)
  
    // Load it the same way too
    set b = LoadInteger(ht, 0, 0)

    call BJDebugMsg(b.name) // prints Bob   
endfunction

Or with Table:
JASS:
globals
    Table t
endglobals

struct MyStruct
    string name
endstruct

private function Init takes nothing returns nothing
    local MyStruct a
    local MyStruct b
   
    set t = Table.create()

    set a = MyStruct.create()
    set a.name = "Bob"

    // Store it in the table
    set t.integer[0] = a

    // Load it from the table
    set b = t.integer[0]   

    call BJDebugMsg(b.name) // prints Bob   
endfunction

You could also use custom allocators, such as Global Alloc, which was designed for newer patches, for your structs.
good point--but just note that in order to use those custom allocators, you need to make your struct extends array, which prevents you from having array attributes (which he seems to have in this case).
 
Thanks for all your answers, you're very reactive

I used that " local thistype po = thistype(num)" to create some struct, it works at some points allow me to create the good amount of struct but causes random bugs

what is the difference with a " local thistype po = thistype.allocate()"
Whenever you want to make a new instance of a struct, you should typically use thistype.allocate() or .create(). That way, vJASS will be able to keep track of which indices are "available" vs. which ones are currently being used. Then when you call thistype.deallocate() or .destroy(), it'll correctly mark those indices as available again.

Keep in mind that structs are just arrays:
JASS:
struct Example
    real x
    real y
endstruct

// under-the-hood, this is:
globals
    // used for .allocate() and .deallocate() to keep track of which indices are free/used
    constant integer si__Example=1
    integer si__Example_F=0
    integer si__Example_I=0
    integer array si__Example_V

    // the actual attributes
    real array s__Example_x
    real array s__Example_y
endglobals

And the struct "instance" is just an integer that serves as the index for that array.

So technically, using thistype(num) can "work" since you're just choosing a specific integer index to use for those arrays, but vJASS won't actually be aware that you're using that index. This can cause all sorts of bugs:
  • You need to make sure "num" is unique, otherwise you may end up overwriting data that is meant for a different struct instance.
  • If you call .create() or .allocate() for that struct elsewhere, it might end up choosing a number you already used--again, causing data to be unexpectedly overwritten.
So in your case, I recommend sticking with the usual allocators/deallocators!



That being said, there's still a place for using integers directly when you want to override the allocation/deallocation. For those cases, you'll typically mark the struct as extends array to disable the default allocator/deallocator. Then you can either add your own functions to generate and track indices used, or you can use indices directly. A common example is when you use a unit indexer (which just assigns every unit on the map a unique integer from 0 to 8191)--for those cases you might just end up using the IDs directly:
JASS:
struct Example extends array
    real x
    real y
endstruct

function TrackUnit takes unit u returns nothing
    local Example e = GetUnitUserData(u)
    set e.x = GetUnitX(u)
    set e.y = GetUnitY(u)
endfunction

But in general, those are usually for more niche purposes.
 
Back
Top