File.WRITE is not creating a new file

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By lavaduder
:warning: Old Version Published before Godot 3 was released.

While I’m debugging my game I can not write a new file. I don’t know why.

I use a filepath var to send the file to a location, but it never makes the file. Why?

extends Control

var num = 0
var filepath = user://save/sssss

#Save system

func save():
	var savedictionary = {
	click = num
	}
	return savedictionary

func gamesave():
	var savefile = File.new()
	savefile.open(filepath,File.WRITE)
	var savedict = save()
	var i = 0
	while i < savedict.size():
		savefile.store_line(savedict.to_json())
		i+=1
	savefile.close()

Version is Godot stable 2.1.3

Are you sure the folder save/ exists? Also you should check the return value of savefile.open to know what’s going on.

Zylann | 2017-06-30 10:40

savefile.open() returns The value 12.

ERR_FILE_CANT_OPEN

lavaduder | 2017-06-30 22:29

:bust_in_silhouette: Reply From: wombatstampede

Hi lavaduder,
I see at least two errors in your code. Additionally you should consider what Zylann said: (does the folder “save” exist ?) and make sure that you didn’t accidentially state a directory instead of a file name.

Error 1:
You should put your file name in quotes:
Example:

const SAVE_GAME = "user://save_game.dat"

Error 2:
You seem to initialize your structure wrong. At least I do it like this:

var savedictionary = {
   "click" : num
    }

Finally: You don’t need to write the json line by line:

var savefile = File.new()
savefile.open(filepath, file.WRITE)
savefile.store_string(savedict.to_json())
savefile.close()

The syntax lavaduder uses for the dictionary is correct, it’s an alternate syntax supported by GDScript: GDScript reference — Godot Engine (latest) documentation in English

But the missing quotes are completely wrong indeed xD
The script editor or the console should have warned about this.

Zylann | 2017-06-30 13:43

Okay I’ve tinkered with it a bit, By the way this is not the complete code, I stripped away the bits that were not related. filepath is actually referenced to a string,(VIA FileDialognode.get_current_path()) but I removed that…Sorry!

But it still does NOT create the file.

extends Control

var num = 0
var filepath #Referenced to `get_current_path()` Example: user://ssss 
var savedictionary = {
	"click" :  num
	}


#Save system
func gamesave():
	var savefile = File.new()
	savefile.open(str(filepath),File.WRITE) #have it convert to string just incase.
	savefile.store_var(savedictionary)
	savefile.close()

Here’s the entire code

extends Control

var num = 0
var filepath
var savedictionary = {
	"click" :  num
	}
#Nodes
var file

func _ready():
	file = get_node("file")#The FileDialog
	pass

#Save system
func gamesave():
	var savefile = File.new()
	savefile.open(str(filepath),File.WRITE)
	savefile.store_var(savedictionary)
	savefile.close()
	
func gameload():#WIP fix the save part first
	var savefile = File.new()
	if !savefile.file_exists(str(filepath)):
		print("error File does not exist")
		
		return
	
	var currentline = {}
	savefile.open(filepath, File.READ)
	while (!savefile.eof_reached()):
		currentline.parse_json(savefile.get_line())
		var newobject = load(currentline["filename"]).instance()
		for i in currentline.keys():
			newobject.set(i, currentline[i])
	savefile.close()

#File node
func display_file(filetype):
	file.set_hidden(false)
	file.set_mode(filetype)

func _on_file_confirmed():
	filepath = file.get_current_path()
	if file.MODE_SAVE_FILE:
		print("saving")
		gamesave()
		
	
	elif file.MODE_OPEN_FILE:
		print("load")
		gameload()
		
	else:
		print("Error, Could not load/Save")
	print("Filename: "+str(filepath))

#Click button
func _on_click_pressed():
	num += 1
	get_node("click").set_text(str(num))

#Save and Load buttons
func _on_save_pressed():
	display_file(file.MODE_SAVE_FILE)

func _on_load_pressed():
	display_file(file.MODE_OPEN_FILE)

lavaduder | 2017-06-30 22:26

I took some minutes and tested your code (on 2.1.3).

#1
This is wrong: (IMHO)

if file.MODE_SAVE_FILE: 

You’re testing the value of a constant. This will always be true if the constant is >0. I think you wanted to do this:

if file.get_mode() == file.MODE_SAVE_FILE:

#2

savefile.store_var(savedictionary)

This stores the dictionary in binary format. That isn’t wrong but unnecessary in my opinion. Especially when you try to read it later as text in your load routine.

If you want to store json files (basically text) then in my opinion you can use:

file.store_string(savedictionary.to_json())

for writing and:

