Unexplainable Data structure with index does not exist

yira

Member
My first post hopefully of many. I'm very new to this, but have made good progress over 3 months or so, and with luck I hope to finish an ancient world-based Rimworld-like game, one day, and hopefully contribute to the community, as I think GMS is awesome (most of the time).

However I've begun to encounter errors that I search high and low, and for which I cannot find an explanation.

I've been staring at this for my whole Saturday:

My house/building objects (oCustomHouse) are built at beginning of game (3 of them for development's sake), and can be built by the player, like in an RTS.

They each have their own inventory, as a ds_grid made in the house's create step, indexed to instance variable (houseInventory) as they can contain food and other resources.

GML:
var rcsv = global.Resourcescsv;
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));

All fine, my peasants are happily moving resources to the houses, until I realise that for some reason, with the newly built houses, the houseInventory ds_grids seemingly disappear instantly on creation of the object. Or I lose their index, whichever way you look at it. This leads to 'Data structure with index does not exist' errors. I narrow it down, and the ds_grid_create is returning 0. The three built by the game will return 24, 25, 26, or 56,57,58 for example, it's different each playthrough. Then the custom built ones come with 0, 0, 0 recurring.

The strange thing is, and this echoes a couple of other weird errors I've had, I can force it to work by doing this:

GML:
var rcsv = global.Resourcescsv;
throwawaygrid1 = ds_grid_create(0,0);
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
Now it's fine, and the returned indexes will come back as e.g. 32, 34, 36 and then, happily, 38, 40, etc.

But I definitely don't want to build this game on shaky foundations, so if anyone has encountered something like this, I'd love to know. Again, I've searched as hard as I can...

Thanks.
 

Roldy

Member
Hi,

the indices for data structures begin at zero. So ds_grid_create returning 0 is returning a valid indices. As well, indices for data structures are reused. So if you destroy a grid that had indices 10, then 10 may be returned when next calling ds_grid_create.

With that being said. What is the value of houseInventory right after creation?

GML:
var rcsv = global.Resourcescsv;
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
show_debug_message(string(houseInventory)); // Print the index
If you create three and they all have the index zero, my first assumption is somewhere you keep destroying ds_grid with index zero. If your solution of making a throw away grid does indeed work then that would reinforce that assumption.

e.g.

  • The throw away grid gets index zero,
  • the real grid gets some other index
  • At some point index 0 is destroyed
  • so the next throw away grid gets index zero
  • etc..
Use Ctrl + Shift + F and search everywhere for ds_grid_destroy and put something like:

GML:
// Right before each ds_grid_destroy output some debug text
show_debug_message(debug_get_callstack(1)[0] + ":: calling grid destroy on index " + string(theIndexBeingDestroyed));

if (theIndexBeingDestroyed == 0) {
    var _breakHere = 1; // Put a break point in the debugger here so you can look through the call stack and start back tracking
}

ds_grid_destroy(theIndexBeingDestroyed);
See if that helps narrow it down at all.

Good luck.

P.S. Most likely you are erroneously destroying index 0. If you are using '0' to indicate a grid that has been destroyed, don't, instead use 'undefined'.

e.g.
GML:
ds_grid_destroy(myGridIndex);
myGridIndex = 0;  // Don't do this in place of null

ds_grid_destroy(myGridIndex);
myGridIndex = undefined; // Do this as it is analog with null
 
Last edited:

yira

Member
Hi,

the indices for data structures begin at zero. So ds_grid_create returning 0 is returning a valid indices. As well, indices for data structures are reused. So if you destroy a grid that had indices 10, then 10 may be returned when next calling ds_grid_create.

With that being said. What is the value of houseInventory right after creation?

GML:
var rcsv = global.Resourcescsv;
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
show_debug_message(string(houseInventory)); // Print the index
If you create three and they all have the index zero, my first assumption is somewhere you keep destroying ds_grid with index zero. If your solution of making a throw away grid does indeed work then that would reinforce that assumption.

e.g.

  • The throw away grid gets index zero,
  • the real grid gets some other index
  • At some point index 0 is destroyed
  • so the next throw away grid gets index zero
  • etc..
Use Ctrl + Shift + F and search everywhere for ds_grid_destroy and put something like:

GML:
// Right before each ds_grid_destroy output some debug text
show_debug_message(debug_get_callstack(1)[0] + ":: calling grid destroy on index " + string(theIndexBeingDestroyed));

if (theIndexBeingDestroyed == 0) {
    var _breakHere = 1; // Put a break point in the debugger here so you can look through the call stack and start back tracking
}

