Save data refusing to read/write file after uploading the build to Steam, works just fine on DEBUG/exporting and running the .exe

Godot Version

4.4.1

Question

I have no clue why this fails only after it gets run through steam.

I’m posting the whole file for visibility but the real culprit is the “save_achieve_data”, “save_endings_data”, “load_acheivements_data”, and “load_endings_data” functions.

Running the game through Godot DEBUG or even as the exported .exe works just fine. As you can see below “achievements.dat” and “endings.dat” are created.

The problem is, once I upload it to Steam it no longer wants to work. It refuses to create these files, and it won’t read them if they happen to already be in there.

What’s weird is I checked the path as a print through ProjectSettings.globalize_path(Achievements.achieve_save_path) and I just get null, only when through Steam of course.

“item_save_info.save” is created in pretty much the exact same way in a different script, but it is created and read just fine.

I’m kind of at a loss for what to do here, as I have no clue what this could be happening.

extends Node

const achieve_save_path = "user://achievements.dat"

var achievement_save_data: Dictionary = {
	"Completed": [],
	"InProgressCount": []
}

var numachievements: int = 10
var achievementName: Array = []
var achievementUnlockdesc: Array = []
var achievementCountneeded: Array = []
var achievementCountresetonrun: Array = []

const ACHIEVE_JSON_DATA = "res://data/Achievements.json"

const ending_save_path = "user://endings.dat"

var ending_save_data: Array = [false, false, false, false]

func _ready() -> void:
	init_json_data()
	await get_tree().physics_frame
	load_acheivements_data()
	load_endings_data()
	await get_tree().create_timer(0.3).timeout
	initialize_steam()

func load_json(path: String):
	var file = FileAccess.get_file_as_string(path)
	var json
	if file != null:
		json = JSON.parse_string(file)
	else:
		print("error importing file")
	
	return json

func init_json_data() -> void:
	var json = load_json(ACHIEVE_JSON_DATA)
	if json != null:
		numachievements = json.size()
		initialize_tables()
		await get_tree().physics_frame
		for i in range(0, numachievements):
			parse_json(i, json[str(i)])
	else:
		print("error initalizing Items.json")

func parse_json(ID: int, json: Dictionary):
	achievementName[ID] = json["NAME"]
	achievementUnlockdesc[ID] = json["UNLOCKDESCR"]
	achievementCountneeded[ID] = json["COUNTNEEDED"]
	achievementCountresetonrun[ID] = json["RESETCOUNTSONRUN"]

func initialize_tables():
	achievementName.resize(numachievements)
	achievementName.fill("name")
	achievementUnlockdesc.resize(numachievements)
	achievementUnlockdesc.fill("descr")
	achievementCountneeded.resize(numachievements)
	var count: int = 0
	achievementCountneeded.fill(count)
	achievementCountresetonrun.resize(numachievements)
	achievementCountresetonrun.fill(false)

func load_acheivements_data():
	if not FileAccess.file_exists(achieve_save_path):
		var starting_ID: int = 0
		create_achieve_save_data(starting_ID)
		return
		
	var file = FileAccess.open(achieve_save_path, FileAccess.READ)
	achievement_save_data = file.get_var(true)
	file.close()
	#print(item_data)
	
	check_completed_achievements()
	
	if achievement_save_data["Completed"].size() < numachievements:
		print("achievement counts dont match")
		var starting_ID: int = achievement_save_data["Completed"].size()
		create_achieve_save_data(starting_ID)
	#print(achievement_save_data)
	
func load_endings_data():
	#Endings
	if not FileAccess.file_exists(ending_save_path):
		save_endings_data()
		return
		
	var file2 = FileAccess.open(ending_save_path, FileAccess.READ)
	ending_save_data = file2.get_var(true)
	file2.close()

func save_achieve_data():
	var file = FileAccess.open(achieve_save_path, FileAccess.WRITE)
	file.store_var(achievement_save_data,true)
	file.close()

