Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use duality serializer for editoruserdata #865

Merged
merged 19 commits into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e766421
Use duality serializer for editoruserdata
Barsonax Jul 13, 2020
9e4c084
MainForm: Renamed variable "str" to "stream" and grouped API methods …
ilexp Jul 16, 2020
69504da
Shortened DualityEditorApp app / user data property names
ilexp Jul 16, 2020
0db620d
Fixed whitespace and tweaked docs for settings properties
ilexp Jul 16, 2020
b080c51
Renamed design-time data property to "PerObjectData"
ilexp Jul 16, 2020
e9568d4
Renamed DesignTimeObjectDataManager API to avoid redundant "DesignTim…
ilexp Jul 16, 2020
87e1103
Reordered DualityEditorUserData and PluginSettings members to match s…
ilexp Jul 16, 2020
31ce8fe
Split up classes to one-class-per-file to match style guide
ilexp Jul 16, 2020
7e9906b
Added XML docs to DualityEditorUserData and PluginSettings
ilexp Jul 16, 2020
9603987
Fixed "FirstSession" flag never being set to false
ilexp Jul 17, 2020
ce61a3e
Rewired editor internals to use the new UserData instance
ilexp Jul 17, 2020
7a0d14f
Fixed FirstSession behavior
ilexp Jul 17, 2020
7f5f8fe
Tweaked SettingsContainer member ordering (style guide) and XML docs …
ilexp Jul 18, 2020
64150ba
Fixed typo in XML docs
ilexp Jul 18, 2020
458ea2c
SettingsContainer Apply method and Saving / Applied events to allow r…
ilexp Jul 18, 2020
796e8f3
Merge branch 'master' into feature/usedualityserializerforeditoruserdata
ilexp Jul 18, 2020
0300363
Moved dock panel serialization from explicit calls to settings event …
ilexp Jul 19, 2020
5eba5b3
Moved plugin user data serialization from explicit calls to settings …
ilexp Jul 19, 2020
42fa673
Moved MainForm launch options UI update to EditorAppData apply event …
ilexp Jul 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Source/Core/Duality/Audio/SoundDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public IEnumerable<SoundInstance> Playing

public SoundDevice()
{
DualityApp.AppData.Changed += this.AppDataChanged;
DualityApp.AppData.Applying += this.OnAppDataApplying;
this.UpdateWorldSettings();
}
~SoundDevice()
Expand All @@ -129,7 +129,7 @@ private void Dispose(bool manually)
if (!this.disposed)
{
this.disposed = true;
DualityApp.AppData.Changed -= this.AppDataChanged;
DualityApp.AppData.Applying -= this.OnAppDataApplying;

// Clear all playing sounds
foreach (SoundInstance inst in this.sounds) inst.Dispose();
Expand Down Expand Up @@ -287,7 +287,7 @@ public void StopAll()
}
}

private void AppDataChanged(object sender, EventArgs e)
private void OnAppDataApplying(object sender, EventArgs e)
{
this.UpdateWorldSettings();
}
Expand Down
6 changes: 2 additions & 4 deletions Source/Core/Duality/DualityApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,13 @@ public static SoundDevice Sound
get { return sound; }
}
/// <summary>
/// [GET / SET] Provides access to Duality's current <see cref="DualityAppData">application data</see>. This is never null.
/// [GET] Provides access to core application / game config, such as physics or backend settings. This is never null.
/// </summary>
public static SettingsContainer<DualityAppData> AppData { get; } = new SettingsContainer<DualityAppData>("AppData.xml");

/// <summary>
/// [GET / SET] Provides access to Duality's current <see cref="DualityUserData">user data</see>. This is never null.
/// [GET] Provides access to game options / user data, such as display resolution or audio volume. This is never null.
/// </summary>
public static SettingsContainer<DualityUserData> UserData { get; } = new SettingsContainer<DualityUserData>(execContext == ExecutionContext.Editor ? "DefaultUserData.xml" : "UserData.xml");

