Not sure if this is the best place for this question, but as it’s asking for more of a discussion/opinion, why not…
My current game project a base building game (think Rimworld, Dwarf Fortress and the like) and the requirements for saving game state are fairly complex. I have most of the saving done, but I’ve arrived at saving the AI state and I want to learn anything I can from the experience of others.
The AI uses behavior trees. Saving the state of those fits well within my framework, except for a specific scenario that comes up several times. My path finding runs in a separate thread, so in my behavior tree I’ll queue up a request to the path finding system and then wait until that query returns (usually within a couple of frames). There are a couple of other systems that work in the same way: queue up a request, wait a few frames for the response, continue on.
If I’m in the “waiting for response” state and the player saves the game, when I go to restore this state we’ll never get a completed request because those aren’t saved as part of the state (nor will they be).
I can deal with this by doing some sort of “After Load” thing where I detect that I was waiting for a request to complete and just kick off that request again. This is fairly straightforward but seems a little messy and prone to error.
And as I type this another option comes to mind where I go into a “pending save” mode where we keep on running until there are no pending queries while not allowing any new queries, but that seems kind of ugly as well.
Anyway, has anyone run across this sort of scenario when trying to restore game state from a save? How do you handle this sort of thing?
Haven’t run into this scenario, but in this case the best idea would be probably to pause the game and wait for pathfinding requests to complete. (how slow is it if it needs separate thread?). This way you’ll have exactly the same state when you load the game.
Alternative behavior would be to save “waiting for request” as “needs to send pathfinding request”. Meaning upon load AI would re-fire pathfinding requiests. The issue here is that in this scenario game state upon loading it may be different compared to the state you saved, because pathfinding system may return different path.
It’s not particularly slow, but there could be a hundred or more entities asking for paths and they can be unpredictably long and complex since the player controls the base layout.
The “needs to send pathfinding request” thing is effectively the same as “I’ve just loaded and I’m in waiting for path state so I must re-query the path”, but is a more clear way of thinking about it so I’m strongly leaning in that direction. I don’t think we’ll get a different state though, or if we do it doesn’t matter - at the the time we saved we had “shrödinger’s path” if you will - didn’t know what it was going to be, so it doesn’t much matter what we get back on restore.
Edit: Pardon the programmer art, but this is 3000 entities doing pathfinding at 60fps…
I don’t have an answer for how to handle the saving, but a possible “gotchas” that I’d want to plan for would be making sure the “pending save” state can’t get stuck indefinitely because one of these other threads it is waiting on fails to return properly.
Depending on how your system is set up, this might require setting a hard time limit on the “pending save” state; though if you’re leaving without getting a proper return, you should have a way (as you suggested) of knowing your game needs to start a new call for that thread when the game save is loaded.
If such a situation does arise, I’d want the game to log it too. The thread not returning properly would be a bug I’d personally want to see tracked down.
Well, then you have your answer. There’s no other way (I can think of). Either you wait for a path or you save entity without path and flag it that it needs to request path.
I do think that reloading may result in different path being calculated (especially if your entities had collision avoidance) but I don’t think it’ll be that much of a big deal.
I probably wouldn’t bother with saving paths at all. I’d simply save the destination for each unit. Then have the whole simulation do a complete recalculation of all of the paths during loading.
Players are used to saving and loading taking a few seconds. So recalculating all of the paths isn’t going to be a big deal.
Yeah, I’d prefer not even saving the AI behavior tree state and maybe there is a way to deal with that but I’m not sure what it is. For example, a task may involve moving to an item, picking it up, moving to a storage location, and dropping it. Ideally just the fact that the entity is in the middle of that particular task is saved and it figures out what is still left to do, including getting paths and such, after load. But there are a lot of different tasks like that and it seems more straightforward to just save all the state for each behavior tree node, and allow that node to restore what it needs (e.g. do a new path query) after load. Seems more focused that way, but potentially a lot more code.
I don’t remember if it was you or not, but someone said in another thread that sometimes you just need to stop thinking and start coding it up. Probably I’m just over thinking and need to just start doing.