Merge pull request #68 from Nitwel/persistent
Make entities and room persistent
This commit is contained in:
commit
3f79fcaf96
|
@ -148,6 +148,10 @@ Thus I've decided to use a custom event system that is similar to the one used i
|
|||
| `ui_focus_skip` | Focus checking on this element will be skipped |
|
||||
| `ui_focus_stop` | The focus will not be reset. Useful for keyboard |
|
||||
|
||||
### Saving and Loading
|
||||
|
||||
In order for an entity to be saved, it has to implement the `_save` function returning a dictionary of the data that should be saved.
|
||||
When loading, first the saved node gets instantiated, then either the `_load` function is called with the saved data, or when no `_load` function is implemented, the saved data directly applied to the node.
|
||||
|
||||
### Functions
|
||||
|
||||
|
|
|
@ -56,3 +56,9 @@ func _on_click(event):
|
|||
|
||||
HomeApi.set_state(entity_id, "on" if !state else "off", attributes)
|
||||
set_state(!state, brightness)
|
||||
|
||||
func _save():
|
||||
return {
|
||||
"transform": transform,
|
||||
"entity_id": entity_id
|
||||
}
|
|
@ -86,3 +86,9 @@ func load_image(url: String):
|
|||
var texture = ImageTexture.create_from_image(image)
|
||||
logo.texture = texture
|
||||
logo.pixel_size = pixel_size
|
||||
|
||||
func _save():
|
||||
return {
|
||||
"transform": transform,
|
||||
"entity_id": entity_id
|
||||
}
|
|
@ -22,3 +22,9 @@ func set_text(stateInfo):
|
|||
text += " " + stateInfo["attributes"]["unit_of_measurement"]
|
||||
|
||||
label.text = text
|
||||
|
||||
func _save():
|
||||
return {
|
||||
"transform": transform,
|
||||
"entity_id": entity_id
|
||||
}
|
|
@ -31,3 +31,9 @@ func _on_click(event):
|
|||
|
||||
func _on_request_completed():
|
||||
pass
|
||||
|
||||
func _save():
|
||||
return {
|
||||
"transform": transform,
|
||||
"entity_id": entity_id
|
||||
}
|
|
@ -55,4 +55,3 @@ func vector_key_mapping(key_positive_x: int, key_negative_x: int, key_positive_y
|
|||
vec = vec.normalized()
|
||||
|
||||
return vec
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ func _on_entity_click(entity_name):
|
|||
return
|
||||
|
||||
entity.set_position(global_position)
|
||||
get_node("/root").add_child(entity)
|
||||
get_node("/root/Main").add_child(entity)
|
||||
|
||||
func clear_menu():
|
||||
for child in devices_node.get_children():
|
||||
|
|
|
@ -41,7 +41,9 @@ func _ready():
|
|||
wall_mesh.visible = false
|
||||
)
|
||||
|
||||
toggle_edit_button.on_button_up.connect(func():
|
||||
toggle_edit_button.on_button_up.connect(_handle_button_up)
|
||||
|
||||
func _handle_button_up():
|
||||
edit_enabled = false
|
||||
|
||||
wall_corners.visible = false
|
||||
|
@ -65,7 +67,6 @@ func _ready():
|
|||
wall_collisions.add_child(static_body)
|
||||
|
||||
wall_mesh.visible = true
|
||||
)
|
||||
|
||||
func generate_mesh():
|
||||
var corner_count = wall_corners.get_child_count()
|
||||
|
@ -200,3 +201,17 @@ func corners_to_edge_transform(from_pos: Vector3, to_pos: Vector3) -> Transform3
|
|||
|
||||
var edge_transform = Transform3D(edge_basis, edge_position)
|
||||
return edge_transform
|
||||
|
||||
func _save():
|
||||
return {
|
||||
"corners": wall_corners.get_children().map(func(corner): return corner.position),
|
||||
}
|
||||
|
||||
func _load(data):
|
||||
for corner in data["corners"]:
|
||||
add_corner(corner)
|
||||
|
||||
_handle_button_up()
|
||||
|
||||
queue_free()
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
extends Node
|
||||
|
||||
var file_url: String = "user://config.json"
|
||||
const VariantSerializer = preload("res://lib/utils/variant_serializer.gd")
|
||||
|
||||
var file_url: String = "user://config.cfg"
|
||||
|
||||
func save_config(data: Dictionary):
|
||||
var file := FileAccess.open(file_url, FileAccess.WRITE)
|
||||
|
@ -8,7 +10,7 @@ func save_config(data: Dictionary):
|
|||
if file == null:
|
||||
return
|
||||
|
||||
var json_data := JSON.stringify(data)
|
||||
var json_data := JSON.stringify(VariantSerializer.stringify_value(data))
|
||||
file.store_string(json_data)
|
||||
|
||||
func load_config():
|
||||
|
@ -18,6 +20,6 @@ func load_config():
|
|||
return {}
|
||||
|
||||
var json_data := file.get_as_text()
|
||||
var data = JSON.parse_string(json_data)
|
||||
var data = VariantSerializer.parse_value(JSON.parse_string(json_data))
|
||||
|
||||
return data
|
|
@ -47,6 +47,7 @@ func start_adapter(type: String, url: String, token: String):
|
|||
add_child(api)
|
||||
|
||||
api.on_connect.connect(func():
|
||||
SaveSystem.load()
|
||||
on_connect.emit()
|
||||
)
|
||||
|
||||
|
@ -92,3 +93,7 @@ func set_state(entity: String, state: String, attributes: Dictionary = {}):
|
|||
func watch_state(entity: String, callback: Callable):
|
||||
assert(has_connected(), "Not connected")
|
||||
return api.watch_state(entity, callback)
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_WM_CLOSE_REQUEST || what == NOTIFICATION_WM_GO_BACK_REQUEST:
|
||||
SaveSystem.save()
|
85
lib/globals/save_system.gd
Normal file
85
lib/globals/save_system.gd
Normal file
|
@ -0,0 +1,85 @@
|
|||
extends Node
|
||||
|
||||
const VariantSerializer = preload("res://lib/utils/variant_serializer.gd")
|
||||
|
||||
func save():
|
||||
if HomeApi.has_connected() == false:
|
||||
return
|
||||
|
||||
var filename = HomeApi.api.url.split("//")[1].replace("/api/websocket", "").replace(".", "_").replace(":", "_")
|
||||
|
||||
var save_file = FileAccess.open("user://%s.save" % filename, FileAccess.WRITE)
|
||||
|
||||
if save_file == null:
|
||||
return
|
||||
|
||||
var save_tree = _generate_save_tree(get_tree().root.get_node("Main"))
|
||||
|
||||
var json_text = JSON.stringify(save_tree)
|
||||
save_file.store_line(json_text)
|
||||
|
||||
func load():
|
||||
if HomeApi.has_connected() == false:
|
||||
return
|
||||
|
||||
var filename = HomeApi.api.url.split("//")[1].replace("/api/websocket", "").replace(".", "_").replace(":", "_")
|
||||
|
||||
var save_file = FileAccess.open("user://%s.save" % filename, FileAccess.READ)
|
||||
|
||||
if save_file == null:
|
||||
return
|
||||
|
||||
var json_text = save_file.get_line()
|
||||
var save_tree = JSON.parse_string(json_text)
|
||||
|
||||
if save_tree is Array:
|
||||
for tree in save_tree:
|
||||
_build_save_tree(tree)
|
||||
else:
|
||||
_build_save_tree(save_tree)
|
||||
|
||||
|
||||
func _generate_save_tree(node: Node):
|
||||
var children = []
|
||||
|
||||
if node.has_method("_save") == false:
|
||||
for child in node.get_children():
|
||||
var data = _generate_save_tree(child)
|
||||
|
||||
if data is Array:
|
||||
for child_data in data:
|
||||
children.append(child_data)
|
||||
else:
|
||||
children.append(data)
|
||||
return children
|
||||
|
||||
|
||||
var save_tree = {
|
||||
"data": VariantSerializer.stringify_value(node.call("_save")),
|
||||
"parent": node.get_parent().get_path(),
|
||||
"filename": node.get_scene_file_path()
|
||||
}
|
||||
|
||||
for child in node.get_children():
|
||||
var child_data = _generate_save_tree(child)
|
||||
|
||||
if child_data is Array:
|
||||
for data in child_data:
|
||||
children.append(data)
|
||||
else:
|
||||
children.append(child_data)
|
||||
|
||||
save_tree["children"] = children
|
||||
|
||||
return save_tree
|
||||
|
||||
func _build_save_tree(tree: Dictionary):
|
||||
var new_object = load(tree["filename"]).instantiate()
|
||||
|
||||
get_node(tree["parent"]).add_child(new_object)
|
||||
|
||||
if new_object.has_method("_load"):
|
||||
new_object.call("_load", VariantSerializer.parse_value(tree["data"]))
|
||||
else:
|
||||
for key in tree["data"].keys():
|
||||
new_object.set(key, VariantSerializer.parse_value(tree["data"][key]))
|
79
lib/utils/variant_serializer.gd
Normal file
79
lib/utils/variant_serializer.gd
Normal file
|
@ -0,0 +1,79 @@
|
|||
extends Object
|
||||
|
||||
static func stringify_value(value):
|
||||
match typeof(value):
|
||||
TYPE_DICTIONARY:
|
||||
var new_dict = {}
|
||||
for key in value.keys():
|
||||
new_dict[key] = stringify_value(value[key])
|
||||
return new_dict
|
||||
TYPE_ARRAY:
|
||||
return value.map(func(item):
|
||||
return stringify_value(item)
|
||||
)
|
||||
TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING:
|
||||
return value
|
||||
TYPE_VECTOR2:
|
||||
return {
|
||||
"x": value.x,
|
||||
"y": value.y,
|
||||
"_type": "Vector2"
|
||||
}
|
||||
TYPE_VECTOR3:
|
||||
return {
|
||||
"x": value.x,
|
||||
"y": value.y,
|
||||
"z": value.z,
|
||||
"_type": "Vector3"
|
||||
}
|
||||
TYPE_TRANSFORM3D:
|
||||
return {
|
||||
"origin": stringify_value(value.origin),
|
||||
"basis": stringify_value(value.basis),
|
||||
"_type": "Transform3D"
|
||||
}
|
||||
TYPE_BASIS:
|
||||
return {
|
||||
"x": stringify_value(value.x),
|
||||
"y": stringify_value(value.y),
|
||||
"z": stringify_value(value.z),
|
||||
"_type": "Basis"
|
||||
}
|
||||
TYPE_QUATERNION:
|
||||
return {
|
||||
"x": value.x,
|
||||
"y": value.y,
|
||||
"z": value.z,
|
||||
"w": value.w,
|
||||
"_type": "Quaternion"
|
||||
}
|
||||
_:
|
||||
assert(false, "Unsupported type: %s" % typeof(value))
|
||||
|
||||
static func parse_value(value):
|
||||
if typeof(value) == TYPE_ARRAY:
|
||||
return value.map(func(item):
|
||||
return parse_value(item)
|
||||
)
|
||||
elif typeof(value) == TYPE_DICTIONARY:
|
||||
if value.has("_type"):
|
||||
match value["_type"]:
|
||||
"Vector2":
|
||||
return Vector2(value["x"], value["y"])
|
||||
"Vector3":
|
||||
return Vector3(value["x"], value["y"], value["z"])
|
||||
"Transform3D":
|
||||
return Transform3D(parse_value(value["basis"]), parse_value(value["origin"]))
|
||||
"Basis":
|
||||
return Basis(parse_value(value["x"]), parse_value(value["y"]), parse_value(value["z"]))
|
||||
"Quaternion":
|
||||
return Quaternion(value["x"], value["y"], value["z"], value["w"])
|
||||
_:
|
||||
assert(false, "Unsupported type: %s" % value["_type"])
|
||||
else:
|
||||
var new_dict = {}
|
||||
for key in value.keys():
|
||||
new_dict[key] = parse_value(value[key])
|
||||
return new_dict
|
||||
else:
|
||||
return value
|
|
@ -23,6 +23,7 @@ Request="*res://lib/globals/request.gd"
|
|||
HomeApi="*res://lib/globals/home_api.gd"
|
||||
AudioPlayer="*res://lib/globals/audio_player.gd"
|
||||
EventSystem="*res://lib/globals/event_system.gd"
|
||||
SaveSystem="*res://lib/globals/save_system.gd"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user