Godot MCP
by Coding-Solo
Verified
#!/usr/bin/env -S godot --headless --script
extends SceneTree
# Debug mode flag
var debug_mode = false
func _init():
var args = OS.get_cmdline_args()
# Check for debug flag
debug_mode = "--debug-godot" in args
# Find the script argument and determine the positions of operation and params
var script_index = args.find("--script")
if script_index == -1:
log_error("Could not find --script argument")
quit(1)
# The operation should be 2 positions after the script path (script_index + 1 is the script path itself)
var operation_index = script_index + 2
# The params should be 3 positions after the script path
var params_index = script_index + 3
if args.size() <= params_index:
log_error("Usage: godot --headless --script godot_operations.gd <operation> <json_params>")
log_error("Not enough command-line arguments provided.")
quit(1)
# Log all arguments for debugging
log_debug("All arguments: " + str(args))
log_debug("Script index: " + str(script_index))
log_debug("Operation index: " + str(operation_index))
log_debug("Params index: " + str(params_index))
var operation = args[operation_index]
var params_json = args[params_index]
log_info("Operation: " + operation)
log_debug("Params JSON: " + params_json)
# Parse JSON using Godot 4.x API
var json = JSON.new()
var error = json.parse(params_json)
var params = null
if error == OK:
params = json.get_data()
else:
log_error("Failed to parse JSON parameters: " + params_json)
log_error("JSON Error: " + json.get_error_message() + " at line " + str(json.get_error_line()))
quit(1)
if not params:
log_error("Failed to parse JSON parameters: " + params_json)
quit(1)
log_info("Executing operation: " + operation)
match operation:
"create_scene":
create_scene(params)
"add_node":
add_node(params)
"load_sprite":
load_sprite(params)
"export_mesh_library":
export_mesh_library(params)
"save_scene":
save_scene(params)
"get_uid":
get_uid(params)
"resave_resources":
resave_resources(params)
_:
log_error("Unknown operation: " + operation)
quit(1)
quit()
# Logging functions
func log_debug(message):
if debug_mode:
print("[DEBUG] " + message)
func log_info(message):
print("[INFO] " + message)
func log_error(message):
printerr("[ERROR] " + message)
# Get a script by name or path
func get_script_by_name(name_of_class):
if debug_mode:
print("Attempting to get script for class: " + name_of_class)
# Try to load it directly if it's a resource path
if ResourceLoader.exists(name_of_class, "Script"):
if debug_mode:
print("Resource exists, loading directly: " + name_of_class)
var script = load(name_of_class) as Script
if script:
if debug_mode:
print("Successfully loaded script from path")
return script
else:
printerr("Failed to load script from path: " + name_of_class)
elif debug_mode:
print("Resource not found, checking global class registry")
# Search for it in the global class registry if it's a class name
var global_classes = ProjectSettings.get_global_class_list()
if debug_mode:
print("Searching through " + str(global_classes.size()) + " global classes")
for global_class in global_classes:
var found_name_of_class = global_class["class"]
var found_path = global_class["path"]
if found_name_of_class == name_of_class:
if debug_mode:
print("Found matching class in registry: " + found_name_of_class + " at path: " + found_path)
var script = load(found_path) as Script
if script:
if debug_mode:
print("Successfully loaded script from registry")
return script
else:
printerr("Failed to load script from registry path: " + found_path)
break
printerr("Could not find script for class: " + name_of_class)
return null
# Instantiate a class by name
func instantiate_class(name_of_class):
if name_of_class.is_empty():
printerr("Cannot instantiate class: name is empty")
return null
var result = null
if debug_mode:
print("Attempting to instantiate class: " + name_of_class)
# Check if it's a built-in class
if ClassDB.class_exists(name_of_class):
if debug_mode:
print("Class exists in ClassDB, using ClassDB.instantiate()")
if ClassDB.can_instantiate(name_of_class):
result = ClassDB.instantiate(name_of_class)
if result == null:
printerr("ClassDB.instantiate() returned null for class: " + name_of_class)
else:
printerr("Class exists but cannot be instantiated: " + name_of_class)
printerr("This may be an abstract class or interface that cannot be directly instantiated")
else:
# Try to get the script
if debug_mode:
print("Class not found in ClassDB, trying to get script")
var script = get_script_by_name(name_of_class)
if script is GDScript:
if debug_mode:
print("Found GDScript, creating instance")
result = script.new()
else:
printerr("Failed to get script for class: " + name_of_class)
return null
if result == null:
printerr("Failed to instantiate class: " + name_of_class)
elif debug_mode:
print("Successfully instantiated class: " + name_of_class + " of type: " + result.get_class())
return result
# Create a new scene with a specified root node type
func create_scene(params):
print("Creating scene: " + params.scene_path)
# Get project paths and log them for debugging
var project_res_path = "res://"
var project_user_path = "user://"
var global_res_path = ProjectSettings.globalize_path(project_res_path)
var global_user_path = ProjectSettings.globalize_path(project_user_path)
if debug_mode:
print("Project paths:")
print("- res:// path: " + project_res_path)
print("- user:// path: " + project_user_path)
print("- Globalized res:// path: " + global_res_path)
print("- Globalized user:// path: " + global_user_path)
# Print some common environment variables for debugging
print("Environment variables:")
var env_vars = ["PATH", "HOME", "USER", "TEMP", "GODOT_PATH"]
for env_var in env_vars:
if OS.has_environment(env_var):
print(" " + env_var + " = " + OS.get_environment(env_var))
# Normalize the scene path
var full_scene_path = params.scene_path
if not full_scene_path.begins_with("res://"):
full_scene_path = "res://" + full_scene_path
if debug_mode:
print("Scene path (with res://): " + full_scene_path)
# Convert resource path to an absolute path
var absolute_scene_path = ProjectSettings.globalize_path(full_scene_path)
if debug_mode:
print("Absolute scene path: " + absolute_scene_path)
# Get the scene directory paths
var scene_dir_res = full_scene_path.get_base_dir()
var scene_dir_abs = absolute_scene_path.get_base_dir()
if debug_mode:
print("Scene directory (resource path): " + scene_dir_res)
print("Scene directory (absolute path): " + scene_dir_abs)
# Only do extensive testing in debug mode
if debug_mode:
# Try to create a simple test file in the project root to verify write access
var initial_test_file_path = "res://godot_mcp_test_write.tmp"
var initial_test_file = FileAccess.open(initial_test_file_path, FileAccess.WRITE)
if initial_test_file:
initial_test_file.store_string("Test write access")
initial_test_file.close()
print("Successfully wrote test file to project root: " + initial_test_file_path)
# Verify the test file exists
var initial_test_file_exists = FileAccess.file_exists(initial_test_file_path)
print("Test file exists check: " + str(initial_test_file_exists))
# Clean up the test file
if initial_test_file_exists:
var remove_error = DirAccess.remove_absolute(ProjectSettings.globalize_path(initial_test_file_path))
print("Test file removal result: " + str(remove_error))
else:
var write_error = FileAccess.get_open_error()
printerr("Failed to write test file to project root: " + str(write_error))
printerr("This indicates a serious permission issue with the project directory")
# Use traditional if-else statement for better compatibility
var root_node_type = "Node2D" # Default value
if params.has("root_node_type"):
root_node_type = params.root_node_type
if debug_mode:
print("Root node type: " + root_node_type)
# Create the root node
var scene_root = instantiate_class(root_node_type)
if not scene_root:
printerr("Failed to instantiate node of type: " + root_node_type)
printerr("Make sure the class exists and can be instantiated")
printerr("Check if the class is registered in ClassDB or available as a script")
quit(1)
scene_root.name = "root"
if debug_mode:
print("Root node created with name: " + scene_root.name)
# Set the owner of the root node to itself (important for scene saving)
scene_root.owner = scene_root
# Pack the scene
var packed_scene = PackedScene.new()
var result = packed_scene.pack(scene_root)
if debug_mode:
print("Pack result: " + str(result) + " (OK=" + str(OK) + ")")
if result == OK:
# Only do extensive testing in debug mode
if debug_mode:
# First, let's verify we can write to the project directory
print("Testing write access to project directory...")
var test_write_path = "res://test_write_access.tmp"
var test_write_abs = ProjectSettings.globalize_path(test_write_path)
var test_file = FileAccess.open(test_write_path, FileAccess.WRITE)
if test_file:
test_file.store_string("Write test")
test_file.close()
print("Successfully wrote test file to project directory")
# Clean up test file
if FileAccess.file_exists(test_write_path):
var remove_error = DirAccess.remove_absolute(test_write_abs)
print("Test file removal result: " + str(remove_error))
else:
var write_error = FileAccess.get_open_error()
printerr("Failed to write test file to project directory: " + str(write_error))
printerr("This may indicate permission issues with the project directory")
# Continue anyway, as the scene directory might still be writable
# Ensure the scene directory exists using DirAccess
if debug_mode:
print("Ensuring scene directory exists...")
# Get the scene directory relative to res://
var scene_dir_relative = scene_dir_res.substr(6) # Remove "res://" prefix
if debug_mode:
print("Scene directory (relative to res://): " + scene_dir_relative)
# Create the directory if needed
if not scene_dir_relative.is_empty():
# First check if it exists
var dir_exists = DirAccess.dir_exists_absolute(scene_dir_abs)
if debug_mode:
print("Directory exists check (absolute): " + str(dir_exists))
if not dir_exists:
if debug_mode:
print("Directory doesn't exist, creating: " + scene_dir_relative)
# Try to create the directory using DirAccess
var dir = DirAccess.open("res://")
if dir == null:
var open_error = DirAccess.get_open_error()
printerr("Failed to open res:// directory: " + str(open_error))
# Try alternative approach with absolute path
if debug_mode:
print("Trying alternative directory creation approach...")
var make_dir_error = DirAccess.make_dir_recursive_absolute(scene_dir_abs)
if debug_mode:
print("Make directory result (absolute): " + str(make_dir_error))
if make_dir_error != OK:
printerr("Failed to create directory using absolute path")
printerr("Error code: " + str(make_dir_error))
quit(1)
else:
# Create the directory using the DirAccess instance
if debug_mode:
print("Creating directory using DirAccess: " + scene_dir_relative)
var make_dir_error = dir.make_dir_recursive(scene_dir_relative)
if debug_mode:
print("Make directory result: " + str(make_dir_error))
if make_dir_error != OK:
printerr("Failed to create directory: " + scene_dir_relative)
printerr("Error code: " + str(make_dir_error))
quit(1)
# Verify the directory was created
dir_exists = DirAccess.dir_exists_absolute(scene_dir_abs)
if debug_mode:
print("Directory exists check after creation: " + str(dir_exists))
if not dir_exists:
printerr("Directory reported as created but does not exist: " + scene_dir_abs)
printerr("This may indicate a problem with path resolution or permissions")
quit(1)
elif debug_mode:
print("Directory already exists: " + scene_dir_abs)
# Save the scene
if debug_mode:
print("Saving scene to: " + full_scene_path)
var save_error = ResourceSaver.save(packed_scene, full_scene_path)
if debug_mode:
print("Save result: " + str(save_error) + " (OK=" + str(OK) + ")")
if save_error == OK:
# Only do extensive testing in debug mode
if debug_mode:
# Wait a moment to ensure file system has time to complete the write
print("Waiting for file system to complete write operation...")
OS.delay_msec(500) # 500ms delay
# Verify the file was actually created using multiple methods
var file_check_abs = FileAccess.file_exists(absolute_scene_path)
print("File exists check (absolute path): " + str(file_check_abs))
var file_check_res = FileAccess.file_exists(full_scene_path)
print("File exists check (resource path): " + str(file_check_res))
var res_exists = ResourceLoader.exists(full_scene_path)
print("Resource exists check: " + str(res_exists))
# If file doesn't exist by absolute path, try to create a test file in the same directory
if not file_check_abs and not file_check_res:
printerr("Scene file not found after save. Trying to diagnose the issue...")
# Try to write a test file to the same directory
var test_scene_file_path = scene_dir_res + "/test_scene_file.tmp"
var test_scene_file = FileAccess.open(test_scene_file_path, FileAccess.WRITE)
if test_scene_file:
test_scene_file.store_string("Test scene directory write")
test_scene_file.close()
print("Successfully wrote test file to scene directory: " + test_scene_file_path)
# Check if the test file exists
var test_file_exists = FileAccess.file_exists(test_scene_file_path)
print("Test file exists: " + str(test_file_exists))
if test_file_exists:
# Directory is writable, so the issue is with scene saving
printerr("Directory is writable but scene file wasn't created.")
printerr("This suggests an issue with ResourceSaver.save() or the packed scene.")
# Try saving with a different approach
print("Trying alternative save approach...")
var alt_save_error = ResourceSaver.save(packed_scene, test_scene_file_path + ".tscn")
print("Alternative save result: " + str(alt_save_error))
# Clean up test files
DirAccess.remove_absolute(ProjectSettings.globalize_path(test_scene_file_path))
if alt_save_error == OK:
DirAccess.remove_absolute(ProjectSettings.globalize_path(test_scene_file_path + ".tscn"))
else:
printerr("Test file couldn't be verified. This suggests filesystem access issues.")
else:
var write_error = FileAccess.get_open_error()
printerr("Failed to write test file to scene directory: " + str(write_error))
printerr("This confirms there are permission or path issues with the scene directory.")
# Return error since we couldn't create the scene file
printerr("Failed to create scene: " + params.scene_path)
quit(1)
# If we get here, at least one of our file checks passed
if file_check_abs or file_check_res or res_exists:
print("Scene file verified to exist!")
# Try to load the scene to verify it's valid
var test_load = ResourceLoader.load(full_scene_path)
if test_load:
print("Scene created and verified successfully at: " + params.scene_path)
print("Scene file can be loaded correctly.")
else:
print("Scene file exists but cannot be loaded. It may be corrupted or incomplete.")
# Continue anyway since the file exists
print("Scene created successfully at: " + params.scene_path)
else:
printerr("All file existence checks failed despite successful save operation.")
printerr("This indicates a serious issue with file system access or path resolution.")
quit(1)
else:
# In non-debug mode, just check if the file exists
var file_exists = FileAccess.file_exists(full_scene_path)
if file_exists:
print("Scene created successfully at: " + params.scene_path)
else:
printerr("Failed to create scene: " + params.scene_path)
quit(1)
else:
# Handle specific error codes
var error_message = "Failed to save scene. Error code: " + str(save_error)
if save_error == ERR_CANT_CREATE:
error_message += " (ERR_CANT_CREATE - Cannot create the scene file)"
elif save_error == ERR_CANT_OPEN:
error_message += " (ERR_CANT_OPEN - Cannot open the scene file for writing)"
elif save_error == ERR_FILE_CANT_WRITE:
error_message += " (ERR_FILE_CANT_WRITE - Cannot write to the scene file)"
elif save_error == ERR_FILE_NO_PERMISSION:
error_message += " (ERR_FILE_NO_PERMISSION - No permission to write the scene file)"
printerr(error_message)
quit(1)
else:
printerr("Failed to pack scene: " + str(result))
printerr("Error code: " + str(result))
quit(1)
# Add a node to an existing scene
func add_node(params):
print("Adding node to scene: " + params.scene_path)
var full_scene_path = params.scene_path
if not full_scene_path.begins_with("res://"):
full_scene_path = "res://" + full_scene_path
if debug_mode:
print("Scene path (with res://): " + full_scene_path)
var absolute_scene_path = ProjectSettings.globalize_path(full_scene_path)
if debug_mode:
print("Absolute scene path: " + absolute_scene_path)
if not FileAccess.file_exists(absolute_scene_path):
printerr("Scene file does not exist at: " + absolute_scene_path)
quit(1)
var scene = load(full_scene_path)
if not scene:
printerr("Failed to load scene: " + full_scene_path)
quit(1)
if debug_mode:
print("Scene loaded successfully")
var scene_root = scene.instantiate()
if debug_mode:
print("Scene instantiated")
# Use traditional if-else statement for better compatibility
var parent_path = "root" # Default value
if params.has("parent_node_path"):
parent_path = params.parent_node_path
if debug_mode:
print("Parent path: " + parent_path)
var parent = scene_root
if parent_path != "root":
parent = scene_root.get_node(parent_path.replace("root/", ""))
if not parent:
printerr("Parent node not found: " + parent_path)
quit(1)
if debug_mode:
print("Parent node found: " + parent.name)
if debug_mode:
print("Instantiating node of type: " + params.node_type)
var new_node = instantiate_class(params.node_type)
if not new_node:
printerr("Failed to instantiate node of type: " + params.node_type)
printerr("Make sure the class exists and can be instantiated")
printerr("Check if the class is registered in ClassDB or available as a script")
quit(1)
new_node.name = params.node_name
if debug_mode:
print("New node created with name: " + new_node.name)
if params.has("properties"):
if debug_mode:
print("Setting properties on node")
var properties = params.properties
for property in properties:
if debug_mode:
print("Setting property: " + property + " = " + str(properties[property]))
new_node.set(property, properties[property])
parent.add_child(new_node)
new_node.owner = scene_root
if debug_mode:
print("Node added to parent and ownership set")
var packed_scene = PackedScene.new()
var result = packed_scene.pack(scene_root)
if debug_mode:
print("Pack result: " + str(result) + " (OK=" + str(OK) + ")")
if result == OK:
if debug_mode:
print("Saving scene to: " + absolute_scene_path)
var save_error = ResourceSaver.save(packed_scene, absolute_scene_path)
if debug_mode:
print("Save result: " + str(save_error) + " (OK=" + str(OK) + ")")
if save_error == OK:
if debug_mode:
var file_check_after = FileAccess.file_exists(absolute_scene_path)
print("File exists check after save: " + str(file_check_after))
if file_check_after:
print("Node '" + params.node_name + "' of type '" + params.node_type + "' added successfully")
else:
printerr("File reported as saved but does not exist at: " + absolute_scene_path)
else:
print("Node '" + params.node_name + "' of type '" + params.node_type + "' added successfully")
else:
printerr("Failed to save scene: " + str(save_error))
else:
printerr("Failed to pack scene: " + str(result))
# Load a sprite into a Sprite2D node
func load_sprite(params):
print("Loading sprite into scene: " + params.scene_path)
# Ensure the scene path starts with res:// for Godot's resource system
var full_scene_path = params.scene_path
if not full_scene_path.begins_with("res://"):
full_scene_path = "res://" + full_scene_path
if debug_mode:
print("Full scene path (with res://): " + full_scene_path)
# Check if the scene file exists
var file_check = FileAccess.file_exists(full_scene_path)
if debug_mode:
print("Scene file exists check: " + str(file_check))
if not file_check:
printerr("Scene file does not exist at: " + full_scene_path)
# Get the absolute path for reference
var absolute_path = ProjectSettings.globalize_path(full_scene_path)
printerr("Absolute file path that doesn't exist: " + absolute_path)
quit(1)
# Ensure the texture path starts with res:// for Godot's resource system
var full_texture_path = params.texture_path
if not full_texture_path.begins_with("res://"):
full_texture_path = "res://" + full_texture_path
if debug_mode:
print("Full texture path (with res://): " + full_texture_path)
# Load the scene
var scene = load(full_scene_path)
if not scene:
printerr("Failed to load scene: " + full_scene_path)
quit(1)
if debug_mode:
print("Scene loaded successfully")
# Instance the scene
var scene_root = scene.instantiate()
if debug_mode:
print("Scene instantiated")
# Find the sprite node
var node_path = params.node_path
if debug_mode:
print("Original node path: " + node_path)
if node_path.begins_with("root/"):
node_path = node_path.substr(5) # Remove "root/" prefix
if debug_mode:
print("Node path after removing 'root/' prefix: " + node_path)
var sprite_node = null
if node_path == "":
# If no node path, assume root is the sprite
sprite_node = scene_root
if debug_mode:
print("Using root node as sprite node")
else:
sprite_node = scene_root.get_node(node_path)
if sprite_node and debug_mode:
print("Found sprite node: " + sprite_node.name)
if not sprite_node:
printerr("Node not found: " + params.node_path)
quit(1)
# Check if the node is a Sprite2D or compatible type
if debug_mode:
print("Node class: " + sprite_node.get_class())
if not (sprite_node is Sprite2D or sprite_node is Sprite3D or sprite_node is TextureRect):
printerr("Node is not a sprite-compatible type: " + sprite_node.get_class())
quit(1)
# Load the texture
if debug_mode:
print("Loading texture from: " + full_texture_path)
var texture = load(full_texture_path)
if not texture:
printerr("Failed to load texture: " + full_texture_path)
quit(1)
if debug_mode:
print("Texture loaded successfully")
# Set the texture on the sprite
if sprite_node is Sprite2D or sprite_node is Sprite3D:
sprite_node.texture = texture
if debug_mode:
print("Set texture on Sprite2D/Sprite3D node")
elif sprite_node is TextureRect:
sprite_node.texture = texture
if debug_mode:
print("Set texture on TextureRect node")
# Save the modified scene
var packed_scene = PackedScene.new()
var result = packed_scene.pack(scene_root)
if debug_mode:
print("Pack result: " + str(result) + " (OK=" + str(OK) + ")")
if result == OK:
if debug_mode:
print("Saving scene to: " + full_scene_path)
var error = ResourceSaver.save(packed_scene, full_scene_path)
if debug_mode:
print("Save result: " + str(error) + " (OK=" + str(OK) + ")")
if error == OK:
# Verify the file was actually updated
if debug_mode:
var file_check_after = FileAccess.file_exists(full_scene_path)
print("File exists check after save: " + str(file_check_after))
if file_check_after:
print("Sprite loaded successfully with texture: " + full_texture_path)
# Get the absolute path for reference
var absolute_path = ProjectSettings.globalize_path(full_scene_path)
print("Absolute file path: " + absolute_path)
else:
printerr("File reported as saved but does not exist at: " + full_scene_path)
else:
print("Sprite loaded successfully with texture: " + full_texture_path)
else:
printerr("Failed to save scene: " + str(error))
else:
printerr("Failed to pack scene: " + str(result))
# Export a scene as a MeshLibrary resource
func export_mesh_library(params):
print("Exporting MeshLibrary from scene: " + params.scene_path)
# Ensure the scene path starts with res:// for Godot's resource system
var full_scene_path = params.scene_path
if not full_scene_path.begins_with("res://"):
full_scene_path = "res://" + full_scene_path
if debug_mode:
print("Full scene path (with res://): " + full_scene_path)
# Ensure the output path starts with res:// for Godot's resource system
var full_output_path = params.output_path
if not full_output_path.begins_with("res://"):
full_output_path = "res://" + full_output_path
if debug_mode:
print("Full output path (with res://): " + full_output_path)
# Check if the scene file exists
var file_check = FileAccess.file_exists(full_scene_path)
if debug_mode:
print("Scene file exists check: " + str(file_check))
if not file_check:
printerr("Scene file does not exist at: " + full_scene_path)
# Get the absolute path for reference
var absolute_path = ProjectSettings.globalize_path(full_scene_path)
printerr("Absolute file path that doesn't exist: " + absolute_path)
quit(1)
# Load the scene
if debug_mode:
print("Loading scene from: " + full_scene_path)
var scene = load(full_scene_path)
if not scene:
printerr("Failed to load scene: " + full_scene_path)
quit(1)
if debug_mode:
print("Scene loaded successfully")
# Instance the scene
var scene_root = scene.instantiate()
if debug_mode:
print("Scene instantiated")
# Create a new MeshLibrary
var mesh_library = MeshLibrary.new()
if debug_mode:
print("Created new MeshLibrary")
# Get mesh item names if provided
var mesh_item_names = params.mesh_item_names if params.has("mesh_item_names") else []
var use_specific_items = mesh_item_names.size() > 0
if debug_mode:
if use_specific_items:
print("Using specific mesh items: " + str(mesh_item_names))
else:
print("Using all mesh items in the scene")
# Process all child nodes
var item_id = 0
if debug_mode:
print("Processing child nodes...")
for child in scene_root.get_children():
if debug_mode:
print("Checking child node: " + child.name)
# Skip if not using all items and this item is not in the list
if use_specific_items and not (child.name in mesh_item_names):
if debug_mode:
print("Skipping node " + child.name + " (not in specified items list)")
continue
# Check if the child has a mesh
var mesh_instance = null
if child is MeshInstance3D:
mesh_instance = child
if debug_mode:
print("Node " + child.name + " is a MeshInstance3D")
else:
# Try to find a MeshInstance3D in the child's descendants
if debug_mode:
print("Searching for MeshInstance3D in descendants of " + child.name)
for descendant in child.get_children():
if descendant is MeshInstance3D:
mesh_instance = descendant
if debug_mode:
print("Found MeshInstance3D in descendant: " + descendant.name)
break
if mesh_instance and mesh_instance.mesh:
if debug_mode:
print("Adding mesh: " + child.name)
# Add the mesh to the library
mesh_library.create_item(item_id)
mesh_library.set_item_name(item_id, child.name)
mesh_library.set_item_mesh(item_id, mesh_instance.mesh)
if debug_mode:
print("Added mesh to library with ID: " + str(item_id))
# Add collision shape if available
var collision_added = false
for collision_child in child.get_children():
if collision_child is CollisionShape3D and collision_child.shape:
mesh_library.set_item_shapes(item_id, [collision_child.shape])
if debug_mode:
print("Added collision shape from: " + collision_child.name)
collision_added = true
break
if debug_mode and not collision_added:
print("No collision shape found for mesh: " + child.name)
# Add preview if available
if mesh_instance.mesh:
mesh_library.set_item_preview(item_id, mesh_instance.mesh)
if debug_mode:
print("Added preview for mesh: " + child.name)
item_id += 1
elif debug_mode:
print("Node " + child.name + " has no valid mesh")
if debug_mode:
print("Processed " + str(item_id) + " meshes")
# Create directory if it doesn't exist
var dir = DirAccess.open("res://")
if dir == null:
printerr("Failed to open res:// directory")
printerr("DirAccess error: " + str(DirAccess.get_open_error()))
quit(1)
var output_dir = full_output_path.get_base_dir()
if debug_mode:
print("Output directory: " + output_dir)
if output_dir != "res://" and not dir.dir_exists(output_dir.substr(6)): # Remove "res://" prefix
if debug_mode:
print("Creating directory: " + output_dir)
var error = dir.make_dir_recursive(output_dir.substr(6)) # Remove "res://" prefix
if error != OK:
printerr("Failed to create directory: " + output_dir + ", error: " + str(error))
quit(1)
# Save the mesh library
if item_id > 0:
if debug_mode:
print("Saving MeshLibrary to: " + full_output_path)
var error = ResourceSaver.save(mesh_library, full_output_path)
if debug_mode:
print("Save result: " + str(error) + " (OK=" + str(OK) + ")")
if error == OK:
# Verify the file was actually created
if debug_mode:
var file_check_after = FileAccess.file_exists(full_output_path)
print("File exists check after save: " + str(file_check_after))
if file_check_after:
print("MeshLibrary exported successfully with " + str(item_id) + " items to: " + full_output_path)
# Get the absolute path for reference
var absolute_path = ProjectSettings.globalize_path(full_output_path)
print("Absolute file path: " + absolute_path)
else:
printerr("File reported as saved but does not exist at: " + full_output_path)
else:
print("MeshLibrary exported successfully with " + str(item_id) + " items to: " + full_output_path)
else:
printerr("Failed to save MeshLibrary: " + str(error))
else:
printerr("No valid meshes found in the scene")
# Find files with a specific extension recursively
func find_files(path, extension):
var files = []
var dir = DirAccess.open(path)
if dir:
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if dir.current_is_dir() and not file_name.begins_with("."):
files.append_array(find_files(path + file_name + "/", extension))
elif file_name.ends_with(extension):
files.append(path + file_name)
file_name = dir.get_next()
return files
# Get UID for a specific file
func get_uid(params):
if not params.has("file_path"):
printerr("File path is required")
quit(1)
# Ensure the file path starts with res:// for Godot's resource system
var file_path = params.file_path
if not file_path.begins_with("res://"):
file_path = "res://" + file_path
print("Getting UID for file: " + file_path)
if debug_mode:
print("Full file path (with res://): " + file_path)
# Get the absolute path for reference
var absolute_path = ProjectSettings.globalize_path(file_path)
if debug_mode:
print("Absolute file path: " + absolute_path)
# Ensure the file exists
var file_check = FileAccess.file_exists(file_path)
if debug_mode:
print("File exists check: " + str(file_check))
if not file_check:
printerr("File does not exist at: " + file_path)
printerr("Absolute file path that doesn't exist: " + absolute_path)
quit(1)
# Check if the UID file exists
var uid_path = file_path + ".uid"
if debug_mode:
print("UID file path: " + uid_path)
var uid_check = FileAccess.file_exists(uid_path)
if debug_mode:
print("UID file exists check: " + str(uid_check))
var f = FileAccess.open(uid_path, FileAccess.READ)
if f:
# Read the UID content
var uid_content = f.get_as_text()
f.close()
if debug_mode:
print("UID content read successfully")
# Return the UID content
var result = {
"file": file_path,
"absolutePath": absolute_path,
"uid": uid_content.strip_edges(),
"exists": true
}
if debug_mode:
print("UID result: " + JSON.stringify(result))
print(JSON.stringify(result))
else:
if debug_mode:
print("UID file does not exist or could not be opened")
# UID file doesn't exist
var result = {
"file": file_path,
"absolutePath": absolute_path,
"exists": false,
"message": "UID file does not exist for this file. Use resave_resources to generate UIDs."
}
if debug_mode:
print("UID result: " + JSON.stringify(result))
print(JSON.stringify(result))
# Resave all resources to update UID references
func resave_resources(params):
print("Resaving all resources to update UID references...")
# Get project path if provided
var project_path = "res://"
if params.has("project_path"):
project_path = params.project_path
if not project_path.begins_with("res://"):
project_path = "res://" + project_path
if not project_path.ends_with("/"):
project_path += "/"
if debug_mode:
print("Using project path: " + project_path)
# Get all .tscn files
if debug_mode:
print("Searching for scene files in: " + project_path)
var scenes = find_files(project_path, ".tscn")
if debug_mode:
print("Found " + str(scenes.size()) + " scenes")
# Resave each scene
var success_count = 0
var error_count = 0
for scene_path in scenes:
if debug_mode:
print("Processing scene: " + scene_path)
# Check if the scene file exists
var file_check = FileAccess.file_exists(scene_path)
if debug_mode:
print("Scene file exists check: " + str(file_check))
if not file_check:
printerr("Scene file does not exist at: " + scene_path)
error_count += 1
continue
# Load the scene
var scene = load(scene_path)
if scene:
if debug_mode:
print("Scene loaded successfully, saving...")
var error = ResourceSaver.save(scene, scene_path)
if debug_mode:
print("Save result: " + str(error) + " (OK=" + str(OK) + ")")
if error == OK:
success_count += 1
if debug_mode:
print("Scene saved successfully: " + scene_path)
# Verify the file was actually updated
var file_check_after = FileAccess.file_exists(scene_path)
print("File exists check after save: " + str(file_check_after))
if not file_check_after:
printerr("File reported as saved but does not exist at: " + scene_path)
else:
error_count += 1
printerr("Failed to save: " + scene_path + ", error: " + str(error))
else:
error_count += 1
printerr("Failed to load: " + scene_path)
# Get all .gd and .shader files
if debug_mode:
print("Searching for script and shader files in: " + project_path)
var scripts = find_files(project_path, ".gd") + find_files(project_path, ".shader") + find_files(project_path, ".gdshader")
if debug_mode:
print("Found " + str(scripts.size()) + " scripts/shaders")
# Check for missing .uid files
var missing_uids = 0
var generated_uids = 0
for script_path in scripts:
if debug_mode:
print("Checking UID for: " + script_path)
var uid_path = script_path + ".uid"
var uid_check = FileAccess.file_exists(uid_path)
if debug_mode:
print("UID file exists check: " + str(uid_check))
var f = FileAccess.open(uid_path, FileAccess.READ)
if not f:
missing_uids += 1
if debug_mode:
print("Missing UID file for: " + script_path + ", generating...")
# Force a save to generate UID
var res = load(script_path)
if res:
var error = ResourceSaver.save(res, script_path)
if debug_mode:
print("Save result: " + str(error) + " (OK=" + str(OK) + ")")
if error == OK:
generated_uids += 1
if debug_mode:
print("Generated UID for: " + script_path)
# Verify the UID file was actually created
var uid_check_after = FileAccess.file_exists(uid_path)
print("UID file exists check after save: " + str(uid_check_after))
if not uid_check_after:
printerr("UID file reported as generated but does not exist at: " + uid_path)
else:
printerr("Failed to generate UID for: " + script_path + ", error: " + str(error))
else:
printerr("Failed to load resource: " + script_path)
elif debug_mode:
print("UID file already exists for: " + script_path)
if debug_mode:
print("Summary:")
print("- Scenes processed: " + str(scenes.size()))
print("- Scenes successfully saved: " + str(success_count))
print("- Scenes with errors: " + str(error_count))
print("- Scripts/shaders missing UIDs: " + str(missing_uids))
print("- UIDs successfully generated: " + str(generated_uids))
print("Resave operation complete")
# Save changes to a scene file
func save_scene(params):
print("Saving scene: " + params.scene_path)
# Ensure the scene path starts with res:// for Godot's resource system
var full_scene_path = params.scene_path
if not full_scene_path.begins_with("res://"):
full_scene_path = "res://" + full_scene_path
if debug_mode:
print("Full scene path (with res://): " + full_scene_path)
# Check if the scene file exists
var file_check = FileAccess.file_exists(full_scene_path)
if debug_mode:
print("Scene file exists check: " + str(file_check))
if not file_check:
printerr("Scene file does not exist at: " + full_scene_path)
# Get the absolute path for reference
var absolute_path = ProjectSettings.globalize_path(full_scene_path)
printerr("Absolute file path that doesn't exist: " + absolute_path)
quit(1)
# Load the scene
var scene = load(full_scene_path)
if not scene:
printerr("Failed to load scene: " + full_scene_path)
quit(1)
if debug_mode:
print("Scene loaded successfully")
# Instance the scene
var scene_root = scene.instantiate()
if debug_mode:
print("Scene instantiated")
# Determine save path
var save_path = params.new_path if params.has("new_path") else full_scene_path
if params.has("new_path") and not save_path.begins_with("res://"):
save_path = "res://" + save_path
if debug_mode:
print("Save path: " + save_path)
# Create directory if it doesn't exist
if params.has("new_path"):
var dir = DirAccess.open("res://")
if dir == null:
printerr("Failed to open res:// directory")
printerr("DirAccess error: " + str(DirAccess.get_open_error()))
quit(1)
var scene_dir = save_path.get_base_dir()
if debug_mode:
print("Scene directory: " + scene_dir)
if scene_dir != "res://" and not dir.dir_exists(scene_dir.substr(6)): # Remove "res://" prefix
if debug_mode:
print("Creating directory: " + scene_dir)
var error = dir.make_dir_recursive(scene_dir.substr(6)) # Remove "res://" prefix
if error != OK:
printerr("Failed to create directory: " + scene_dir + ", error: " + str(error))
quit(1)
# Create a packed scene
var packed_scene = PackedScene.new()
var result = packed_scene.pack(scene_root)
if debug_mode:
print("Pack result: " + str(result) + " (OK=" + str(OK) + ")")
if result == OK:
if debug_mode:
print("Saving scene to: " + save_path)
var error = ResourceSaver.save(packed_scene, save_path)
if debug_mode:
print("Save result: " + str(error) + " (OK=" + str(OK) + ")")
if error == OK:
# Verify the file was actually created/updated
if debug_mode:
var file_check_after = FileAccess.file_exists(save_path)
print("File exists check after save: " + str(file_check_after))
if file_check_after:
print("Scene saved successfully to: " + save_path)
# Get the absolute path for reference
var absolute_path = ProjectSettings.globalize_path(save_path)
print("Absolute file path: " + absolute_path)
else:
printerr("File reported as saved but does not exist at: " + save_path)
else:
print("Scene saved successfully to: " + save_path)
else:
printerr("Failed to save scene: " + str(error))
else:
printerr("Failed to pack scene: " + str(result))