/// <summary>
/// [GET] Returns the <see cref="ExecutionContext"/> in which this DualityApp is currently running.
/// </summary>
Expand Down
6 changes: 0 additions & 6 deletions Source/Core/Duality/Utility/ISettingsContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,10 @@ namespace Duality
{
public interface ISettingsContainer
{
/// <summary>
/// Fired when settings have changed.
/// </summary>
event EventHandler Changed;

/// <summary>
/// Loads the data of the settings.
/// </summary>
void Load();

/// <summary>
/// Saves the data of the settings.
/// </summary>
Expand Down
49 changes: 35 additions & 14 deletions Source/Core/Duality/Utility/SettingsContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,66 @@ namespace Duality
/// A container class to abstract all serialization logic away from settings classes.
/// </summary>
/// <typeparam name="TSettings"></typeparam>
public class SettingsContainer<TSettings> : ISettingsContainer
where TSettings : class, new()
public class SettingsContainer<TSettings> : ISettingsContainer where TSettings : class, new()
{
private readonly string path;

/// <summary>
/// Fired when <see cref="Instance"/> has changed.
/// The path of the file where the settings are loaded from and saved persistently.
/// </summary>
public event EventHandler Changed;

public string Path { get; }
/// <summary>
/// The settings data. This is null till <see cref="Load"/> is called after which it can never be null again.
/// The settings data. Will equal null until <see cref="Load"/> is called for the first time,
/// after which it can never be null again.
/// </summary>
public TSettings Instance { get; private set; }


/// <summary>
/// Fired when the settings <see cref="Instance"/> is being applied after loading or modification.
/// </summary>
public event EventHandler Applying;
/// <summary>
/// Fired when the settings <see cref="Instance"/> is about to be saved, allowing subscribers to
/// extend it with previously missing or updated information.
/// </summary>
public event EventHandler Saving;


/// <summary>
/// Creates a new settings container where the data for the settings will be saved and loaded from <paramref name="path"/>.
/// Creates a new settings container where the data for the settings will be saved and loaded
/// from <paramref name="path"/>.
/// </summary>
/// <param name="path"></param>
public SettingsContainer(string path)
{
this.path = path;
this.Path = path;
}

/// <summary>
/// Loads the data of <typeparamref name="TSettings"/> from file.
/// </summary>
public void Load()
{
this.Instance = Serializer.TryReadObject<TSettings>(this.path, typeof(XmlSerializer)) ?? new TSettings();
this.Changed?.Invoke(this, EventArgs.Empty);
this.Instance = Serializer.TryReadObject<TSettings>(this.Path, typeof(XmlSerializer)) ?? new TSettings();
this.Applying?.Invoke(this, EventArgs.Empty);
}

/// <summary>
/// Saves the data of <typeparamref name="TSettings"/> to file.
/// </summary>
public void Save()
{
Serializer.WriteObject(this.Instance, this.path, typeof(XmlSerializer));
this.Saving?.Invoke(this, EventArgs.Empty);
Serializer.WriteObject(this.Instance, this.Path, typeof(XmlSerializer));
}
/// <summary>
/// Ensures that any modifications to the settings <see cref="Instance"/> are applied to
/// all systems relying on them.
///
/// Note that trivial settings values such as audio volume or similar may be applied directly
/// as values are changed even before <see cref="Apply"/> is called.
/// </summary>
public void Apply()
{
this.Applying?.Invoke(this, EventArgs.Empty);
}
}
}
137 changes: 6 additions & 131 deletions Source/Editor/DualityEditor/DesignTimeObjectData.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.IO;

using Duality;
using Duality.Serialization;
using Duality.Resources;

Expand Down Expand Up @@ -77,42 +74,20 @@ public bool Equals(DataContainer other)
}
}

private static DesignTimeObjectDataManager manager = new DesignTimeObjectDataManager();

internal static void Init()
{
Load(DualityEditorApp.DesignTimeDataFile);
Scene.Leaving += Scene_Leaving;
}
internal static void Terminate()
{
Scene.Leaving -= Scene_Leaving;
Save(DualityEditorApp.DesignTimeDataFile);
}

public static DesignTimeObjectData Get(Guid objId)
{
return manager.RequestDesignTimeData(objId);
}
public static DesignTimeObjectData Get(GameObject obj)
{
return manager.RequestDesignTimeData(obj.Id);
}