ds_grid_destroy(theIndexBeingDestroyed);
See if that helps narrow it down at all.

Good luck.

P.S. Most likely you are erroneously destroying index 0. If you are using '0' to indicate a grid that has been destroyed, don't, instead use 'undefined'.

e.g.
GML:
ds_grid_destroy(myGridIndex);
myGridIndex = 0;  // Don't do this in place of null

ds_grid_destroy(myGridIndex);
myGridIndex = undefined; // Do this as it is analog with null
Okay, I finally put some time aside to digest all of this properly. So, I implemented or adapted all your instructions. The project has moved on pretty far from 3 months ago, but I can still reproduce similar errors when I take away my 'throwaway' ds_grids.

Here's some output showing what I've got now. (For context this is a few mins into a game, a few hundred lines down)

A houseInventory is created (with maybe the 7th house/building object I've made). This time it gets index 255.

However, an unrelated script belonging to a human then destroys one of its grids, as it is programmed to do regularly. ds_grid_destroy_blank is what I'm using for all ds_grid destroying, and it both prints the debug messages, and makes the variable undefined. humanCarrying:192 destroys the ds_grid 'wntr' (who needs this resource?) humanCarrying:86 destroys 'ticc' (things I could carry).

So it looks like somebody's 'ticc' gets 255, that's destroyed, then a houseInventory gets 255, then someone's pre-existing 'wntr' which is also 255 is destroyed!

Why, then, would my grids be sharing indexes with each other? Is there a well-known programming term for this problem, that would help me search for a solution?

1645082895291.png

I don't know what would be useful to share exactly, but the grid creation is normal presumably.


GML:
//a few extracts of code that may be relevant, not sure what else would be relevant, but please let me know.

//oParentBuilding create step c. line 70
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
show_debug_message("houseInventory created of "+string(id)+", "+string(houseInventory));


//script humanCarrying line 21
ticc = ds_grid_create(2, 1);

//script humanCarrying c. line 86

if ds_exists(ticc, ds_type_grid) {
    ds_grid_destroy_blank(ticc);   
}


//script humanCarrying c. line 150
if variable_instance_exists(id,"wntr") and ds_exists(wntr,ds_type_grid) ds_grid_destroy_blank(wntr);
wntr = ds_grid_create(4,1);

//script humanCarrying c. line 192
ds_grid_destroy_blank(wntr);
I would also add that all of the grids, and the game generally, are working great, most of the time. And I've been able to patch over this by making all these throwaway grids, and I can play for hours without apparent problems.

Code:
throwawaygrid1 = ds_grid_create(1,1);
throwawaygrid2 = ds_grid_create(1,1);
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
houseInventory = ds_grid_create(ds_grid_width(rcsv),ds_grid_height(rcsv));
show_debug_message("houseInventory created of "+string(id)+", "+string(houseInventory));
But for our purposes I'm removing all of these and only doing 1 line of houseInventory = ds_grid_create to reproduce this bug and tackle the issue.

If it's bad etiquette to resurrect this thread, I can make a new one too.

Thanks!
 

Attachments

TheouAegis

Member
Why are ou destroying your grids rather than just clearing them? It's not like the instance has stopped using it -- you recreate it immediately afterward. I think if you create your grids in the Create events and only in the Create events, this issue should disappear.

Also, the game crashed on an "out of bounds" error, saying you were trying to write to column -1. It didn't say the grid didn't exist. In fact, the grid has a size of 2x8. That's a different error all together.
 

yira

Member
My apologies, I forgot to illustrate the crash itself. The out of bounds is probably related, but the crash itself usually looks like this:

Code:
ERROR in
action number 1
of Alarm Event for alarm 3
for object oFarm1:

Data structure with index does not exist.
at gml_Script_ds_grid_v_lookup_add (line 8) -        var yy = ds_grid_value_y(grid,0,0,0,ds_grid_height(grid),value);
############################################################################################
gml_Script_ds_grid_v_lookup_add (line 8)
gml_Object_oFarm1_Alarm_3 (line 16) -        ds_grid_v_lookup_add(houseInventory,1001,2,8); //column 2: max capacity

As for why I'm destroying instead of clearing... it's a good point. If I recall correctly, I had tried replacing my destroys with clears, and issues persisted. Then it seemed like I ought to learn how to avoid this, for my own learning, and in order to build the game on strong foundations, as it's pretty complex and only getting more so. Clearing then seemed like a workaround rather than a fix.
 
Top