data = file.get_as_text()
if (data!=null) and (data!=""):
  dictionary = {}
  var err = dictionary.parse_json(data)
  if err != OK:
    print ("json parse error)
else
  print("file empty!")

something like that for reading…

To get to the point. I understood that you wanted to write your file to the user directory. There’s a little catch indeed. I set the Access attribute to “User data” (in the FileDialog node “file”). But that alone wasn’t enough for the dialogue to propose files in that directory. Additionally i had to init the file node with:

file.set_current_dir("user://")

Then everything worked perfectly. The returned path also contained the “user://” and the file was successfully created+written in the “user://” directory. This is operating system dependend. I tried this on a windows machine. There “user://” links to "C:\Users\<username>\AppData\Roaming\Godot\<projectname>\".

wombatstampede | 2017-07-01 10:18

THANK YOU! The file now writes. But now I have a different problem. The file is empty apparently.

Here’s my updated code.

extends Control
var filepath
var savedictionary = {
	"click" :  0
	}
#Nodes
var file

func _ready():
	file = get_node("file")#The FileDialog
	file.set_current_dir("user://")
	pass

#Save system
func gamesave():
	var savefile = File.new()
	savefile.open(str(filepath),File.WRITE)
	print(savefile.open(str(filepath),File.WRITE))
	savefile.store_string(savedictionary.to_json())
	savefile.close()
	
func gameload():#WIP fix the save part first
	#Check if file exists
	var savefile = File.new()
	if !savefile.file_exists(str(filepath)):
		print("error File does not exist")
		return
	
	#Read File
	var data = savefile.get_as_text()
	if (data!=null)&&(data!=""):
		savedictionary = {}
		var saveparse = savedictionary.parse_json(data)
		if saveparse != OK:
			print("json parse error")
	else:
		print("File is empty") #I am getting this.
	savefile.close()

#File node
func display_file(filetype):
	file.set_hidden(false)
	file.set_mode(filetype)

func _on_file_confirmed():
	filepath = file.get_current_path()
	if file.get_mode() == file.MODE_SAVE_FILE:
		print("saving")
		gamesave()
		
	
	elif file.get_mode() == file.MODE_OPEN_FILE:
		print("load")
		gameload()
		
	else:
		print("Error, Could not load/Save")
	print("Filename: "+str(filepath))

#Click button
func _on_click_pressed():
	savedictionary["click"] += 1
	get_node("click").set_text(str(savedictionary["click"]))

#Save and Load buttons
func _on_save_pressed():
	display_file(file.MODE_SAVE_FILE)

func _on_load_pressed():
	display_file(file.MODE_OPEN_FILE)

lavaduder | 2017-07-01 19:14

Here, a file is generated. It contains: {"click":0}

But there are a few other things which I noticed:
a)
You need to refresh the file list in “file” so that the files show up. Call file.invalidate() after file.set_mode(filetype) for that.
b)
You should save/open the file on the signal file_selected. If you use confirmed then it will always show a confirmation dialogue regardless whether the file exists or not. (It should save the file anyway)

wombatstampede | 2017-07-02 09:59

From what I have tested with. file_selected did not work with MODE_OPEN_FILE I don’t know why, so I used this method instead.

Also when I load the file it empty. Not when I save it. Sorry I should have explained that in more depth.

My reupdated code. (Man I feel like a fool for not knowing this stuff.)

extends Control
var filepath
var savedictionary = {
	"click" :  0
	}
#Nodes
var file

func _ready():
	file = get_node("file")#The FileDialog
	file.set_current_dir("user://")
	pass

#Save system
func gamesave():
	var savefile = File.new()
	savefile.open(str(filepath),File.WRITE)
	print(savefile.open(str(filepath),File.WRITE))
	savefile.store_string(savedictionary.to_json())
	print(str(savedictionary.to_json()))
	savefile.close()
	
func gameload():#WIP fix the save part first
	#Check if file exists
	var savefile = File.new()
	if !savefile.file_exists(str(filepath)):
		print("error File does not exist")
		return
	
	#Read File
	var data = savefile.get_as_text()
	print(str(data))
	if (data!=null)&&(data!=""):
		savedictionary = {}
		var saveparse = savedictionary.parse_json(data)
		if saveparse != OK:
			print("json parse error")
	else:
		print("File is empty")
	savefile.close()

#File node
func display_file(filetype):
	file.set_hidden(false)
	file.set_mode(filetype)

func _on_file_confirmed():
	filepath = file.get_current_path()
	if file.get_mode() == file.MODE_SAVE_FILE:
		print("saving")
		gamesave()
		
	
	elif file.get_mode() == file.MODE_OPEN_FILE:
		print("load")
		gameload()
		
	else:
		print("Error, Could not load/Save")
	print("Filename: "+str(filepath))

#Click button
func _on_click_pressed():
	savedictionary["click"] += 1
	get_node("click").set_text(str(savedictionary["click"]))

#Save and Load buttons
func _on_save_pressed():
	display_file(file.MODE_SAVE_FILE)
	file.invalidate()

func _on_load_pressed():
	display_file(file.MODE_OPEN_FILE)
	file.invalidate()

lavaduder | 2017-07-02 21:39

You didn’t open the file in gameload()

savefile.open(filepath,File.READ)

is missing.

wombatstampede | 2017-07-03 07:27

THANK YOU! It works now.

lavaduder | 2017-07-03 19:36

:bust_in_silhouette: Reply From: thunderrabbit

This helped me find the saved file in Godot 3:

print(OS.get_user_data_dir())

My code saved the file, but my OS could not find it.

On OS X, Godot 3 saved the file for me in ~/Library/Application Support/Godot/app_userdata/(appname)/