3 min read

Evading GridMap Crashes

It's been another busy streak since the last update including a rework of all the existing levels. In the process of doing this, a not-so-obvious bug revealed itself in Godot.

As mentioned in The Previous Post, several levels were reworked to use new wall meshes. While doing this, some were rearranged slightly and needed some areas of the map to be moved. While doing this, the editor consistently crashed when bulk moving tiles with the following stack trace:

ERROR: Requested for nonexistent MeshLibrary item '1'.                                                           
   at: get_item_mesh (scene/resources/3d/mesh_library.cpp:221)

================================================================
handle_crash: Program crashed with signal 11
Engine version: Godot Engine v4.4.1.stable.official (49a5bc7b616bd04689a2c89e89bda41f50241464)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[1] /lib/x86_64-linux-gnu/libc.so.6(+0x45810) [0x75d825645810] (??:0)
[2] ~/.local/bin/godot() [0xf9a45b] (??:0)
[3] ~/.local/bin/godot() [0x4513ff4] (??:0)
[4] ~/.local/bin/godot() [0x47d555a] (??:0)
[5] ~/.local/bin/godot() [0x80ba73] (??:0)
[6] ~/.local/bin/godot() [0xff6b76] (??:0)
[7] ~/.local/bin/godot() [0x18d15b6] (??:0)
[8] ~/.local/bin/godot() [0x1fcbfc8] (??:0)
[9] ~/.local/bin/godot() [0x1fa8d7c] (??:0)
[10] ~/.local/bin/godot() [0x47d555a] (??:0)
[11] ~/.local/bin/godot() [0x2a3ac76] (??:0)
[12] ~/.local/bin/godot() [0x2985808] (??:0)
[13] ~/.local/bin/godot() [0x29865b6] (??:0)
[14] ~/.local/bin/godot() [0x299d7bc] (??:0)
[15] ~/.local/bin/godot() [0x4f18ecb] (??:0)
[16] ~/.local/bin/godot() [0x4ced85] (??:0)
[17] ~/.local/bin/godot() [0x442a293] (??:0)
[18] ~/.local/bin/godot() [0x442c14f] (??:0)
[19] ~/.local/bin/godot() [0x4dc1e3] (??:0)
[20] ~/.local/bin/godot() [0x41e7a0] (??:0)
[21] /lib/x86_64-linux-gnu/libc.so.6(+0x2a338) [0x75d82562a338] (??:0)
[22] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x8b) [0x75d82562a3fb] (??:0)
[23] ~/.local/bin/godot() [0x44297a] (??:0)
-- END OF BACKTRACE --
================================================================

As it turns out, when some old items were removed from the mesh library, they hadn't also been removed from the GridMap that was in use first; this leaves a lingering reference in the GridMap to items that no longer exist in the mesh library.

In the below example, you can see we have three distinct meshes; a blue (item 1), a red (item 2) and a green (item 3) cube:

A GridMap before deleting any items from its assigned mesh library

If we leave the GridMap as is, and delete item 2 from the assigned mesh library, the GridMap will visually update to reflect this, as can be seen below, but the cells between the blue and green cells will still reference item 2 of the mesh library.

The GridMap after deleting the red mesh from the mesh library but not from the GridMap itself

Even though these items no longer exist in the mesh library and show no visible mesh on the GridMap, the cells containing the invalid references will still attempt to access the item when moving them after selecting an area that contains them, which then causes the crash.

An issue for this has been raised for this on GitHub (which you can track Here), but in the meantime, you can cleanup GridMaps containing invalid items using the script we've created below:

@tool
class_name GridMapCleaner
extends Node


@export_tool_button("Remove Invalid Items", "Callable") var remove_invalid_items_action: Callable = remove_invalid_items

@export var grid_map: GridMap


func remove_invalid_items() -> void:
	if not grid_map:
		print("GridMapCleaner: GridMap has not been set, aborting.")
		return

	var mesh_library: MeshLibrary = grid_map.mesh_library
	var used_cells: Array[Vector3i] = grid_map.get_used_cells()

	for i in range(used_cells.size()):
		var cell: Vector3i = used_cells[i]
		var item_index: int = grid_map.get_cell_item(cell)

		var mesh: Mesh = mesh_library.get_item_mesh(item_index)

		if not mesh:
			print("GridMapCleaner: Removing invalid mesh ID (", item_index, ") at ", cell)
			grid_map.set_cell_item(cell, GridMap.INVALID_CELL_ITEM)

	print("GridMapCleaner: Finished removing invalid items.")

To use this tool, create a new GDScript file wherever you prefer to store your tools and paste the above code into it.

Next, go to the scene containing a GridMap that you want to clean of invalid items and add a new GridMapCleaner node. If you open the GridMapCleaner node in the inspector, you'll see a "Grid Map" property [1] that you can assign your problematic GridMap to and a button [2] that can be used to remove all the invalid items:

The GridMapCleaner inspector pane

After hitting the Remove Invalid Items button, you should see a message in the output pane indicating it is complete, and the items should no longer cause the aforementioned crash or any other unexpected issues.