Skip to content

Instantly share code, notes, and snippets.

@Jayman2000
Created December 13, 2023 21:27
Show Gist options
  • Save Jayman2000/c153522e40fe9b50935c8ea68c14f334 to your computer and use it in GitHub Desktop.
Save Jayman2000/c153522e40fe9b50935c8ea68c14f334 to your computer and use it in GitHub Desktop.
Force a new version of Godot 3 to make any automatic changes that it’s going to make.
# 🅭🄍1.0 This file is dedicated to the public domain using the CC0 1.0 Universal
# Public Domain Dedication <https://creativecommons.org/publicdomain/zero/1.0/>.
tool
extends EditorScript
signal finished_updating_scene_files
# If this was Godot 4, I would probably just use the built-in Callable type
# instead of creating my own.
class ActionToTakeOnPath extends Reference:
# Returns whether or not the action was successful.
func run(
_path: String,
_is_dir: bool,
_editor_interface: EditorInterface
):
push_error("ActionToTakeOnPath shouldn’t be used directly. Only subclasses of it should be used.")
return false
class DeletePath extends ActionToTakeOnPath:
func run(
path: String,
_is_dir: bool,
_editor_interface: EditorInterface
):
var successful := true
var error_code: int = Directory.new().remove(path)
if error_code != OK:
push_error("Failed to delete “%s”. Error code: %s" % [path, error_code])
successful = false
return successful
class PotentiallyOpenPathAsScene extends ActionToTakeOnPath:
func run(
path: String,
is_dir: bool,
editor_interface: EditorInterface
):
var successful := true
if not is_dir:
var is_editable_resource = ResourceLoader.exists(path)
if is_editable_resource:
var resource : Resource = load(path)
if resource != null:
if resource is PackedScene:
editor_interface.open_scene_from_path(resource.resource_path)
# We need to wait a frame or else the
# EditorInterface will ignore all but one of our
# open_scene_from_path() calls.
var tree = editor_interface.get_base_control().get_tree()
yield(tree, "idle_frame")
else:
push_error("Failed to load “%s” as a resource." % [path])
successful = false
return successful
func get_editors_tree() -> SceneTree:
return get_editor_interface().get_base_control().get_tree()
func fix_result(result) -> bool:
if result is bool:
return result
else:
push_error("result wasn’t a valid type. This should never happen.")
return false
func act_on_paths_in_dir_recursively(
dir_path: String,
action: ActionToTakeOnPath,
skip_hidden: bool
):
var successful := true
if not dir_path.ends_with("/"):
dir_path += "/"
var dir := Directory.new()
var error_code : int = dir.open(dir_path)
if error_code == OK:
error_code = dir.list_dir_begin(true, skip_hidden)
if error_code == OK:
var name : String = dir.get_next()
while name != "":
var path: String = dir_path + name
if dir.current_is_dir():
if not act_on_paths_in_dir_recursively(path, action, skip_hidden):
successful = false
var result = action.run(path, dir.current_is_dir(), get_editor_interface())
if result is GDScriptFunctionState:
result = yield(result, "completed")
if not fix_result(result):
successful = false
name = dir.get_next()
else:
push_error("Failed to list the contents of “%s”. Error code: %s" % [dir_path, error_code])
successful = false
else:
push_error("Failed to open “%s” as a directory. Error code: %s" % [dir_path, error_code])
successful = false
dir.list_dir_end()
return successful
func update_dot_import_files() -> void:
print("Updating .import files…")
print("Note: This will probably produce a bunch of errors about missing files. You can ignore those errors.")
var result = act_on_paths_in_dir_recursively("res://.import", DeletePath.new(), false)
if result:
# Thanks, Gnumaru for this workaround:
# <https://github.com/godotengine/godot-proposals/issues/1615>
# Reimport everything
OS.window_minimized = true
for i in 2:
yield(get_editors_tree(), "idle_frame")
OS.move_window_to_foreground()
print("Finished updating .import files.")
else:
push_error("Failed to update .import files.")
emit_signal("finished_updating_scene_files")
func update_scene_files(_resources = null) -> void:
print("Updating scene files…")
if act_on_paths_in_dir_recursively("res://", PotentiallyOpenPathAsScene.new(), true):
# If we were using Godot 4, then I would just do this:
#get_editor_interface().save_all_scenes()
# Since we’re in Godot 3, I have to tell the user to do that manually:
print("Please click Scene > Save All Scenes.")
else:
push_error("Failed to update .import files.")
emit_signal("finished_updating_scene_files")
func _run() -> void:
var error_code = get_editor_interface().get_resource_filesystem().connect(
"resources_reimported",
self,
"update_scene_files",
[],
CONNECT_ONESHOT
)
if error_code == OK:
var result = update_dot_import_files()
if result is GDScriptFunctionState:
yield(result, "completed")
else:
push_error("Failed to connect EditorFileSystem’s resources_reimported signal. This should never happen.")
# When Objects are freed, any signals that are connected to it are
# disconnected. It looks like this Script gets freed shortly after _run()
# finishes executing. Without this next line of code, this Script will get
# freed before the connection that I made earlier is triggered.
yield(self, "finished_updating_scene_files")
# This is a workaround for this issue:
# <https://github.com/godotengine/godot/issues/71998>
yield(get_editors_tree(), "idle_frame")
print("Done!")
func print_test(whatever):
print(whatever)
emit_signal("finished_updating_scene_files")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment