00:00
00:00
3p0ch
If you like hard games try my Daxolissian System series

plasmid @3p0ch

Cat

Scientist

Read the manual & try stuff

So Cal

Joined on 2/13/10

Level:
13
Exp Points:
1,708 / 1,880
Exp Rank:
38,121
Vote Power:
5.50 votes
Audio Scouts
1
Rank:
Portal Security
Global Rank:
23,485
Blams:
50
Saves:
377
B/P Bonus:
8%
Whistle:
Normal
Medals:
4,746
Supporter:
3y 10m 15d

Cloud Saving in the Godot NewGrounds API implementation

Posted by 3p0ch - August 9th, 2022


I've updated my Godot implementation for the NewGrounds API to handle cloud saves, and using it will be a little tricky especially if you're not used to using a FuncRef so here's the TL DR on how it works. (Admittedly a little rushed because it seems like this would be useful for the Mobile Game Jam if players want to be able to play both from phone and computer and keep their save data.)


NewGrounds' cloud save system will give your game some number of save slots that can each store a string with some maximum size. NewGrounds might change the number of save slots and maximum size over time, so the specific numbers aren't built into the Godot API implementation and you'll want to check your game project's page and look in the API section to see what the current values are.


You'll probably want to make a Dictionary specifically designed to hold all of the data that should be cloudsaved and keep it in an AutoLoad so it's persistent when you change scenes and every script can access it – I'll include an example of the save_handler.gd script I've been using at the end of this post. While the cloud technically just stores a string in each save slot, the procedure for converting an object to a string to transmit over the internet and then be converted back to an object again (known as serializing and deserializing with JSON) is built into Godot and I set up the API implementation to handle that process for you, so you can blithely consider a slot to be something that holds a Dictionary and not worry about those details.


You can send a request to cloud save a variable (like a complex Dictionary) with the command

Ngio.cloud_save(data_to_be_saved, slot_number)

The slot_number is optional and if you leave it out it will save in slot #1. THERE IS NO SLOT #0 – SAVE SLOT NUMBERING STARTS AT #1 and if you use multiple save slots then that will probably mess you up at some point so consider yourself warned. Once you send the request to save the data, it will take some time for the request to be sent and the data to actually be saved. You'll probably want to just send the request and hope that it succeeds without going to inordinate measures to make sure it worked, but if you do want to show some indication that the cloud save was successful you can use a FuncRef like so

Ngio.cloud_save(data_to_be_saved, slot_number, funcref(self, "_on_cloudsave_complete"))

and have an _on_cloudsave_complete() function in your script like this

func _on_cloudsave_complete(result):
	print("in _on_cloudsave_complete with result ", result)

It should accept one parameter that has the results that the NewGrounds API sends from the cloud save attempt. If you don't include that third parameter of a FuncRef when you call Ngio.cloud_save() then by default it will just print that result to the browser's Developer Tools window in the Console tab where you can check it for debugging purposes. Because I know you've been checking there for error messages to debug your HTML5 games this whole time, right?


You can send a request to load cloud data from a single slot with the command

Ngio.cloud_load(funcref(self, "_on_cloud_loaded"), slot_number)

The slot number is again optional and the default is slot #1. Since it will take time for the cloud data to be transferred over the internet and loaded, you can't just have your code stop at that line and wait until the data is loaded and then in the next line of code start using the data (or else it will seem like the game is frozen during the loading process for what could be seconds or longer). Instead you need to have the first parameter of the cloud_load() call be a FuncRef pointing to a function that will handle the saved data once it finally gets in from the internet, and a simple function could be something like

func _on_cloud_loaded(loaded_data):
	my_dict_of_save_data = loaded_data

It should take one parameter which will be the same as whatever you originally sent in your request to save the data. In practice you'll probably want to get more sophisticated and just before calling Ngio.cloud_load() make some visual cue letting the player know that a cloud load is being attempted, and then in the referenced function give the player an indication that the load is complete, and also check that the loaded data is what you expect it to be with something like