func save_endings_data():
	var file = FileAccess.open(ending_save_path, FileAccess.WRITE)
	file.store_var(ending_save_data,true)
	file.close()

func create_achieve_save_data(starting_ID: int):
	#print("create")
	for i in range(starting_ID, numachievements):
		achievement_save_data["Completed"].append(false)
		achievement_save_data["InProgressCount"].append(0)
	#var path = ProjectSettings.globalize_path(achieve_save_path)
	#DirAccess.make_dir_recursive(path)
	save_achieve_data()
	
	await get_tree().physics_frame
	initialize_steam()

func check_completed_achievements():
	for itemID in range(Items.numitems):
		if Items.itemLocked[itemID]:
			var achieveID: int = Items.itemAchieveID[itemID]
			#print(achieveID)
			if achievement_save_data["Completed"][achieveID]:
				Items.itemLocked[itemID] = false

func get_item_achieve_completed(itemID: int):
	var achieveID: int = Items.itemAchieveID[itemID]
	return achievement_save_data["Completed"][achieveID]

func get_item_achieve_count(itemID: int):
	var achieveID: int = Items.itemAchieveID[itemID]
	return achievement_save_data["InProgressCount"][achieveID]

func item_increase_achieve_count(itemID: int):
	var achieveID: int = Items.itemAchieveID[itemID]
	if not achievement_save_data["Completed"][achieveID]:
		achievement_save_data["InProgressCount"][achieveID] = achievement_save_data["InProgressCount"][achieveID] + 1
		save_achieve_data()
		if achievement_save_data["InProgressCount"][achieveID] >= achievementCountneeded[achieveID]:
			unlock_achievement(itemID)

func reset_run_dependant_item_counts():
	for ID in range(numachievements):
		if achievementCountresetonrun[ID]:
			achievement_save_data["InProgressCount"][ID] = 0
	save_achieve_data()

func item_decrease_achieve_count(itemID: int):
	var achieveID: int = Items.itemAchieveID[itemID]
	if not achievement_save_data["Completed"][achieveID]:
		achievement_save_data["InProgressCount"][achieveID] = achievement_save_data["InProgressCount"][achieveID] - 1
		save_achieve_data()

func item_reset_achieve_count(itemID: int):
	var achieveID: int = Items.itemAchieveID[itemID]
	achievement_save_data["InProgressCount"][achieveID] = 0
	save_achieve_data()

func unlock_achievement(itemID: int, bypass_console_lock: bool = false):
	if !Globals.can_unlock_achievements and !bypass_console_lock:
		return
	var achieveID: int = Items.itemAchieveID[itemID]
	if not achievement_save_data["Completed"][achieveID]:
		print("Achievement '"+achievementName[achieveID]+"' Unlocked")
		achievement_save_data["Completed"][achieveID] = true
		Items.unlock_item(itemID)
		save_achieve_data()
		$AchievementNotification.activate(itemID, achieveID)
	if Globals.can_unlock_achievements:
		var Acheive_API_Name: String = "ACHIEVEMENT_" + str(achieveID)
		set_steam_achievement(Acheive_API_Name)

func is_ending_discovered(endingID: int):
	return ending_save_data[endingID-1]

func unlock_ending(endingID: int):
	#if !Globals.can_unlock_achievements:
		#return
	if !is_ending_discovered(endingID):
		ending_save_data[endingID-1] = true
		save_endings_data()
	var Acheive_API_Name: String = "ENDING_" + str(endingID)
	set_steam_achievement(Acheive_API_Name)

func is_save_completed():
	pass

#STEAM API

func initialize_steam() -> void:
	var initialize_response: Dictionary = Steam.steamInitEx()
	print("Did Steam initialize?: %s" % initialize_response)
	
	if initialize_response['status'] > Steam.STEAM_API_INIT_RESULT_OK:
		print("Failed to initialize Steam: %s" % initialize_response['status'])
		return
	
	load_steam_achievements()

