AdamKormos/SaveMadeEasy

Dictionary with keys that are not strings (ints, floats, vectors, etc) are loaded incorrectly

JUSTCAMH opened this issue · 7 comments

When a dictionary has keys that are not of type String, the keys are loaded inconsistently. It's difficult to explain the bugged case, so please see the example stripped down project below.

To reproduce:

  • Open the project below on 4.2.1.stable
  • Enter play mode, press the save button, then load.
  • When pressing load, it prints the returned dictionary that was loaded. The printed result should be: { 10: "hello!", 20.5: "bye", true: "test", (1, 1): true }
  • Now exit play mode then re-enter it. Press the load button without first saving (this should of course work, because we saved earlier!)
  • BUG: When loading this time, the printed output is actually { "true": "test", "10": "hello!", "20.5": "bye", "(1, 1)": true }. Note the extra "" around the keys; the keys of the dictionary that were previously read as int/float/bool/vector2 are now being read as Strings. If you now press save again, every time you load the dictionary keys will be the correct type until you exit and re-enter play mode again.

So basically, if you save then load, the keys of a dictionary are of different types to if you load without saving. All the code is in test.gd, I would expect that every time you load, the keys of the dictionary are the correct type (int / float / bool / whatever rather than strings)

Save Load Bug Repro.zip

This bug is causing me some major save / load headaches, would be very helpful if this could be fixed!

Hello! This is related to this Issue submitted on the Godot 3 version, probably the same thing is up. Feel free to give it a read.
One alternative I can recommend for the time being, is converting all your keys to string when accessing the dictionary. (Then you'd do var value = dict[str(my_key)], and you'd need to type-cast the returned value too. I know it's not convenient, but this is what came to my mind off the bat while I don't get around to implementing type safety.
AdamKormos/SaveMadeEasyG3#1

I can understand that it's tough to figure out the type of the data to load, but why would it be different in the case of saving then loading vs just loading? The save file that is being loaded has the same data inside in either case, but I'm getting different types for the keys of the dictionaries. Sometimes it gives the type I expect, and sometimes not. But thanks, converting keys to strings makes sense

Because when you call set_var, you add a value to the dictionary run-time, and because of that, your type is known since you pass it a certain type of variable, not text.
When you save to the file however, the file is going to contain text, no matter what you do. Therefore, when you load it back, you're gonna get only text. But I'll try a fairly easy-to-add method shortly to see if that fixes parsing and type-casting.

Does it work for you if you replace the get_var method entirely with this?
func get_var(key_path : String, default = null):
key_path = _sanitize_key_path(key_path)
var var_at_path = _get_variable_at_path(key_path)
if var_at_path != null:
if var_at_path is String:
return str_to_var(var_at_path)
else:
return var_at_path
else:
return default

Ah damn, Github messes up the format both in code view and otherwise. Uploading it to pastebin for better formatting: https://pastebin.com/n8zerkV9

Nope it doesn't, I don't see any difference

Damn. Alrighty, I'll work something out sometime, but in the meanwhile, you can do the manual typecasting, I hope. :)

I've just looked into this again. It seems to me, the keys are the only ones that do not get type-checked automatically.
I added a new method called typecast_dictionary_keys. Godot seems to have some weird gimmicks regarding type conversion, but now your issue seems to be solved on my end. Dropping the modified project zip here, feel free to check it yourself and validate if this approach helps out. :)
Save Load Bug Repro.zip