using Dictionary = Godot.Collections.Dictionary; public partial class Workshop : Area3D { /// Returns whether this workshop is owned by the local player. public bool IsLocal => this.IsAuthority(); /// Gets the peer ID of the player owning this workshop. public int PeerId => GetMultiplayerAuthority(); /// Gets the player that owns this workshop. public Player Player => Game.Players.ByPeerId(PeerId); [Export] public Node Objects { get; private set; } public void Clear() { // TODO: This is very ugly please send help. var manager = this.GetNodeOrThrow(nameof(ItemManager)); manager.TrackedItems.Clear(); foreach (var obj in Objects.GetChildren()) { if (obj is Item) { Objects.RemoveChild(obj); obj.QueueFree(); } if (obj.GetNodeOrNull(nameof(Grid)) is Grid g) foreach (var child in g.GetChildren().OfType()) { g.RemoveChild(child); child.QueueFree(); } // TODO: Do we need to do recursion here? } } public void FromDictionary(Dictionary dict) { // TODO: File information shouldn't be handled by this. var version = dict["version"].AsInt32(); if (version != 0) throw new Exception($"Unknown version '{version}'"); Clear(); var manager = this.GetNodeOrThrow(nameof(ItemManager)); var objectsDict = dict["objects"].AsGodotDictionary(); foreach (var (childName, childDict) in objectsDict) FromDictionary(manager, Objects, childName, childDict); } static void FromDictionary(ItemManager manager, Node parent, string name, Dictionary dict) { Node node; if (dict.ContainsKey("scene")) { var sceneName = dict["scene"].AsString(); var scene = ResourceLoader.Load(sceneName, "PackedScene"); var item = scene.Instantiate(); item.Name = name; item.Transform = ArrayToTransform(dict["transform"].AsFloat32Array()); parent.AddChild(item); manager.Add(item); node = item; } else { node = parent.GetNodeOrThrow(name); } if (dict.ContainsKey("grid")) { var grid = node.GetNodeOrThrow(nameof(Grid)); var gridDict = dict["grid"].AsGodotDictionary(); foreach (var (childName, childDict) in gridDict) FromDictionary(manager, grid, childName, childDict); } } public Dictionary ToDictionary() { var result = new Dictionary { ["version"] = 0, }; var objects = new Dictionary(); foreach (var child in Objects.GetChildren()) objects[child.Name] = ToDictionary(child); result["objects"] = objects; return result; } // TODO: Rework the way object are loaded / saved. // Since it will be likely that objects will have varying data // associated with them, only save the scene / object ID and let // the object itself handle loading and saving its data. static Dictionary ToDictionary(Node node) { var result = new Dictionary(); if (node is Item item) { result["scene"] = item.SceneFilePath; result["transform"] = TransformToArray(item.Transform); } var grid = new Dictionary(); if (node.GetNodeOrNull(nameof(Grid)) is Grid g) foreach (var child in g.GetChildren().OfType()) grid[child.Name] = ToDictionary(child); if (grid.Count > 0) result["grid"] = grid; return result; } public void SaveToFile(string path) { using var file = FileAccess.Open(path, FileAccess.ModeFlags.Write) ?? throw new Exception($"Failed to open '{path}' for writing: {FileAccess.GetOpenError()}"); file.StoreString(Json.Stringify(ToDictionary())); } public void LoadFromFile(string path) { using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read) ?? throw new Exception($"Failed to open '{path}' for reading: {FileAccess.GetOpenError()}"); var json = file.GetLine(); var dict = Json.ParseString(json).AsGodotDictionary(); FromDictionary(dict); } static float[] TransformToArray(Transform3D t) => [ t[0, 0], t[1, 0], t[2, 0], t[0, 1], t[1, 1], t[2, 1], t[0, 2], t[1, 2], t[2, 2], t[3, 0], t[3, 1], t[3, 2] ]; static Transform3D ArrayToTransform(float[] a) => new(a[ 0], a[ 1], a[ 2], a[ 3], a[ 4], a[ 5], a[ 6], a[ 7], a[ 8], a[ 9], a[10], a[11]); }