Skip to content

Commit

Permalink
Add backwards compatibility support (graph conversion)
Browse files Browse the repository at this point in the history
  • Loading branch information
TeodorVecerdi committed Nov 27, 2020
1 parent 43542e1 commit 441438b
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 14 deletions.
1 change: 1 addition & 0 deletions Editor/AssetCallbacks/CreateDlogGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public override void Action(int instanceId, string pathName, string resourceFile
var dlogObject = CreateInstance<DlogGraphObject>();
dlogObject.Initialize(dlogGraph);
dlogObject.DlogGraph.AssetGuid = AssetDatabase.GetAssetPath(instanceId);
dlogObject.DlogGraph.DialogueGraphVersion = DlogVersion.Version.GetValue();
dlogObject.AssetGuid = dlogObject.DlogGraph.AssetGuid;
DlogUtility.CreateFile(pathName, dlogObject, false);
AssetDatabase.ImportAsset(pathName);
Expand Down
4 changes: 1 addition & 3 deletions Editor/Graph/Data/DlogGraphData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class DlogGraphData : ISerializationCallbackReceiver {
[NonSerialized] private List<SerializedEdge> edgeSelectionQueue = new List<SerializedEdge>();
public List<SerializedNode> NodeSelectionQueue => nodeSelectionQueue;
public List<SerializedEdge> EdgeSelectionQueue => edgeSelectionQueue;

public void OnBeforeSerialize() {
if (Owner != null)
IsBlackboardVisible = Owner.IsBlackboardVisible;
Expand All @@ -55,8 +55,6 @@ public void OnBeforeSerialize() {
foreach (var property in properties) {
serializedProperties.Add(new SerializedProperty(property));
}

DialogueGraphVersion = DlogVersion.Version.GetValue();
}

public void OnAfterDeserialize() {
Expand Down
47 changes: 42 additions & 5 deletions Editor/Graph/Views/DlogEditorWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class DlogEditorWindow : EditorWindow {
private EditorView editorView;

private bool deleted;
private bool skipOnDestroyCheck;

public string SelectedAssetGuid {
get => selectedAssetGuid;
Expand Down Expand Up @@ -46,8 +47,9 @@ public void BuildWindow() {
IsBlackboardVisible = dlogObject.IsBlackboardVisible
};
rootVisualElement.Add(editorView);

Refresh();
if (VersionCheck()) {
Refresh();
}
}

private void Update() {
Expand Down Expand Up @@ -95,8 +97,7 @@ private void Update() {
private void DisplayDeletedFromDiskDialog() {
bool shouldClose = true; // Close unless if the same file was replaced

if (EditorUtility.DisplayDialog("Dialogue Graph Missing", AssetDatabase.GUIDToAssetPath(selectedAssetGuid)
+ " has been deleted or moved outside of Unity.\n\nWould you like to save your Graph Asset?", "Save As", "Close Window")) {
if (EditorUtility.DisplayDialog("Dialogue Graph Missing", $"{AssetDatabase.GUIDToAssetPath(selectedAssetGuid)} has been deleted or moved outside of Unity.\n\nWould you like to save your Graph Asset?", "Save As", "Close Window")) {
shouldClose = !SaveAs();
}

Expand Down Expand Up @@ -129,14 +130,50 @@ private void OnEnable() {
this.SetAntiAliasing(4);
}

private bool VersionCheck() {
var fileVersion = (SemVer)dlogObject.DlogGraph.DialogueGraphVersion;
var comparison = fileVersion.CompareTo(DlogVersion.Version.GetValue());
if (comparison < 0) {
if (EditorUtility.DisplayDialog("Version mismatch", $"The graph you are trying to load was saved with an older version of Dialogue Graph.\nIf you proceed with loading it will be converted to the current version. (A backup will be created)\n\nDo you wish to continue?", "Yes", "No")) {
var assetPath = AssetDatabase.GUIDToAssetPath(dlogObject.AssetGuid);
var assetNameSubEndIndex = assetPath.LastIndexOf('.');
var backupAssetPath = assetPath.Substring(0, assetNameSubEndIndex);
DlogUtility.CreateFileNoUpdate($"{backupAssetPath}.backup_{fileVersion}.dlog", dlogObject);
DlogUtility.VersionConvert(fileVersion, dlogObject);
Refresh();
} else {
skipOnDestroyCheck = true;
Close();
}
return false;
}

if (comparison > 0) {
if (EditorUtility.DisplayDialog("Version mismatch", $"The graph you are trying to load was saved with a newer version of Dialogue Graph.\nLoading the file might cause unexpected behaviour or errors. (A backup will be created)\n\nDo you wish to continue?", "Yes", "No")) {
var assetPath = AssetDatabase.GUIDToAssetPath(dlogObject.AssetGuid);
var assetNameSubEndIndex = assetPath.LastIndexOf('.');
var backupAssetPath = assetPath.Substring(0, assetNameSubEndIndex);
DlogUtility.CreateFileNoUpdate($"{backupAssetPath}.backup_{fileVersion}.dlog", dlogObject);
Refresh();
} else {
skipOnDestroyCheck = true;
Close();
}

return false;
}
return true;
}

private void OnDestroy() {
if (IsDirty && EditorUtility.DisplayDialog("Dlog Graph has been modified", "Do you want to save the changes you made in the Dialogue Graph?\nYour changes will be lost if you don't save them.", "Save", "Don't Save")) {
if (!skipOnDestroyCheck && IsDirty && EditorUtility.DisplayDialog("Dlog Graph has been modified", "Do you want to save the changes you made in the Dialogue Graph?\nYour changes will be lost if you don't save them.", "Save", "Don't Save")) {
SaveAsset();
}
}

#region Window Events
private void SaveAsset() {
dlogObject.DlogGraph.DialogueGraphVersion = DlogVersion.Version.GetValue();
DlogUtility.SaveGraph(dlogObject);
UpdateTitle();
}
Expand Down
16 changes: 14 additions & 2 deletions Editor/Util/DlogUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ public static bool CreateFile(string path, DlogGraphObject dlogObject, bool refr
var assetGuid = AssetDatabase.AssetPathToGUID(path);
dlogObject.DlogGraph.AssetGuid = assetGuid;

CreateFileNoUpdate(path, dlogObject, refreshAsset);
return true;
}

public static void CreateFileNoUpdate(string path, DlogGraphObject dlogObject, bool refreshAsset = true) {
var jsonString = JsonUtility.ToJson(dlogObject.DlogGraph, true);
File.WriteAllText(path, jsonString);
if (refreshAsset) AssetDatabase.ImportAsset(path);

return true;
}

public static bool SaveGraph(DlogGraphObject dlogObject, bool refreshAsset = true) {
Expand Down Expand Up @@ -66,6 +69,15 @@ public static DlogGraphObject LoadGraphAtGuid(string assetGuid) {
}
#endregion

/// <summary>
/// Converts (back-ports or forward-ports) dlogObject from <paramref name="fromVersion"/> to the current version.
/// </summary>
/// <param name="fromVersion">Dlog object version</param>
/// <param name="dlogObject">Dlog object to be converted</param>
public static void VersionConvert(SemVer fromVersion, DlogGraphObject dlogObject) {
VersionConverter.ConvertVersion(fromVersion, DlogVersion.Version.GetValue(), dlogObject);
}

/**
* Found this nifty method inside the codebase of ShaderGraph while reverse engineering some functionality.
* I needed something like this so it didn't make sense to reinvent the wheel, so I took this and slightly modified it.
Expand Down
3 changes: 3 additions & 0 deletions Editor/Util/Versioning/Conversion.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions Editor/Util/Versioning/Conversion/ConvertMethodAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace Dlog {
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false), ]
public class ConvertMethodAttribute : Attribute {
public readonly SemVer TargetVersion;

/// <summary>
/// Specifies that tha attached method is a converting method (from one version of Dialogue Graph to another)
/// </summary>
/// <param name="targetVersion">The target version the method converts to.</param>
public ConvertMethodAttribute(string targetVersion) {
TargetVersion = (SemVer)targetVersion;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

72 changes: 72 additions & 0 deletions Editor/Util/Versioning/Conversion/VersionConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using UnityEngine;

namespace Dlog {
public static class VersionConverter {
private static readonly SemVer v111 = (SemVer) "1.1.1";
private static readonly SemVer v112 = (SemVer) "1.1.2";

private static SemVer[] sortedVersions = {v111, v112};

private static bool builtMethodCache;
private static Dictionary<SemVer, Action<DlogGraphObject>> upgradeMethodCache;

private static SemVer GetNextVersion(SemVer from) {
for (var i = 1; i < sortedVersions.Length; i++) {
var comparePrev = from.CompareTo(sortedVersions[i - 1]);
var compareNext = from.CompareTo(sortedVersions[i]);
if (comparePrev >= 0 && compareNext < 0) return sortedVersions[i];
}

return SemVer.Invalid;
}

public static void ConvertVersion(SemVer from, SemVer to, DlogGraphObject dlogObject) {
if (from == to) return;
var next = GetNextVersion(from);
if (next == SemVer.Invalid) {
Debug.Log($"Could not find upgrading method [{from} -> {to}]");
return;
}

UpgradeTo(next, dlogObject);
ConvertVersion(next, to, dlogObject);
}


[ConvertMethod("1.1.2")]
private static void U_112(DlogGraphObject dlogObject) {
dlogObject.DlogGraph.DialogueGraphVersion = v112;
}

private static void UpgradeTo(SemVer version, DlogGraphObject dlogGraphObject) {
if (!builtMethodCache) {
BuildMethodCache();
}
if(upgradeMethodCache.ContainsKey(version))
upgradeMethodCache[version](dlogGraphObject);
else Debug.LogWarning($"Upgrade conversion with [target={version}] is not supported.");
}


private static void BuildMethodCache() {
Debug.Log("Building cache");
builtMethodCache = true;
upgradeMethodCache = new Dictionary<SemVer, Action<DlogGraphObject>>();

var methods = typeof(VersionConverter).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
foreach (var method in methods) {
var attributes = method.GetCustomAttributes<ConvertMethodAttribute>(false).ToList();
if (attributes.Count <= 0) continue;
var attribute = attributes[0];

var methodCall = method.CreateDelegate(typeof(Action<DlogGraphObject>)) as Action<DlogGraphObject>;
upgradeMethodCache.Add(attribute.TargetVersion, methodCall);
}
}
}
}
3 changes: 3 additions & 0 deletions Editor/Util/Versioning/Conversion/VersionConverter.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 14 additions & 4 deletions Editor/Util/Versioning/SemVer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

namespace Dlog {
[Serializable]
public struct SemVer : IEquatable<SemVer> {
public static SemVer Invalid = new SemVer {MAJOR = -1, MINOR = -1, PATCH = -1};
public struct SemVer : IEquatable<SemVer>, IComparable<SemVer> {
public static readonly SemVer Invalid = new SemVer {MAJOR = -1, MINOR = -1, PATCH = -1};

// ReSharper disable once InconsistentNaming
public int MAJOR;

Expand All @@ -15,6 +15,7 @@ public struct SemVer : IEquatable<SemVer> {
// ReSharper disable once InconsistentNaming
public int PATCH;


public SemVer(string versionString) {
if (!IsValid(versionString, out var major, out var minor, out var patch)) {
Debug.LogError($"Could not parse SemVer string {versionString} into format MAJOR.MINOR.PATCH.");
Expand Down Expand Up @@ -52,7 +53,6 @@ public static explicit operator SemVer(string versionString) {
return FromVersionString(versionString);
}


public static SemVer FromVersionString(string versionString) {
return new SemVer(versionString);
}
Expand Down Expand Up @@ -101,5 +101,15 @@ public override int GetHashCode() {
public static bool operator !=(SemVer left, SemVer right) {
return !left.Equals(right);
}

public int CompareTo(SemVer other) {
var majorComparison = MAJOR.CompareTo(other.MAJOR);
if (majorComparison != 0)
return majorComparison;
var minorComparison = MINOR.CompareTo(other.MINOR);
if (minorComparison != 0)
return minorComparison;
return PATCH.CompareTo(other.PATCH);
}
}
}

0 comments on commit 441438b

Please sign in to comment.