Cannot serialize graph while it is locked

I’ve built up a more robust point graph editor so that i can add/remove/connect/unconnect nodes in a point graph. I would like to auto-save the graph after I do these types of operations so that I have a clear undo history. for the most part this works, but i’ve run into a little bit of trouble with saving during an add operation.

This is basically what i’m doing -

AstarPath.active.AddWorkItem(new AstarWorkItem(ctx => {
	var pointGraph = AstarPath.active.data.pointGraph;
	PointNode newNode = pointGraph.AddNode(current.PointNodeData.position);

	// auto save the graph. this doesn't work!
	var settings = new Pathfinding.Serialization.SerializeSettings();
	settings.nodes = true;
	byte[] bytes = AstarPath.active.data.SerializeGraphs(settings);
	Pathfinding.Serialization.AstarSerializer.SaveToFile(path, bytes);
}));

this gives an error though -

InvalidOperationException: Graphs cannot be added, removed or serialized while the graph structure is locked. This is the case when a graph is currently being scanned and when executing graph updates and work items.
However as a special case, graphs can be added inside work items.
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.

Which is understandable. Is there some ez way i can accomplish this? perhaps there’s some callback I can register to (or add) when the work item is finished and its save to serialize the graph?

Hi

Currently I must admit that there is no callback for when work items complete. However the easiest solution is to do this:

You can call AstarPath.active.FlushWorkItems() right after you have called AddWorkItem. This will make all queued work items execute immediately and synchronously. After that you are guaranteed that all works items have been executed and you can serialize the graphs.
You could also set a boolean flag inside your work item, and then repeatedly check for if that was true and AstarPath.active.IsAnyWorkItemInProgress was false.

I also read your survey response (thanks for answering) and I must say that you are using the package in a very unconventional way. Generally graphs are intended to be generated from some existing representation and recalculated. The package workflow has not been optimized for editing an existing graph and using the save & load tab as a sort of undo stack. For editing the point graph in a more declarative way there is a component called NodeLink which you can use to add/remove/change cost of connections between nodes. There are also some keyboard shortcuts (you can see them under Menubar -> Edit -> Pathfinding, for example if you select two node GameObjects you can use alt+meta+L to link those nodes, or press alt+meta+L again to delete the connection between them). Though this workflow is not very well documented at the moment I must admit.

I am very interested in the null reference exceptions that you are getting though? How are you managing to get those?

Also. I see that you are making a MOBA-like game. However a point graph seems like an odd choice for that type of game (I would have expected a grid graph or possibly a recast graph). Is there any particular reason you wanted a point graph instead?

[edit] I also added a short documentation page that briefly describes the NodeLink component: https://arongranberg.com/astar/docs/editinggraphs.html

FlushWorkItems() did the trick, thanks for that! and thanks for the detailed response

It looks like NodeLink may have solved some of my issues…oops :slight_smile: Actually writing a script to link and unlink nodes was pretty straightforward though. Most of my pain came in when I wanted to add/remove nodes.

I haven’t fully groked your codebase yet, but it seems to me that a lot of my troubles stem from PointGenerator.AddNode creating a new node Array that has some room to grow (i.e. it doesn’t just allocate an array big enough to hold one more node). I think what you’re doing here is a great optimization, but perhaps something else in my code trips up on this a lot. It seems that when I edit the graph, the gizmo calls often end up calling GetNodes a lot, inevitably hitting some empty slots in the nodes Array. I’ve patched up most of the places it would hit a null error i think, and (as a nuclear option) i’ve added a MenuItem command that rebuilds the nodes array, trimming out any nulls.

I’m willing to bet there’s probably a couple things I’m doing with your code that is just fundamentally wrong lol, but what i have now is pretty much good enough. having an undo history basically seals the deal for me and should allow me to work through any other issues that pop up without losing too much work.

Anyways regarding my game. I probably use the MOBA term too loosely. I’m making a hero shooter with some team objectives. Showing is easier than telling in this case: https://store.steampowered.com/app/536460/Warp_League_Basketball/

It’s a VR game where you use a teleport gun to point where you want to move to. I’m using your pathfinding library to make bots for the game. I think a PointGraph makes the most sense for this - players & bots move from point to point, and they can also point at ledges to ‘climb’ up.

With that in mind, I pretty much need to be able to fine tune the generated PointGraph. I started out with a script that basically raycasts down from the sky to the ground to generate a preliminary graph that ends up being very grid like, but then have to tune it a lot after that to make sure the paths make sense & don’t produce broken things (like connected nodes on either side of a wall). It is probably an unusual use case, but tbh i feel like the stuff i’m hacking in here are things i would want out of the box for any/every PointGraph application i can think of. I feel like just allowing auto scan to do its thing is probably not quite good enough, and would require a ton of noodling to get right. I could be wrong though.

Here’s a pic of my WIP point graph in the editor btw. maybe it will also help illustrate what I’m trying to do.