How to manage unreliable Table Objects?

Basically, I have a group of characters in a table. Let’s call them shoppers.

  1. They buy something, and then at some point they disappear.
  2. Once they are offscreen I remove them from the table as they are not supposed to come back.
  3. However, I have a pause function. It works pretty simply, like this:

for i = 1, #shoppers do shoppers[i]:toggle() -- pause or unpause depending on the game end

  1. As you can see, it just iterates through the table, pausing each customer.
  2. The problem is, a customer could be removed from the table while this is happening. So suddenly 1, #shoppers is no longer reliable and it toggles a customer twice. Or skips one. Or crashes because inside the toggle code it’s referring to [i] which no longer means what it says it means.

Is there any sort of strategy people use for stuff like this? Ideally I would love to halt the table removal until after the pause completes, but I don’t see any useful way to tell if something like that is in progress. The best I can come up with is:

local maxshoppers = #shoppers for i = 1, #shoppers do if shoppers[i].paused == false then shoppers[i]:toggle() end end if #shoppers ~= maxshoppers then -- do the same thing over again end

…but all that solves is making sure everything pause/unpauses. It doesn’t solve the problem of it addressing the wrong objects.

Any ideas or approaches you’ve used would be awesome!
[import]uid: 41884 topic_id: 23356 reply_id: 323356[/import]

The first thing you should do is go through your table *backward*.

for i=#shoppers,1-1 do  
 --stuff  
end  

That will eliminate any out-of-bounds index issues or skips. [import]uid: 44647 topic_id: 23356 reply_id: 93572[/import]

That’s a great idea! Thanks toby2. I’ll try that and see if it stabilizes some…

EDIT: It’s probably helping, but the core problem is still there. When a character gets removed from the list, if it gets removed when pause is called, things go to hell and characters start moving backwards…

I guess I better explain the basic structure of my code, maybe it’s a bloody stupid method and somebody has a better idea…

  1. I have a spawner that calls shopper creation code every few seconds randomly and adds that new guy/gal to a table.

  2. As you might imagine I have a pause button. :slight_smile: When it’s pressed, I do two things:

local function pressPause(event) if event.phase == ended then spawner:toggle() -- toggle the spawner on/off regardless if pause == true then for i = #shoppers, 1, -1 do -- iterate for the shoppers shoppers[i]:toggle( function() customerStep[nextstep](shoppers[i]) end) -- a) this toggles shoppers to stop or go. -- b) but if it's time to go, I pass the event they need to carry out, which happens to be the functions customerStep[x]() -- c) but I have to pass a subject for that function, so I have to add shoppers[i] into the argument for the function-within-a-function. end end

customerStep{} looks a little like this:

customerStep[2] = function(person) person:move(3, customerStep[3](person) --he will move to waypoint #3 with a transition.to command. --the second argument says what to do when he gets there. person.step = 2 --Saves progress. If I pause the game, I have to cancel the transition, and knowing this step tells me which customerStep{} function to restart the transition with. end

The trouble is, I guess sometimes when you unpause:

  1. customer gets toggled back on
  2. At that point it’s instructed to move using the [lua]nextstep[/lua] variable I generate (tells it what table index to use)
  3. As part of that step it’s given [lua]shoppers[i][/lua] as the target, which I’m guessing can no longer be considered reliable information.

Ideally I would just localize it all and refer using ‘self’, but the number of things happening per step extends outside the bounds of the character… [import]uid: 41884 topic_id: 23356 reply_id: 93599[/import]

How are you removing the character from the table? if you are just using nil to do so, then you will have an issue as that forms a blank and the iterations stop where the blank is found.

So you will have to have the array as just an index which will then refer to the shoppers separately, so that the index #3 in your table will refer to nothing but the third shopper, which could be anyone of the shoppers.

Hope you are getting that little difference by using it this way. [import]uid: 3826 topic_id: 23356 reply_id: 93657[/import]

Well, I’m using a metatable structure, so it’s a two part process.

function shopper:delete() print("Removing the shopper") display.remove(self.vis) self.vis = nil self = nil end --/customer:remove()

That’s my shopper removal code. I then destroy the shopper with this:

customerStep[12] = function(target) -- Remove the customer table.remove ( shoppers, table.indexOf( shoppersTable, target )) target:delete() target = nil print("[DEBUG] #shoppersTable", #shoppersTable) end --/customerStep[12]()

The print statement confirms that the table is not growing out of control.

Not removing from the table is interesting but causes the iteration problem and I need something that can clean itself up, so I didn’t think that seemed feasible…?
[import]uid: 41884 topic_id: 23356 reply_id: 93666[/import]

I know the iPad 3 stuff is exciting…but if anybody has any ideas here I’d love to hear them. Or maybe the solution is to localize everything into their own lua files…hrm. [import]uid: 41884 topic_id: 23356 reply_id: 93898[/import]