if loaded_data == null:
	print("Could not load data from that slot, was anything saved there in the first place?")
elif loaded_data is Dictionary:
	print("Load successful, here's your data: ", loaded_data)
else:
	handle a messup

The results that the NewGrounds API sends from the load request will also show up the browser's Developer Tools window in the Console pane to help with debugging.


You can also send a request to load from all slots with a single command

Ngio.cloud_load_all(funcref(self, "_on_cloud_loaded"))

With a function that handles each slot of data as it comes in like

func _on_cloud_loaded(slot_number, loaded_data):
	if loaded_data == null:
		Could not load data from that slot
	else:
		Do something with the loaded_data from slot_number


If you want to clear the data from a save slot, just use this command because it's simple enough that I didn't bother to write my own function to streamline it.

Ngio.request("CloudSave.clearSlot", {"id": slot_number})

You can also add a FuncRef as a third parameter in that call, but you probably don't need to.


Lastly, by default I made it show results from any API requests to cloudsave in your browser's Developer Tools – Console tab because my Spidey senses tell me that you'll probably need to do some debugging. But once your game is ready to publish, you can (if you like) change show_cloudsave_results near the top of the Ngio.gd script to false so players can't look there to see where the cloudsaved data lives.


And here's the save_handler script I usually use. It includes functions to save locally either in LocalStorage if you're playing from web or your hard drive if you're playing from the Godot editor, and you might want to keep those because saving and loading locally is faster than having to go through the cloud and could be another backup.

extends Node

var key = "3p0ch_cloud_save_test"

var data = {
	"boolean_test": true,
	"int_test": 12,
	"float_test": 4.3,
	"string_test": "testing_funcref_default",
	"array_test": [
		1,
		2.3,
		"four",
		[5, "six"],
	],
	"dictionary_test": {
		"seven": 8,
		9: "ten",
	},
}

func read_save():
	if OS.has_feature('JavaScript'):
		var JSONstr = JavaScript.eval("window.localStorage.getItem('" + key + "');")
		if (JSONstr):
			return parse_json(JSONstr)
		else:
			return null
	else:
		var file = File.new()
		if not file.file_exists("user://" + key + ".save"):
			return null
		file.open("user://" + key + ".save", File.READ)
		return parse_json(file.get_line())

func write_save():
	if OS.has_feature('JavaScript'):
		JavaScript.eval("window.localStorage.setItem(\"" + key + "\", \'" + to_json(data) + "\');")
	else:
		var file = File.new()
		file.open("user://" + key + ".save", File.WRITE)
		file.store_line(to_json(data))
		file.close()

func open_site(url):
	if OS.has_feature('JavaScript'):
		JavaScript.eval("window.open(\"" + url + "\");")
	else:
		print("Could not open site " + url + " without an HTML5 build")

func switchToSite(url):
	if OS.has_feature('JavaScript'):
		JavaScript.eval("window.open(\"" + url + "\", \"_parent\");")
	else:
		print("Could not switch to site " + url + " without an HTML5 build")

Tags:

11

Comments

Impressive! Thank you for giving that engine some more love!

Whoa, neat! When was cloud save added to the newgrounds API? I'd completely forgotten about it!

AFAIK two days ago. At least that's when this post went up https://www.newgrounds.com/bbs/topic/1507547 and I heard about it from Uneven Prankster while working on the Mobile Jam.

@3p0ch I think Tom kinda buried the lede with that post, I completely skipped over it. Looks like I'll be experimenting around with the API this weekend.

is there a way to add it to your gdevelop one as well?

I can't think of an obvious way of doing it in GDevelop because it would need to be set up to handle asynchronous processes -- basically the game needs to send a request to the NewGrounds API and go on about its business, but set things up so that a function will be waiting for the reply and able to let the game know that the saved data arrived whenever it makes it in from the cloud. It didn't look like GDevelop had a way of handling a JavaScript callback like that, but maybe I'll find a way if I screw around with it enough.

When cloud saving for scratchjr