# Process achievements
func load_steam_achievements() -> void:
	for achieveID in range(achievement_save_data.size()):
		var Acheive_API_Name: String = "ACHIEVEMENT_" + str(achieveID)
		var steam_achievement: Dictionary = Steam.getAchievement(Acheive_API_Name)
		
		# Does the achievement actually exist in the Steamworks back-end?
		if not steam_achievement['ret']:
			print("Steam does not have this achievement, defaulting to local value: %s" % achievementName[achieveID])
			continue
		
		if achievement_save_data["Completed"][achieveID] == steam_achievement['achieved']:
			print("Steam achievements match local file, skipping: %s" % achieveID)
			continue
		
		set_steam_achievement(Acheive_API_Name)
	print("Steam achievements loaded")
	#reset_achievement("ACHIEVEMENT_15")

func set_steam_achievement(Acheive_API_Name: String) -> void:
	return
	#if not achievements.has(Acheive_API_Name):
		#print("This achievement does not exist locally: %s" % this_achievement)
		#return
	#achievements[this_achievement] = true
	
	if not Steam.setAchievement(Acheive_API_Name):
		print("Failed to set achievement: %s" % Acheive_API_Name)
		return
	
	print("Set acheivement: %s" % Acheive_API_Name)
	store_steam_data()

func store_steam_data() -> void:
	if not Steam.storeStats():
		print("Failed to store data on Steam, should be stored locally")
		return
	print("Data successfully sent to Steam")

func reset_achievement(Acheive_API_Name: String) -> void:
	print("Resetting achievement %s" % Acheive_API_Name)
	if not Steam.clearAchievement(Acheive_API_Name):
		print("Failed to reset achievement: %s" % Acheive_API_Name)

Might help to see the working script.

Also, have you tried changing the file extension from .dat to .data? Seems unlikely as a fix, but who knows?

I haven’t, but I’ve tried the .ini and .save extensions as well. Will give it a shot though

Here’s that script with item_save_info.save

extends Node

const item_save_path = "user://item_save_info.save"

var item_data: Dictionary = {
	"Locked": [],
	"Discovered": [],
	"TimesChosen": []
}

#func _ready(): #called by Items.gd?
	#load_data()

func load_data():
	if not FileAccess.file_exists(item_save_path):
		var starting_ID: int = 0
		create_save_data(starting_ID)
		return
		
	var file = FileAccess.open(item_save_path, FileAccess.READ)
	item_data = file.get_var(true)
	file.close()
	#print(item_data)
	
	if item_data["Locked"].size() < Items.numitems:
		print("item counts dont match")
		var starting_ID: int = item_data["Locked"].size()
		create_save_data(starting_ID)
	
	await get_tree().create_timer(1.0).timeout
	if !Achievements.get_item_achieve_completed(Items.CIRCUSTICKET):
		check_circus_ticket_unlock()

func check_circus_ticket_unlock():
	var count: int = 0
	var countneeded = Achievements.achievementCountneeded[Items.itemAchieveID[Items.CIRCUSTICKET]]
	for i in range(0, Items.numitems):
		if item_data["Discovered"][i]:
			count+=1
			if count>=countneeded:
				Achievements.unlock_achievement(Items.CIRCUSTICKET)
				break

func save_data():
	#print(item_data)
	var file = FileAccess.open(item_save_path, FileAccess.WRITE)
	file.store_var(item_data,true)
	file.close()

func create_save_data(starting_ID: int):
	for i in range(starting_ID, Items.numitems):
		item_data["Locked"].append(Items.itemLocked[i])
		item_data["Discovered"].append(false)
		item_data["TimesChosen"].append(0)
	save_data()

Figured it out.

Once I moved all the Steam API code into its own script I was able to save and load the data just fine.

Now the new problem was that I could receive steam achievements on the godot editor, but not while playing through steam

So the fix for this was actually pretty easy. So apparently all these files output by godot steam when I exported were actually important, who would’ve guessed? Once I put these files into same spot I uploaded everything else to steam the achievements were given just fine.

1 Like