Global Scripts Issues with Version Control

File permissions from the OS can get propagated with transfers. Also, I don’t know anything about any machine the problem occurs on. The only specification I know is from your video: you’re on Windows 11. :see_no_evil_monkey:

It still could be a filename issue. What is the full project path on your system? Does the project name or file structure (outside of Godot, so not the res:// but absolute path) contain any special characters, or simply any non camelCase/PascalCase characters?

Windows 10 and Windows 11 are the systems used across the machines.

The path on my current machine is: D:\Others\Godot Projects\GLO-Project

Removing the space in Godot Projectsdidn’t seem to change anything.

If this helps, here’s system info:

OS Name	Microsoft Windows 11 Home
Version	10.0.26200 Build 26200
Other OS Description 	Not Available
OS Manufacturer	Microsoft Corporation
System Name	DESKTOP-SPMMDH0
System Manufacturer	ASUSTeK COMPUTER INC.
System Model	ROG Strix G531GT_G531GT
System Type	x64-based PC
System SKU	
Processor	Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz, 2592 Mhz, 6 Core(s), 12 Logical Processor(s)
BIOS Version/Date	American Megatrends Inc. G531GT.308, 01/02/2021
SMBIOS Version	3.2
Embedded Controller Version	255.255
BIOS Mode	UEFI
BaseBoard Manufacturer	ASUSTeK COMPUTER INC.
BaseBoard Product	G531GT
BaseBoard Version	1.0
Platform Role	Mobile
Secure Boot State	On
PCR7 Configuration	Elevation Required to View
Windows Directory	C:\WINDOWS
System Directory	C:\WINDOWS\system32
Boot Device	\Device\HarddiskVolume2
Locale	United Kingdom
Hardware Abstraction Layer	Version = "10.0.26100.1"
Time Zone	W. Europe Summer Time
Installed Physical Memory (RAM)	32.0 GB
Total Physical Memory	31.9 GB
Available Physical Memory	20.0 GB
Total Virtual Memory	33.9 GB
Available Virtual Memory	21.6 GB
Page File Space	2.00 GB
Page File	C:\pagefile.sys
Kernel DMA Protection	Off
Virtualisation-based security	Not enabled
App Control for Business policy	Enforced
App Control for Business user mode policy	Off
Automatic Device Encryption Support	Elevation Required to View
Hyper-V - VM Monitor Mode Extensions	Yes
Hyper-V - Second Level Address Translation Extensions	Yes
Hyper-V - Virtualisation Enabled in Firmware	Yes
Hyper-V - Data Execution Protection	Yes

snake_case for folders, no caps, it can lead to problem in Windows. I’d avoid spaces also, even though they’ve been accepted on Windows for a long time, there is plenty of code out there that doesn’t

Cheers!

1 Like

Changing every folder in the path to snake_case doesn’t solve the issue, even after re-importing the project in Godot. Current it’s:

D:\others\godot_projects\glo_project

TLDR, I believe I found the problem and have a fix for you.

It’ll help you when you export your game to Windows and Mac. Without that you’d have a whole other issue of images and other assets not showing up in your exported builds.


Your issue is honestly really weird.

That’s good.

This shouldn’t be happening.

What happens if you right-click on it and use the Move or Duplicate option to move it instead? Same issue? (It shouldn’t matter, but like I said this is weird.)

This shouldn’t matter either. (Again, weird.)


Errors

It’s opening res://scripts/stats/resources/puzzle_stats.gd

Line 1127

This is erroring out in a really odd place. It’s the function that returns OK when loading a GDScript file. But if it doesn’t return OK, it should go onto the error section of the function.

Then trying to load res://scripts/stats/resources/puzzle_stats.gd even though it failed to open it.

Line 343

This error is potentially more helpful, because it’s happening in the multi-threading code. Which begs the question: Are you making multithreading calls anywhere in your code?

Then it’s trying to load user://puzzle_data.tres and having a problem parsing line 62 of that file. Because (I think) it couldn’t find the Resource file where it was expecting to find it at res://scripts/stats/resources/puzzle_stats.gd

Line 40

This is erroring out on the first line of the class definition. Again, not what you would expect. The error should be coming from lower in the file.

AFAICT, exact same problem as before, not sure why it’s happening twice.

This time the loading is failing for user://puzzle_data.tres

If you look at the code throwing these errors, they’re all in really odd places - with the exception of the multi-threading error. I do think it’s important that you’re getting three separate errors, all from the same file. All connected to this line in puzzle_data.gd:

var _resource = load(_save_file_path)

All related to failing to load this file: res://scripts/stats/resources/puzzle_stats.gd. So somewhere in your code is a hardcoded reference to that file, and based on the cyclic reference error, it’s probably in res://scripts/player_data/puzzle.gd

So what’s that script look like?

It’s also possible that this is a data mismatch that is being confused by naming conventions that are too close, and lack of strict typing. So let’s fix that and see if it solves your problem.

PuzzleDataResource

First, let’s take a look at puzzle_data_resource.gd. Naming conventions are important. This is ends with “Resource” because there’s a naming conflict - namely puzzle_data.gd. But in the comments I see the # Player data comment ahead of all of the variables in the file. So it’s the data about the puzzle, but it’s also data about the player. So it is the player’s puzzle data. Lets name it that: PlayerPuzzleData. This also gets rid of the use of Resource in a class name, which is an example of RAS syndrome.

player_puzzle_data.gd

class_name PlayerPuzzleData extends Resource

# Player data
@export var puzzle_name: String
@export var difficulty: Puzzle.PuzzleDifficulty
@export var completed: bool
@export var completion_time: int

This is a more descriptive name which helps with code readability and namespace conflicts. You’ll notice I’ve also adopted the Godot 4 convention of putting class_name and extends on the same line. (You were following the Godot 3 convention.)

You can do a replace across all your files for this name using Ctrl+Shift+R in the Godot IDE. It’s not quite the same as a Refactor option, but it should be good enough. Once you tell it what you want to replace, it’ll give you an option to select all the instances it found and de-select the ones you don’t want changing.

PuzzleStatistics

Just a few changes here. The main thing though, is naming consistency. You use stats and statistics interchangeably. And then in your code you have this line a lot:

puzzle_statistics.stats

RAS syndrome rearing its ugly head again. PuzzleStatistics is just a container of all the PlayerPuzzleData for your puzzles. We can’t name it Puzzle, because you already have a class with that name. You probably also have a Player class. But what if we call it PlayerData? Then if you want to store something about the player that needs to be stored on disk, it can be put in here too. It also solves the RAS syndrome issue.

player_data.gd

class_name PlayerData extends Resource

@export var statistics: Array[PuzzlePlayerData]

Then find and replace:

PuzzleData

Now we have no naming conflicts. There’s nothing preventing us from naming this class, which may help in the future. Also, since the name of the save file isn’t going to change, we can make it a constant. Then, we can add static typing - which I hope will help us solve your problem. This is because I think what’s happening is you’re loading the wrong thing into the a variable - expecting one type and loading another. Either way, it’ll make your code cleaner and easier to read.

Multi-Select

Just a side-note, when I was editing your file, to replace the _save_file_path variable, I double-clicked on it, and then pressed Ctrl+D until all the options were selected.

Then I typed in the new name.

When you’re done, click in the file instead of pressing “Enter”.


As I was editing, I noticed the problem with the renaming:

var puzzle_statistics: PlayerData


func _ready() -> void:
	# Load data
	puzzle_statistics = load_data()


func save_data(data: PlayerPuzzleData):
	puzzle_statistics.stats.push_back(data)
	ResourceSaver.save(puzzle_statistics, SAVE_FILE_PATH)

puzzle_statistics holds PlayerData (formerly PuzzleStatistics), but save_data() saves PlayerPuzzleData (formerly PuzzleDataResource).

puzzle_data.gd

class_name PuzzleData extends Node

const SAVE_FILE_PATH = "user://puzzle_data.tres"

var player_data: PlayerData


func _ready() -> void:
	# Load data
	player_data = load_data()


func save_data(data: PlayerData):
	player_data.stats.push_back(data)
	ResourceSaver.save(player_data, SAVE_FILE_PATH)


func load_data():
	# Load save file, if it exists
	if ResourceLoader.exists(SAVE_FILE_PATH):
		var _resource = load(SAVE_FILE_PATH)
		return _resource
	
	var _new_resource = PlayerData.new()
	return _new_resource


func count_completed(result: bool) -> int:
	var _amount: int = 0
	for i in player_data.stats.size():
		if player_data.stats[i].completed == result:
			_amount += 1
	return _amount


func count_difficulties(difficulty: Puzzle.PuzzleDifficulty):
	var _amount: int = 0
	for i in player_data.stats.size():
		if player_data.stats[i].difficulty == difficulty:
			_amount += 1
	return _amount

This script should solve your problem.

TLDR

Of course, you can ignore all my other advice, and just change the one line:

func save_data(data: PuzzleDataResource):

To this:

func save_data(data: PuzzleStatistics):
2 Likes

I FINALLY SOLVED IT!

About load_data() errors

I opened .tres file in text editor and I noticed one thing. It stores the path to the resource that was saved in it.

[gd_resource type="Resource" script_class="TestResource" format=3]

[ext_resource type="Script" path="res://resource_saving/test_resource.gd" id="1_um5sb"]

[resource]
script = ExtResource("1_um5sb")
variable = 6

Now technically the path should change whenever you move the resource (test_resource.gd in this case) to another folder. This doesn’t always work. Especially if the file is saved in user:// as opposed to res://. And to make it weirder, if the variable storing a path is originally starting with res:// the issue does not appear locally, but if the save file path variable starts with user://, even when it’s changed on _ready(), the issue will still appear.

Here’s the visualization:

Solutions:

  1. Never move the scripts saved with ResourceSaver / Resource Loader. I marked the folder with resource scripts red so it’s more obvious that this is no-touch folder.
  2. Every time you move your resource script, you need to delete the save file, or manually change the reference path in .tres file.
  3. Store your save file in res:// instead of user://. Change it to user:// for build. The original path needs to be res:// and then changed, not the other way around. Remember to put your save file in .gitignore if you work with other developers unless you want them to have your save file.
    Keep in mind that this is not intended way to save resources originally. They are normally supposed to stay in user://, but saving it this way prevents the issue for me.
  4. I haven’t found a way how to do it, but if you’re able to compare [ext_resource path= ]from your .tres save file to the current path of your script, you could try to correct it automatically when the game starts.

Make sure to always test things like this on build, since the save files will behave differently.

About cyclic resource inclusion

So this error:

ERROR: Another resource is loaded from path 'res://folder_1/new_script.gd' (possible cyclic resource inclusion).

On 4.6.2. stable, this happens EVERY TIME you move any .gd file anywhere. Even if you make an empty project with only one .gd file and move it from folder1 to folder2. So unless you can fix Godot itself, this is a fully ignoreable issue.

Why did I try to solve it then? I assumed that this was causing the load_data() errors, because both were related to moving .gd files. But nope. This one comes with Godot itself. Seems like no one noticed it because you start ignoring it automatically after a while.

About missing Autoload path

As @dragonforge-dev suggested, there are two solutions here.

  1. Put scenes in Autoload and attach scripts to them, as opposed to storing scripts without owners.
  2. Make a plugin that will automatically put all Autoloads in Project Settings and remove them when needed. Here’s how to write one:
    1. Global Scripts Issues with Version Control - #9 by dragonforge-dev

Conclusion

As @dragonforge-dev mentioned, this was a mix of XY issues (where the issue is something else than originally described). In this comment, I compiled all of the issues I’ve had and solutions together with explanations to them. I hope this will serve someone in the future.

I would like to thank everyone who endured my whining on this thread and still kept trying to help. This taught me a lot about Godot and version control.

I will mark this as a solution but feel free to add something that can give even more information.

It would be good to log a bug report about this.

I think this deserves a caveat, which is - if you want other developers to have access to your save files. Otherwise, I don’t understand why you wouldn’t always save in User, because then those files are never under version control and never cause you issues.

This only becomes an issue if you’re saving Resource files through scripts to res:// and you are moving your directory structure around.

Adding scripts to a versioned plugin would also prevent this behavior.

It’s a text file. Just read it and change it.

This is not reproducible for me. I literally moved dozens of .gd files in my project yesterday with no issues. If this were that common of a problem, you wouldn’t have been the first person to report it.

Solutions

Your code still says you are trying to load a different Resource than you are saving.

If I can’t find it reported already, I will.

Good point. Added it. The reason why it’s not in user:// is because the issue is more likely to happen and the save file becomes more tedious to delete.

Not for me. The error happens the most often when it’s saved in user:// actually.

This is weird because I was able to reproduce it on every machine and different systems: Windows 10, Windows 11, MacOS. Even on a laptop from developer who never worked on our project, and the error was present on his own projects. Even when I created empty new project the error happened.

Make sure it’s on 4.6.2. The error appears in Output and not Debugger.

Yes, but it also means that updating your project won’t wipe out whatever you were testing. And it’s easy enough to create a shortcut to the save folder and just clean it out.

This is new info. Also, it highlights what we might be doing differently. You cannot see the user:// folder in the Godot editor, so I do not see how you can move files into or out of it inside the Godot IDE.

I made a brand new project yesterday in 4.6.2. I moved a bunch of things around. This was my Output window when I loaded the project:

Moving a scene doesn’t change that. Moving a script within the Godot IDE using drag-and-drop, I got this:

Reloading the project, the error is gone.

I then moved 5 files within the Godot IDE using drag-and-drop.

When I hit run, everything worked. No errors.

I believe you are doing something that you are not communicating. Can you write up reproducible steps?

This is exactly it! It doesn’t happen when you run the project but every time you move anything by drag & drop, you get this output message. It doesn’t break anything as far as I’m aware but it’s still weird that everyone gets an error just by moving a file.

This is what I was trying to solve for a while until I realized that everyone has it. I thought that was causing the initial resource error. I wanted to include this in the solution so no one else is as confused as I am as to why this appears.

2 Likes

Yeah that was a bug introduced in 4.6 and fixed in 4.7. Based on I never saw it before 4.6, and I no longer see it in 4.7. (Just tested it.)

1 Like