godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.84k stars 20.15k forks source link

Instance version of `DirAccess.copy_absolute()` and `DirAccess.copy()` doesn't work with relative paths #74105

Open eh-jogos opened 1 year ago

eh-jogos commented 1 year ago

Production edit: The original report was about several limitations which are explained in the docs and should be discussed in a feature proposal. There had been one bug involved in the MRP, so the issue was renamed to reflect that bug.


Godot version

4.0rc6

System information

Manjaro Linux using i3, Foward+

Issue description

Either I'm doing a dumb mistake or they are not working at all.

Whenever I try to use DirAccess.copy_absolute() or DirAcess.copy() I either get error 12 (ERR_FILE_CANT_OPEN) if I'm using an absolute path or error 7 (ERR_FILE_NOT_FOUND) if I'm using a relative path.

But if I try to use the exact same paths on DirAccess.open() or FileAccess.open() they all work OK image

Steps to reproduce

or see MRP below

Minimal reproduction project

copy_not_working.zip

To make testing easier the MRP has a plugin that adds a tool menu to execute the function below: image

func _copy_icon() -> void:
    var error := DirAccess.copy_absolute("res://icon.svg", "res://to/")
    print("error trying to copy file: %s"%[error])
    var file := FileAccess.open("res://icon.svg", FileAccess.READ_WRITE)
    print("error opening file: %s"%[file.get_open_error()])
    var dir_error := DirAccess.copy_absolute("res://folder_to_copy/", "res://to/")
    print("error trying to copy dir: %s"%[dir_error])
    var dir := DirAccess.open("res://folder_to_copy/")
    print("error opening 'res://folder_to_copy/': %s"%[dir.get_open_error()])
    var error_not_absolute := dir.copy("test.txt", "res://to/")
    print("error trying to copy 'test.txt' with 'copy' on and opened dir, and not from static 'copy_absolute': %s"%[error_not_absolute])
    print(dir.get_files())

it will try to copy either files or folders with copy_absolute() or just copy(), printing the errors and then trying to access the same files/folders and printing the OK errors.

akien-mga commented 1 year ago

The MRP doesn't work (and isn't minimal at all), it has some 3.x code which doesn't compile in 4.0.

eh-jogos commented 1 year ago

I drag and dropped the wrong zip file, that was an MRP from an old issue involving EditorResourcePicker on 3.x, just edited the description uploading the correct one.

akien-mga commented 1 year ago

So I can confirm some issues, but others are misuse of the API. See the documentation:

image

copy and copy_absolute handle files, not directories, and require a path from and to a file. So the first test should be:

    var error := DirAccess.copy_absolute("res://icon.svg", "res://to/copy.svg")

which works fine.

    var dir_error := DirAccess.copy_absolute("res://folder_to_copy/", "res://to/copy_folder")

fails, which seems to be expected given the documentation that copy only handles files. I'm not sure why it doesn't handle directories, but that should probably be discussed in a proposal to change it.

var dir := DirAccess.open("res://folder_to_copy/")
var error_not_absolute := dir.copy("test.txt", "res://to/copy.txt")

That fails too, which seems to be a bug, at least reading the documentation.

eh-jogos commented 1 year ago

Thanks a lot for the help!

I read that documentation a lot of times but I guess I glanced over the "files" word and only focused on paths being absolute or relative and the return error. I guess I assumed it would work with directories since it's a DirAccess function and not a FileAccess function? Anyway, my mistake in not paying more attention to the docs.

KoBeWi commented 1 year ago

I'm not sure why it doesn't handle directories, but that should probably be discussed in a proposal to change it.

I think it simply wasn't implemented. I don't know exactly how file APIs work, but I assume "copying directories" is just a recursive extension of copying files and may not be natively supported as a system function. I may be wrong tho.

RedworkDE commented 1 year ago

In core DirAccess has a copy_dir function to copy directories recursively since 3.0, but it is not exposed to scripting, likely because it was just added to support IOS exports.

artcattleya commented 1 year ago

For files it's return error too (7 if copy_absolute). Only for Exported project, in Editor it's ok. image

KoBeWi commented 1 year ago

Directory contents are different on exported project. 7 is ERR_FILE_NOT_FOUND, which means that the file does not exist in the location you expect. Make sure you added *.db in additional exported files in export preset (the Resources tab in export dialog).

artcattleya commented 1 year ago

Thank you, I added, but nothing changes. Copy func works correctly (because db file opened from user: path), but I still have this error image

KoBeWi commented 1 year ago

The file opens, because you copied it in the editor. Exported project uses the same user data folder. Can you attach some minimal project that reproduces your problem?

artcattleya commented 1 year ago

done https://github.com/artcattleya/godot-test

KoBeWi commented 1 year ago

Looks like copy_absolute() does not work with files inside PCK. Not sure if it's intended. Open a new issue, as it's unrelated to this one.

YuriSizov commented 1 year ago

So the remaining issue that is actually a bug here is that instances of DirAccess fail with relative paths for some reason. This should be investigated.

For exposing dir_copy (as a separate method or as enhancement of this one) please make a proposal. And for the PCK access issue please make a new bug report, if you haven't already.

swlyons commented 3 months ago

Ran into this issue in 4.2. This comment is just confirming the bug and its current behavior in 4.2

File structure is initialized like the following

E:\godot\Test Env\testdirectory\test.txt

func _ready():
    var dir = DirAccess.open("res://testdirectory")
    var error
    print(dir.file_exists("test.txt"))
    error = dir.copy("test.txt", "test2.txt")
    if(error==ERR_FILE_NOT_FOUND):
        print(str("File does not exist ",error))
    error = dir.rename("test.txt", "test2.txt")
    if(error==OK):
        print(str("Renaming works just fine"))
    error = dir.copy("testdirectory/test2.txt", "testdirectory/test3.txt")
    if(error==OK):
        print("So \"relative\" path seems to be relative from res://")

Here's the output

true
File does not exist 7
Renaming works just fine
So "relative" path seems to be relative from res://

and here's the file structure after running

E:\godot\Test Env\testdirectory\test2.txt E:\godot\Test Env\testdirectory\test3.txt

If nothing else, DirAccess.copy and DirAccess.rename should have identical behavior in determining where files are located and it currently does not.