diff --git a/Source/Engine/Core/ISerializable.h b/Source/Engine/Core/ISerializable.h
index c99051fc0..e706b4b57 100644
--- a/Source/Engine/Core/ISerializable.h
+++ b/Source/Engine/Core/ISerializable.h
@@ -29,6 +29,15 @@ public:
///
typedef JsonWriter SerializeStream;
+ ///
+ /// Serialization callback context container. Used by OnSerializing, OnSerialized, OnDeserializing, OnDeserialized methods.
+ ///
+ struct FLAXENGINE_API CallbackContext
+ {
+ // The deserialization modifier object.
+ ISerializeModifier* Modifier = nullptr;
+ };
+
public:
///
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
index 46f0ec245..4f732353e 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
@@ -1879,6 +1879,25 @@ namespace Flax.Build.Bindings
return false;
}
+ private static void GenerateCppAutoSerializationCallback(BuildData buildData, StringBuilder contents, ApiTypeInfo typeInfo, string callback, string context)
+ {
+ // Generate invoking custom serialization callbacks (pre/post serialization/deserialization)
+ if (typeInfo is ClassStructInfo classStructInfo)
+ {
+ foreach (var functionInfo in classStructInfo.Functions)
+ {
+ if (functionInfo.Name != callback)
+ continue;
+ if (functionInfo.Parameters.Count != 1 && functionInfo.Parameters[0].Type.Type != "CallbackContext")
+ {
+ Log.Warning(GetBuildErrorLocation(typeInfo, $"Invalid serialization callback parameters in function '{typeInfo.Name}::{functionInfo.Name}'"));
+ continue;
+ }
+ contents.AppendLine($" {typeInfo.NativeName}::{functionInfo.Name}({context});");
+ }
+ }
+ }
+
private static void GenerateCppAutoSerialization(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, ApiTypeInfo typeInfo, string typeNameNative)
{
var classInfo = typeInfo as ClassInfo;
@@ -1892,9 +1911,14 @@ namespace Flax.Build.Bindings
CppAutoSerializeProperties.Clear();
CppIncludeFiles.Add("Engine/Serialization/Serialization.h");
+ // ISerializable::CallbackContext
+ var callbackContextSerialize = "{ nullptr }";
+ var callbackContextDeserialize = "{ modifier }";
+
contents.AppendLine();
contents.Append($"void {typeNameNative}::Serialize(SerializeStream& stream, const void* otherObj)").AppendLine();
contents.Append('{').AppendLine();
+ GenerateCppAutoSerializationCallback(buildData, contents, typeInfo, "OnSerializing", callbackContextSerialize);
if (baseType != null)
contents.Append($" {baseType.FullNameNative}::Serialize(stream, otherObj);").AppendLine();
contents.Append($" SERIALIZE_GET_OTHER_OBJ({typeNameNative});").AppendLine();
@@ -1953,11 +1977,13 @@ namespace Flax.Build.Bindings
}
}
+ GenerateCppAutoSerializationCallback(buildData, contents, typeInfo, "OnSerialized", callbackContextSerialize);
contents.Append('}').AppendLine();
contents.AppendLine();
contents.Append($"void {typeNameNative}::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)").AppendLine();
contents.Append('{').AppendLine();
+ GenerateCppAutoSerializationCallback(buildData, contents, typeInfo, "OnDeserializing", callbackContextDeserialize);
if (baseType != null)
contents.Append($" {baseType.FullNameNative}::Deserialize(stream, modifier);").AppendLine();
@@ -1979,6 +2005,7 @@ namespace Flax.Build.Bindings
contents.AppendLine(" }");
}
+ GenerateCppAutoSerializationCallback(buildData, contents, typeInfo, "OnDeserialized", callbackContextDeserialize);
contents.Append('}').AppendLine();
}
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
index 14058aaf5..c22f28212 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
@@ -62,6 +62,15 @@ namespace Flax.Build.Bindings
return $"{context.File.Name}({context.Tokenizer.CurrentLine}): {msg}";
}
+ private static string GetBuildErrorLocation(ApiTypeInfo typeInfo, string msg)
+ {
+ // Make it a link clickable in Visual Studio build output
+ var file = typeInfo.File;
+ if (file != null)
+ return $"{file.Name}(0): {msg}";
+ return msg;
+ }
+
private static string[] ParseComment(ref ParsingContext context)
{
if (context.StringCache == null)