private static void Save(string filePath)
{
Serializer.WriteObject(manager, filePath, typeof(BinarySerializer));
}
private static void Load(string filePath)
{
manager = Serializer.TryReadObject<DesignTimeObjectDataManager>(filePath) ?? new DesignTimeObjectDataManager();
}
private static void Scene_Leaving(object sender, EventArgs e)
{
manager.CleanupDesignTimeData();
DualityEditorApp.UserData.Instance?.PerObjectData.Cleanup();
}


public static readonly DesignTimeObjectData Default = new DesignTimeObjectData();

private Guid objId = Guid.Empty;
Expand Down Expand Up @@ -184,6 +159,11 @@ public DesignTimeObjectData(Guid parentId, DesignTimeObjectData baseData)
this.attached = true;
}

public static DesignTimeObjectData Get(GameObject obj)
{
return DualityEditorApp.UserData.Instance.PerObjectData.Request(obj.Id);
}

public T RequestCustomData<T>() where T : new()
{
this.Detach();
Expand Down Expand Up @@ -230,109 +210,4 @@ private void Detach()
this.attached = false;
}
}

internal class DesignTimeObjectDataManager : ISerializeExplicit
{
private static readonly int GuidByteLength = Guid.Empty.ToByteArray().Length;
private const int Version_First = 1;

private Dictionary<Guid,DesignTimeObjectData> dataStore = new Dictionary<Guid,DesignTimeObjectData>();


public DesignTimeObjectData RequestDesignTimeData(Guid objId)
{
DesignTimeObjectData data;
if (!this.dataStore.TryGetValue(objId, out data))
{
data = new DesignTimeObjectData(objId, DesignTimeObjectData.Default);
this.dataStore[objId] = data;
}
return data;
}
public void CleanupDesignTimeData()
{
// Remove trivial / default data
var removeQuery =
from p in this.dataStore
where p.Value.IsDefault
select p.Value.ParentObjectId;
foreach (Guid objId in removeQuery.ToArray())
this.dataStore.Remove(objId);
}
public void OptimizeDesignTimeData()
{
// Optimize data by sharing
var shareValues = this.dataStore.Values.ToList();
while (shareValues.Count > 0)
{
DesignTimeObjectData data = shareValues[shareValues.Count - 1];
foreach (DesignTimeObjectData other in shareValues)
{
data.TryShareData(other);
}
shareValues.RemoveAt(shareValues.Count - 1);
}
}

void ISerializeExplicit.WriteData(IDataWriter writer)
{
this.CleanupDesignTimeData();
this.OptimizeDesignTimeData();

Guid[] guidArray = dataStore.Keys.ToArray();
byte[] data = new byte[guidArray.Length * GuidByteLength];
for (int i = 0; i < guidArray.Length; i++)
{
Array.Copy(
guidArray[i].ToByteArray(), 0,
data, i * GuidByteLength, GuidByteLength);
}
DesignTimeObjectData.DataContainer[] objData = dataStore.Values.Select(d => d.Data).ToArray();
bool[] objDataDirty = dataStore.Values.Select(d => d.IsAttached).ToArray();

writer.WriteValue("version", Version_First);
writer.WriteValue("dataStoreKeys", data);
writer.WriteValue("dataStoreValues", objData);
writer.WriteValue("dataStoreDirtyFlag", objDataDirty);
}
void ISerializeExplicit.ReadData(IDataReader reader)
{
int version;
reader.ReadValue("version", out version);

if (this.dataStore == null)
this.dataStore = new Dictionary<Guid,DesignTimeObjectData>();
else
this.dataStore.Clear();

if (version == Version_First)
{
byte[] data;
DesignTimeObjectData.DataContainer[] objData;
bool[] objDataDirty;
reader.ReadValue("dataStoreKeys", out data);
reader.ReadValue("dataStoreValues", out objData);
reader.ReadValue("dataStoreDirtyFlag", out objDataDirty);

Guid[] guidArray = new Guid[data.Length / GuidByteLength];
byte[] guidData = new byte[GuidByteLength];
for (int i = 0; i < guidArray.Length; i++)
{
Array.Copy(
data, i * GuidByteLength,
guidData, 0, GuidByteLength);
guidArray[i] = new Guid(guidData);
}

for (int i = 0; i < objData.Length; i++)
{
this.dataStore.Add(guidArray[i], new DesignTimeObjectData(guidArray[i], objData[i], objDataDirty[i]));
}
}
else
{
// Unknown format
}
}
}
}
Loading