Parsing JSON back to InputEvent

Godot Version

v4.3.stable.official [77dcf97d8]

Question

I am using a Dictionary stored as JSON to persist user settings. I had no issues with getting all of this to work visually, but ran into a hiccup with overwriting the input_map as it needed an InputEvent and I was only saving the KeyCodes in my JSON Dictionary. I updated my code to store the entire InputEvent in the hopes to just carry that to the input_map. However I am running into issues parsing my JSON file back into a Dictionary[String, InputEvent]. I could refactor and go back to storing control name and keycode then figure out a way to go from keycode to InputEvent with the files I have created, but I would like to try to solve this problem to help my understanding of gdscript. How would you all go about parsing the file below back into a Dictionary[String, InputEvent]

Sample JSON:

{"Controls":{"move_forward":"InputEventKey: keycode=8388607 (Unknown), mods=none, physical=true, location=unspecified, pressed=false, echo=false","move_left":"InputEventKey: keycode=8388607 (Unknown), mods=none, physical=true, location=unspecified, pressed=false, echo=false","rotate_up":"InputEventKey: keycode=81 (Q), mods=none, physical=false, location=unspecified, pressed=true, echo=false"}}

My current parsing code that doesn’t work:

# Retrieves the settings file from User:// or returns an empty dictionary if an error occured
func _get_settings_dictionary() -> Dictionary:
	var file = FileAccess.open(self.SAVE_FILE, FileAccess.READ)
	if file != null:
		var content: String = file.get_as_text()
		if not content.is_empty():
			var json = JSON.new()
			var error = json.parse(content)
			if error == OK:
				var dataReceived: Dictionary = json.data
				var expectedType: Variant.Type = TYPE_DICTIONARY
				if typeof(dataReceived) == expectedType:
					# TODO Need to deserialize InputEvents from the save file
					return dataReceived
				else:
					Logger.error(self.UNEXPTECTED_FORMAT_LOG, [self.SAVE_FILE, expectedType, content], self)
			else:
				Logger.error(self.JSON_ERROR_LOG, [json.get_error_message(), content, json.get_error_line(), self.SAVE_FILE], self)
		else:
			var saveError: Error = FileAccess.get_open_error()
			Logger.error(self.UNABLE_TO_OPEN_LOG, [self.SAVE_FILE, saveError], self)
	return {}

You will have to do something fancy as JSON does not support de-serializing objects like InputEventKey.

I would recommend using the file system like file.store_var(event, true) and ‘get_var’ over json.

1 Like

I store all my bindings in the project input map and gather them like this. I do add a special token “input_” to separate them from other static bindings. and I allow for two sets of bindings for controllers and mouse and keyboards.

func get_project_bindings() -> Dictionary:
	var input_actions:Dictionary = {}
	for action in get_input_actions():
		input_actions[action] = {JOYPAD_EVENT:null,MOUSE_KEYBOARD_EVENT:null}
		var events = InputMap.action_get_events(action)
		if events.is_empty():
			continue
		for event in events:
			if event is InputEventJoypadButton or event is InputEventJoypadMotion:
				input_actions[action][JOYPAD_EVENT] = event
			else:
				input_actions[action][MOUSE_KEYBOARD_EVENT] = event
	print(name,": found actions, ", input_actions.size())
	return input_actions

func get_input_actions() -> Array:
	return InputMap.get_actions().filter(input_action_filter)

func input_action_filter(action_string:String):
	return action_string.begins_with("input_")

bindings = get_project_bindings()

func load_data():
	print(name, ": loding input bindings...")
	var file = FileAccess.open(file_path, FileAccess.READ)
	if not file:
		return
	var data : Array = file.get_var(true)
#	debug_print(name, ": ", dict, "load_bindings")
	if data.is_empty():
		return
	print(name,": actions:", data[0].size()," device:",data[1])
	bindings = data[0]
	joypad_device = data[1]

func save_data():
	print(name,": saving input bindings...")
			# data[ 0 ], data[ 1 ]
	var data = [bindings,joypad_device]
	var file = FileAccess.open(file_path, FileAccess.WRITE)
	file.store_var(data, true)