Fix scene object reference serialization in C# scripts

#3136
This commit is contained in:
Wojtek Figat
2025-02-17 11:57:31 +01:00
parent 9d4d9ccf38
commit 9049093267
7 changed files with 90 additions and 42 deletions

View File

@@ -250,7 +250,7 @@ namespace FlaxEditor.CustomEditors
if (objA == null && objB is string objBStr && objBStr.Length == 0) if (objA == null && objB is string objBStr && objBStr.Length == 0)
return true; return true;
return Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals(objA, objB); return FlaxEngine.Json.JsonSerializer.ValueEquals(objA, objB);
} }
/// <summary> /// <summary>

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System; using System;
using System.Collections;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -23,7 +24,7 @@ namespace FlaxEngine.Json
: base(sb, formatProvider) : base(sb, formatProvider)
{ {
Encoding = encoding; Encoding = encoding;
} }
} }
partial class JsonSerializer partial class JsonSerializer
@@ -174,7 +175,7 @@ namespace FlaxEngine.Json
internal static JsonSerializerSettings CreateDefaultSettings(bool isManagedOnly) internal static JsonSerializerSettings CreateDefaultSettings(bool isManagedOnly)
{ {
//Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals = ValueEquals; Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals = ValueEquals;
if (SerializationBinder is null) if (SerializationBinder is null)
SerializationBinder = new(); SerializationBinder = new();
var settings = new JsonSerializerSettings var settings = new JsonSerializerSettings
@@ -234,8 +235,66 @@ namespace FlaxEngine.Json
/// <returns>True if both objects are equal, otherwise false.</returns> /// <returns>True if both objects are equal, otherwise false.</returns>
public static bool ValueEquals(object objA, object objB) public static bool ValueEquals(object objA, object objB)
{ {
#if false
// Use default value comparision used by C# json serialization library // Use default value comparision used by C# json serialization library
return Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals(objA, objB); return Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals(objA, objB);
#else
// Based on Newtonsoft.Json MiscellaneousUtils.ValueEquals but with customization for prefab object references diff
if (objA == objB)
return true;
if (objA == null || objB == null)
return false;
// Special case when saving reference to prefab object and the objects are different but the point to the same prefab object
// In that case, skip saving reference as it's defined in prefab (will be populated via IdsMapping during deserialization)
if (objA is SceneObject sceneA && objB is SceneObject sceneB && sceneA.HasPrefabLink && sceneB.HasPrefabLink)
return sceneA.PrefabObjectID == sceneB.PrefabObjectID;
// Comparing an Int32 and Int64 both of the same value returns false, make types the same then compare
if (objA.GetType() != objB.GetType())
{
bool IsInteger(object value)
{
var type = value.GetType();
return type == typeof(SByte) ||
type == typeof(Byte) ||
type == typeof(Int16) ||
type == typeof(UInt16) ||
type == typeof(Int32) ||
type == typeof(UInt32) ||
type == typeof(Int64) ||
type == typeof(SByte) ||
type == typeof(UInt64);
}
if (IsInteger(objA) && IsInteger(objB))
return Convert.ToDecimal(objA, CultureInfo.CurrentCulture).Equals(Convert.ToDecimal(objB, CultureInfo.CurrentCulture));
if ((objA is double || objA is float || objA is decimal) && (objB is double || objB is float || objB is decimal))
return Mathd.NearEqual(Convert.ToDouble(objA, CultureInfo.CurrentCulture), Convert.ToDouble(objB, CultureInfo.CurrentCulture));
return false;
}
// Diff on collections
if (objA is IList aList && objB is IList bList)
{
if (aList.Count != bList.Count)
return false;
}
if (objA is IEnumerable aEnumerable && objB is IEnumerable bEnumerable)
{
var aEnumerator = aEnumerable.GetEnumerator();
var bEnumerator = bEnumerable.GetEnumerator();
using var aEnumerator1 = aEnumerator as IDisposable;
using var bEnumerator1 = bEnumerator as IDisposable;
while (aEnumerator.MoveNext())
{
if (!bEnumerator.MoveNext() || !ValueEquals(aEnumerator.Current, bEnumerator.Current))
return false;
}
return !bEnumerator.MoveNext();
}
return objA.Equals(objB);
#endif
} }
/// <summary> /// <summary>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -10054,7 +10054,20 @@
Helper utilities. Helper utilities.
</summary> </summary>
</member> </member>
<member name="M:Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals(System.Object,System.Object)"> <member name="T:Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEqualsDelegate">
<summary>
Compares two objects data.
</summary>
<param name="objA">The object a.</param>
<param name="objB">The object b.</param>
<returns>True if both objects are equal, otherwise false.</returns>
</member>
<member name="F:Newtonsoft.Json.Utilities.MiscellaneousUtils.ValueEquals">
<summary>
The custom value comparision callback.
</summary>
</member>
<member name="M:Newtonsoft.Json.Utilities.MiscellaneousUtils.DefaultValueEquals(System.Object,System.Object)">
<summary> <summary>
The default implementation of the values comparision function. The default implementation of the values comparision function.
</summary> </summary>

View File

@@ -57,22 +57,11 @@ namespace Flax.Deps.Dependencies
// Default build // Default build
GitCheckout(root, "flax-net80"); GitCheckout(root, "flax-net80");
Deploy.VCEnvironment.BuildSolution(solutionPath, configuration, buildPlatform); Deploy.VCEnvironment.BuildSolution(solutionPath, configuration, buildPlatform);
foreach (var platform in options.Platforms)
{ {
BuildStarted(platform); var platform = "JIT";
switch (platform) Log.Info($"Building {GetType().Name} for {platform}");
{ foreach (var file in outputFileNames)
case TargetPlatform.Windows: Utilities.FileCopy(Path.Combine(binFolder, file), Path.Combine(options.PlatformsFolder, "DotNet", file));
case TargetPlatform.Linux:
case TargetPlatform.Mac:
{
foreach (var file in outputFileNames)
{
Utilities.FileCopy(Path.Combine(binFolder, file), Path.Combine(options.PlatformsFolder, "DotNet", file));
}
break;
}
}
} }
// AOT build (disabled codegen) // AOT build (disabled codegen)
@@ -82,24 +71,11 @@ namespace Flax.Deps.Dependencies
Utilities.ReplaceInFile(Path.Combine(root, "Src", "Newtonsoft.Json", "Newtonsoft.Json.csproj"), "HAVE_REGEX;", ";"); Utilities.ReplaceInFile(Path.Combine(root, "Src", "Newtonsoft.Json", "Newtonsoft.Json.csproj"), "HAVE_REGEX;", ";");
Utilities.ReplaceInFile(Path.Combine(root, "Src", "Newtonsoft.Json", "Newtonsoft.Json.csproj"), "HAVE_TYPE_DESCRIPTOR;", ";"); Utilities.ReplaceInFile(Path.Combine(root, "Src", "Newtonsoft.Json", "Newtonsoft.Json.csproj"), "HAVE_TYPE_DESCRIPTOR;", ";");
Deploy.VCEnvironment.BuildSolution(solutionPath, configuration, buildPlatform); Deploy.VCEnvironment.BuildSolution(solutionPath, configuration, buildPlatform);
foreach (var platform in options.Platforms)
{ {
BuildStarted(platform); var platform = "AOT";
switch (platform) Log.Info($"Building {GetType().Name} for {platform}");
{ var file = "Newtonsoft.Json.dll";
case TargetPlatform.UWP: Utilities.FileCopy(Path.Combine(binFolder, file), Path.Combine(options.PlatformsFolder, "DotNet/AOT", file));
case TargetPlatform.XboxOne:
case TargetPlatform.XboxScarlett:
case TargetPlatform.PS4:
case TargetPlatform.PS5:
case TargetPlatform.Switch:
case TargetPlatform.iOS:
{
var file = "Newtonsoft.Json.dll";
Utilities.FileCopy(Path.Combine(binFolder, file), Path.Combine(options.PlatformsFolder, "DotNet/AOT", file));
break;
}
}
} }
} }
} }