How to strip editor specific code from build in godot 4.2

Godot Version

4.2.1.stable

Question

I’ve written a tool script which works great and is a huge help for editing a particular piece of data. However when exporting the game, I get an error, as the EditorInterface singleton isn’t available in build, fair enough. But I can’t find any way to exclude this code from my build, I need to manually comment it out, then the build works fine. This is breaking even when the code path will never reach that code at runtime, as the code in question is wrapped in if Engine.is_editor_hint(). Optimally I would strip just the few lines that break from the build as if I’d commented them out before building, but that code would still be kept in the editor, just removed from the build.

For my project specific details, you can find the code in question below. When a particular node is created, the plan is to open a dialog menu to create a new custom resource which the node depends on. The bit that fails is trying to get the current editor viewport so that the save file dialog can be opened. Other nodes inherit from this one, so even though the base does nothing at runtime, the extended nodes do things at runtime.

@tool
class_name LocationEmitter extends Node2D

@export var location: Location
var file_dialog : FileDialog = null

func _ready() -> void:
	if Engine.is_editor_hint():
		add_to_group("location_emitter")
		await get_tree().process_frame
		
		if get_tree() == null:
			return
		
		# Ensures no other location emitters have the same resource (must be unique)
		var location_emitters := get_tree().get_nodes_in_group("location_emitter")
		for location_emitter in location_emitters:
			if location_emitter is LocationEmitter:
				if location_emitter != self and location != null and location == location_emitter.location:
					location = null
					break
		
		if location == null and owner != null:
			if file_dialog != null:
				file_dialog.queue_free()
			
			# Open a file dialog
			file_dialog = FileDialog.new()
			file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
			file_dialog.access = FileDialog.ACCESS_RESOURCES
			file_dialog.connect("file_selected", create_resource)
			file_dialog.current_path = "res://data/locations/"
			file_dialog.filters = PackedStringArray(["*.tres, *.res ; Resource"])
			
			# TODO: The following line is what breaks in build and I'd like to strip from builds.
			
			var viewport := EditorInterface.get_base_control().get_viewport()
			viewport.add_child(file_dialog)
			file_dialog.popup_centered_ratio(0.5)


func create_resource(filename: String) -> void:
	location = Location.new()
	location.take_over_path(filename)
	ResourceSaver.save(location, filename)
	
	if file_dialog != null:
		file_dialog.queue_free()


func _process(_delta: float) -> void:
	if Engine.is_editor_hint():
		if location != null and owner != null:
			var filepath := owner.scene_file_path
			var dirty := false
			if location.parent_scene_path != filepath:
				location.parent_scene_path = filepath
				dirty = true
			if location.global_position != global_position:
				location.global_position = global_position
				dirty = true
			
			if dirty:
				ResourceSaver.save(location)

There are a few issues with things like this in GDScript. Ideally you would make a plugin for things that access the editor, but this is not always convenient.

As a workaround, you can try using a cast like (EditorInterface as Variant). It’s got the downside of disabling static typing, but at least sometimes you can trick the compiler into allowing the code to build.

Hmm thanks but that workaround didn’t work. I’m able to make the build, it doesn’t fail at all, but when playing the following error gets thrown, and nodes that extend this node don’t work. I would hope that a plugin isn’t needed as it’s just the one line that breaks

USER SCRIPT ERROR: Parse Error: Identifier “EditorInterface” not declared in the current scope.
at: GDScript::reload (res://scripts/world/area_management/location_emitter.gd:33)
ERROR: Failed to load script “res://scripts/world/area_management/location_emitter.gd” with error “Parse error”.
at: load (modules/gdscript/gdscript.cpp:2788)
USER SCRIPT ERROR: Parse Error: Could not resolve class “LocationEmitter”.
at: GDScript::reload (res://scripts/world/area_management/scene_door.gd:0)
ERROR: Failed to load script “res://scripts/world/area_management/scene_door.gd” with error “Parse error”.
at: load (modules/gdscript/gdscript.cpp:2788)

	if Engine.is_editor_hint():
		var editor_interface = Engine.get_singleton("EditorInterface")
		var control = editor_interface.get_base_control()

That should work, you lose code completion though.

3 Likes

That worked, thanks!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.