mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 21:01:52 +00:00
Save and Load script data from files
This commit is contained in:
@@ -33,5 +33,67 @@ namespace Bon.Integrated
|
||||
value = ?;
|
||||
return Deserialize.Value(reader, ValueView(typeof(T), &value), env);
|
||||
}
|
||||
|
||||
public static bool IsBool(BonReader reader, BonEnvironment env = gBonEnv)
|
||||
{
|
||||
return reader.inStr.StartsWith(bool.TrueString, StringComparison.OrdinalIgnoreCase) || reader.inStr.StartsWith(bool.FalseString, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public enum NumberType
|
||||
{
|
||||
None,
|
||||
Integer,
|
||||
Float,
|
||||
Double,
|
||||
Decimal
|
||||
}
|
||||
|
||||
/*public static bool IsNumber(BonReader reader, out NumberType numberType)
|
||||
{
|
||||
numberType = .None;
|
||||
|
||||
StringView tmpView = reader.inStr;
|
||||
|
||||
if (tmpView.StartsWith('-') || tmpView.StartsWith('+'))
|
||||
tmpView.RemoveFromStart(1);
|
||||
|
||||
if (tmpView.StartsWith('NaN', StringComparison.OrdinalIgnoreCase) || tmpView.StartsWith('infinity', StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
numberType = .Double;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isNumber = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (tmpView[0].IsNumber)
|
||||
{
|
||||
isNumber = true;
|
||||
tmpView.RemoveFromStart(1);
|
||||
}
|
||||
else if (tmpView.StartsWith('.'))
|
||||
{
|
||||
// We also don't allow floats like .5f
|
||||
if (!isNumber)
|
||||
return false;
|
||||
|
||||
tmpView.RemoveFromStart(1);
|
||||
|
||||
// Err on the side of caution and assume double for precision
|
||||
numberType = .Double;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (tmpView.StartsWith('f'))
|
||||
numberType = .Float;
|
||||
else if (tmpView.StartsWith('m'))
|
||||
numberType = .Decimal;
|
||||
else if (numberType != .Double)
|
||||
numberType = .Integer;
|
||||
|
||||
return isNumber;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using Bon.Integrated;
|
||||
using Bon;
|
||||
namespace GlitchyEngine.Scripting;
|
||||
|
||||
/// A struct providing access to the components of a mono decimal.
|
||||
struct MonoDecimal
|
||||
{
|
||||
[Bitfield<uint16>(.Private, .BitsAt(16, 0), "_dontUse", .Read)]
|
||||
[Bitfield<uint8>(.Public, .BitsAt(8, 16), "Exponent")]
|
||||
[Bitfield<uint8>(.Private, .BitsAt(7, 24), "_dontUse2")]
|
||||
[Bitfield<bool>(.Public, .BitsAt(1, 31), "Sign")]
|
||||
private uint32 _flags;
|
||||
|
||||
private uint32 _high;
|
||||
private uint32 _low;
|
||||
private uint32 _mid;
|
||||
|
||||
public uint32[3] Mantissa => .(_low, _mid, _high);
|
||||
|
||||
static this
|
||||
{
|
||||
gBonEnv.typeHandlers.Add(typeof(MonoDecimal),
|
||||
((.)new => AssetSerialize, new => AssetDeserialize));
|
||||
}
|
||||
|
||||
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
|
||||
{
|
||||
Log.EngineLogger.Assert(value.type == typeof(Self));
|
||||
|
||||
MonoDecimal decimal = value.Get<MonoDecimal>();
|
||||
|
||||
decimal.ToString(writer.outStr);
|
||||
}
|
||||
|
||||
static Result<void> AssetDeserialize(BonReader reader, ValueView value, BonEnvironment environment, DeserializeValueState state)
|
||||
{
|
||||
Log.EngineLogger.Assert(value.type == typeof(Self));
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
uint64 d, r;
|
||||
|
||||
uint32[3] a = .(_high, _mid, _low);
|
||||
|
||||
char8[30] str = .();
|
||||
char8* ptr = &str[29];
|
||||
|
||||
int digits = 0;
|
||||
|
||||
repeat
|
||||
{
|
||||
r = a [0];
|
||||
|
||||
d = r / 10;
|
||||
r = ((r - d * 10) << 32) + a [1];
|
||||
a [0] = (uint32)d;
|
||||
|
||||
d = r / 10;
|
||||
r = ((r - d * 10) << 32) + a [2];
|
||||
a [1] = (uint32)d;
|
||||
|
||||
d = r / 10;
|
||||
r = r - d * 10;
|
||||
a [2] = (uint32)d;
|
||||
|
||||
*ptr = '0' + (uint8)r;
|
||||
ptr--;
|
||||
digits++;
|
||||
if (digits == Exponent)
|
||||
{
|
||||
*ptr = '.';
|
||||
ptr--;
|
||||
digits++;
|
||||
}
|
||||
}
|
||||
while (a[0] > 0 || a[1] > 0 || a[2] > 0 || digits < (Exponent + 2));
|
||||
|
||||
if (Sign)
|
||||
strBuffer.Append('-');
|
||||
|
||||
strBuffer.Append(StringView(&str[30 - digits], digits));
|
||||
strBuffer.Append('m');
|
||||
}
|
||||
|
||||
public static Result<MonoDecimal> Parse(StringView strBuffer)
|
||||
{
|
||||
var strBuffer;
|
||||
|
||||
MonoDecimal result = .();
|
||||
|
||||
if (strBuffer.StartsWith('-'))
|
||||
{
|
||||
strBuffer.RemoveFromStart(1);
|
||||
result.Sign = true;
|
||||
}
|
||||
|
||||
if (strBuffer.EndsWith('m'))
|
||||
{
|
||||
strBuffer.RemoveFromEnd(1);
|
||||
}
|
||||
|
||||
int decimalPointIndex = strBuffer.IndexOf('.');
|
||||
|
||||
if (decimalPointIndex != -1)
|
||||
{
|
||||
int exponent = strBuffer.Length - decimalPointIndex - 1;
|
||||
|
||||
if (exponent < 0 || exponent > 28)
|
||||
return .Err;
|
||||
|
||||
result.Exponent = (uint8)exponent;
|
||||
|
||||
if (strBuffer.IndexOf('.', decimalPointIndex + 1) != -1)
|
||||
return .Err;
|
||||
}
|
||||
|
||||
repeat
|
||||
{
|
||||
if (strBuffer[0] != '.')
|
||||
{
|
||||
int32 digit = strBuffer[0] - '0';
|
||||
|
||||
if (digit < 0 || digit > 10)
|
||||
return .Err;
|
||||
|
||||
uint64 tmp, carry;
|
||||
|
||||
tmp = result._low * 10;
|
||||
carry = (tmp + (uint64)digit) >> 32;
|
||||
result._low = (uint32)tmp + (uint32)digit;
|
||||
|
||||
tmp = result._mid * 10 + carry;
|
||||
carry = tmp >> 32;
|
||||
result._mid = (uint32)tmp;
|
||||
|
||||
tmp = result._high * 10 + carry;
|
||||
carry = tmp >> 32;
|
||||
result._high = (uint32)tmp;
|
||||
}
|
||||
|
||||
strBuffer.RemoveFromStart(1);
|
||||
}
|
||||
while (!strBuffer.IsEmpty);
|
||||
|
||||
return .Ok(result);
|
||||
}
|
||||
}
|
||||
@@ -528,6 +528,27 @@ static class ScriptGlue
|
||||
id = UUID.Create();
|
||||
}
|
||||
|
||||
[RegisterCall("ScriptGlue::Print_Decimal")]
|
||||
static void Print_Decimal(MonoDecimal monoDecimal)
|
||||
{
|
||||
Log.ClientLogger.Info(scope $"{monoDecimal}");
|
||||
}
|
||||
|
||||
[RegisterCall("ScriptGlue::Get_Decimal")]
|
||||
static void Get_Decimal(MonoString* string, out MonoDecimal monoDecimal)
|
||||
{
|
||||
char8* str = Mono.mono_string_to_utf8(string);
|
||||
|
||||
Result<MonoDecimal> decimalo = MonoDecimal.Parse(StringView(str));
|
||||
|
||||
if (!(decimalo case .Ok(out monoDecimal)))
|
||||
{
|
||||
monoDecimal = default;
|
||||
}
|
||||
|
||||
Mono.mono_free(str);
|
||||
}
|
||||
|
||||
#region Application
|
||||
|
||||
[RegisterCall("ScriptGlue::Application_IsEditor")]
|
||||
@@ -590,7 +611,7 @@ static class ScriptGlue
|
||||
}
|
||||
|
||||
[RegisterCall("ScriptGlue::Serialization_DeserializeField")]
|
||||
public static void Serialization_DeserializeField(void* internalContext, SerializationType expectedType, MonoString* fieldName, uint8* target)
|
||||
public static void Serialization_DeserializeField(void* internalContext, SerializationType expectedType, MonoString* fieldName, uint8* target, out SerializationType actualType)
|
||||
{
|
||||
SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject;
|
||||
|
||||
@@ -598,7 +619,7 @@ static class ScriptGlue
|
||||
|
||||
char8* name = Mono.mono_string_to_utf8(fieldName);
|
||||
|
||||
context.GetField(StringView(name), expectedType, target);
|
||||
context.GetField(StringView(name), expectedType, target, out actualType);
|
||||
|
||||
Mono.mono_free(name);
|
||||
}
|
||||
@@ -612,6 +633,8 @@ static class ScriptGlue
|
||||
|
||||
objectContext = null;
|
||||
|
||||
Log.EngineLogger.AssertDebug(context.AllObjects.ContainsKey(context.Id));
|
||||
|
||||
if (!context.AllObjects.TryGetValue(id, let foundObject))
|
||||
return;
|
||||
|
||||
|
||||
@@ -1,36 +1,45 @@
|
||||
using GlitchyEngine.Core;
|
||||
using System;
|
||||
using Bon;
|
||||
|
||||
namespace GlitchyEngine.Serialization;
|
||||
|
||||
public enum SerializationType : int32
|
||||
[BonTarget]
|
||||
public enum SerializationType : uint32
|
||||
{
|
||||
case None;
|
||||
case None = 0;
|
||||
|
||||
case Bool;
|
||||
|
||||
case Char;
|
||||
case String;
|
||||
|
||||
case Int8;
|
||||
case Int16;
|
||||
case Int32;
|
||||
case Int64;
|
||||
case UInt8;
|
||||
case UInt16;
|
||||
case UInt32;
|
||||
case UInt64;
|
||||
|
||||
case Float;
|
||||
case Double;
|
||||
case Decimal;
|
||||
case Bool = 1 << 31;
|
||||
|
||||
case Enum;
|
||||
case TextTypes = 1 << 30;
|
||||
|
||||
case Char = TextTypes | 1;
|
||||
case String = TextTypes | 2;
|
||||
|
||||
case Number = 1 << 29;
|
||||
case Integer = Number | 1 << 28;
|
||||
|
||||
case Int8 = Integer | 1;
|
||||
case Int16 = Integer | 2;
|
||||
case Int32 = Integer | 3;
|
||||
case Int64 = Integer | 4;
|
||||
case UInt8 = Integer | 5;
|
||||
case UInt16 = Integer | 6;
|
||||
case UInt32 = Integer | 7;
|
||||
case UInt64 = Integer | 8;
|
||||
|
||||
case EntityReference;
|
||||
case ComponentReference;
|
||||
case FloatingPoint = Number | 1 << 27;
|
||||
|
||||
case Float = FloatingPoint | 1;
|
||||
case Double = FloatingPoint | 2;
|
||||
case Decimal = FloatingPoint | 3;
|
||||
|
||||
case Enum = 1 << 26;
|
||||
|
||||
case ObjectReference;
|
||||
case EntityReference = 1 << 25;
|
||||
case ComponentReference = 1 << 24;
|
||||
|
||||
case ObjectReference = 1 << 23;
|
||||
|
||||
public int GetSize()
|
||||
{
|
||||
@@ -39,7 +48,7 @@ public enum SerializationType : int32
|
||||
case .Bool:
|
||||
return 1;
|
||||
case .Char:
|
||||
return 1;
|
||||
return 2;
|
||||
case .Int8, .UInt8:
|
||||
return 1;
|
||||
case .Int16, .UInt16:
|
||||
@@ -64,4 +73,16 @@ public enum SerializationType : int32
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNumber => this.HasFlag(.Number);
|
||||
public bool IsInteger => this.HasFlag(.Integer);
|
||||
public bool IsFloatpoint => this.HasFlag(.FloatingPoint);
|
||||
|
||||
public bool CanConvertTo(SerializationType destinationType)
|
||||
{
|
||||
if (this.IsNumber && destinationType.IsNumber)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,13 @@ using GlitchyEngine.Scripting;
|
||||
using Mono;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Bon.Integrated;
|
||||
using Bon;
|
||||
using System.Reflection;
|
||||
|
||||
namespace GlitchyEngine.Serialization;
|
||||
|
||||
[BonTarget]
|
||||
class SerializedObject
|
||||
{
|
||||
[Union]
|
||||
@@ -61,6 +65,25 @@ class SerializedObject
|
||||
TypeName = typeNameCopy;
|
||||
}
|
||||
|
||||
private void SetDataSimple(SerializationType fieldType, void* rawValue, ref FieldData data)
|
||||
{
|
||||
Internal.MemCpy(&data.RawData, rawValue, fieldType.GetSize());
|
||||
}
|
||||
|
||||
private void SetRawData<T>(SerializationType fieldType, T rawValue, ref FieldData data)
|
||||
{
|
||||
#unwarn
|
||||
Internal.MemCpy(&data.RawData, &rawValue, fieldType.GetSize());
|
||||
}
|
||||
|
||||
private void AddField(StringView fieldName, SerializationType fieldType, FieldData data)
|
||||
{
|
||||
String nameCopy = new String(fieldName);
|
||||
_ownedString.Add(nameCopy);
|
||||
|
||||
Fields.Add(nameCopy, (fieldType, data));
|
||||
}
|
||||
|
||||
public void AddField(StringView name, SerializationType primitiveType, MonoObject* value, MonoString* fullTypeName)
|
||||
{
|
||||
FieldData data = .();
|
||||
@@ -103,25 +126,60 @@ class SerializedObject
|
||||
default:
|
||||
void* rawValue = Mono.mono_object_unbox(value);
|
||||
|
||||
Internal.MemCpy(&data.RawData, rawValue, primitiveType.GetSize());
|
||||
|
||||
String nameCopy = new String(name);
|
||||
_ownedString.Add(nameCopy);
|
||||
SetDataSimple(primitiveType, rawValue, ref data);
|
||||
}
|
||||
|
||||
String nameCopy = new String(name);
|
||||
_ownedString.Add(nameCopy);
|
||||
|
||||
Fields.Add(nameCopy, (primitiveType, data));
|
||||
AddField(name, primitiveType, data);
|
||||
}
|
||||
|
||||
public void GetField(StringView fieldName, SerializationType expectedType, uint8* target)
|
||||
|
||||
/*private void ConvertType(SerializationType fieldType, in FieldData fieldData, SerializationType expectedType, uint8* target)
|
||||
{
|
||||
#unwarn
|
||||
void* rawData = &fieldData.RawData;
|
||||
|
||||
void SimpleConversion<TActual, TExpected>() where TExpected: operator explicit TActual
|
||||
{
|
||||
TActual actualValue = *(TActual*)rawData;
|
||||
*(TExpected*)target = (TExpected)actualValue;
|
||||
}
|
||||
|
||||
if (fieldType.IsNumber && expectedType.IsNumber)
|
||||
{
|
||||
Convert.ConvertTo(Variant.Create(fieldType.));
|
||||
}
|
||||
|
||||
switch(fieldType)
|
||||
{
|
||||
case .Float:
|
||||
if (expectedType == .Double)
|
||||
SimpleConversion<float, double>();
|
||||
case .Double:
|
||||
if (expectedType == .Float)
|
||||
SimpleConversion<double, float>();
|
||||
default:
|
||||
Log.EngineLogger.Error($"No field data conversion from {fieldType} to {expectedType}.");
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
public void GetField(StringView fieldName, SerializationType expectedType, uint8* target, out SerializationType actualType)
|
||||
{
|
||||
actualType = .None;
|
||||
|
||||
if (!Fields.TryGetValue(fieldName, let field))
|
||||
return;
|
||||
|
||||
if (expectedType != field.PrimitiveType)
|
||||
actualType = field.PrimitiveType;
|
||||
|
||||
/*if (expectedType != field.PrimitiveType)
|
||||
{
|
||||
if (field.PrimitiveType.CanConvertTo(expectedType))
|
||||
{
|
||||
ConvertType(field.PrimitiveType, field.Data, expectedType, target);
|
||||
}
|
||||
|
||||
return;
|
||||
}*/
|
||||
|
||||
switch (field.PrimitiveType)
|
||||
{
|
||||
@@ -168,4 +226,396 @@ class SerializedObject
|
||||
if (exception != null)
|
||||
ScriptEngine.[Friend]HandleMonoException(exception, scriptInstance);
|
||||
}
|
||||
|
||||
static this
|
||||
{
|
||||
gBonEnv.typeHandlers.Add(typeof(SerializedObject),
|
||||
((.)new => AssetSerialize, null));
|
||||
}
|
||||
|
||||
static T GetFieldDataAs<T>(FieldData fieldData)
|
||||
{
|
||||
return *(T*)&fieldData.RawData;
|
||||
}
|
||||
|
||||
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
|
||||
{
|
||||
Log.EngineLogger.Assert(value.type == typeof(Self));
|
||||
|
||||
SerializedObject object = value.Get<Self>();
|
||||
|
||||
writer.Type(object.TypeName);
|
||||
|
||||
using (writer.ObjectBlock())
|
||||
{
|
||||
Serialize.Value(writer, "ID", object.Id, environment);
|
||||
|
||||
for (let (fieldName, field) in object.Fields)
|
||||
{
|
||||
writer.Identifier(fieldName);
|
||||
switch (field.PrimitiveType)
|
||||
{
|
||||
case .Bool:
|
||||
Serialize.Value(writer, GetFieldDataAs<bool>(field.Data), environment);
|
||||
case .Char:
|
||||
Serialize.Value(writer, GetFieldDataAs<char16>(field.Data), environment);
|
||||
case .String:
|
||||
if (field.Data.StringView.IsNull)
|
||||
{
|
||||
writer.Type("string");
|
||||
}
|
||||
#unwarn
|
||||
Serialize.Value(writer, ValueView(typeof(StringView), &field.Data.StringView), environment);
|
||||
|
||||
case .Int8:
|
||||
writer.Type("int8");
|
||||
Serialize.Value(writer, GetFieldDataAs<int8>(field.Data), environment);
|
||||
case .Int16:
|
||||
writer.Type("int16");
|
||||
Serialize.Value(writer, GetFieldDataAs<int16>(field.Data), environment);
|
||||
case .Int32:
|
||||
writer.Type("int32");
|
||||
Serialize.Value(writer, GetFieldDataAs<int32>(field.Data), environment);
|
||||
case .Int64:
|
||||
writer.Type("int64");
|
||||
Serialize.Value(writer, GetFieldDataAs<int64>(field.Data), environment);
|
||||
case .UInt8:
|
||||
writer.Type("uint8");
|
||||
Serialize.Value(writer, GetFieldDataAs<uint8>(field.Data), environment);
|
||||
case .UInt16:
|
||||
writer.Type("uint16");
|
||||
Serialize.Value(writer, GetFieldDataAs<uint16>(field.Data), environment);
|
||||
case .UInt32:
|
||||
writer.Type("uint32");
|
||||
Serialize.Value(writer, GetFieldDataAs<uint32>(field.Data), environment);
|
||||
case .UInt64:
|
||||
writer.Type("uint64");
|
||||
Serialize.Value(writer, GetFieldDataAs<uint64>(field.Data), environment);
|
||||
|
||||
case .Float:
|
||||
writer.Type("float");
|
||||
Serialize.Value(writer, GetFieldDataAs<float>(field.Data), environment);
|
||||
case .Double:
|
||||
writer.Type("double");
|
||||
Serialize.Value(writer, GetFieldDataAs<double>(field.Data), environment);
|
||||
case .Decimal:
|
||||
writer.Type("decimal");
|
||||
Serialize.Value(writer, GetFieldDataAs<MonoDecimal>(field.Data), environment);
|
||||
|
||||
case .Enum:
|
||||
writer.Type("Enum");
|
||||
#unwarn
|
||||
Serialize.Value(writer, ValueView(typeof(StringView), &field.Data.StringView), environment);
|
||||
case .EntityReference:
|
||||
writer.Type("Entity");
|
||||
if (field.Data.EngineObject.FullTypeName != null)
|
||||
writer.Type(field.Data.EngineObject.FullTypeName);
|
||||
Serialize.Value(writer, field.Data.EngineObject.ID, environment);
|
||||
case .ComponentReference:
|
||||
writer.Type("Component");
|
||||
if (field.Data.EngineObject.FullTypeName != null)
|
||||
writer.Type(field.Data.EngineObject.FullTypeName);
|
||||
Serialize.Value(writer, field.Data.EngineObject.ID, environment);
|
||||
case .ObjectReference:
|
||||
writer.outStr.Append('&');
|
||||
GetFieldDataAs<UUID>(field.Data).ToString(writer.outStr);
|
||||
writer.EntryEnd();
|
||||
default:
|
||||
Log.EngineLogger.Warning("Unhandled primitive type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//AssetHandle.[Friend]AssetSerialize(writer, ValueView(typeof(AssetHandle), &handle), environment, state);
|
||||
|
||||
//writer.String(identifier);
|
||||
}
|
||||
|
||||
private static Result<StringView> NestedIdentifier(BonReader reader)
|
||||
{
|
||||
let name = ParseNestedName(reader);
|
||||
if (name.Length == 0)
|
||||
reader.[Friend]Error!("Expected identifier name");
|
||||
|
||||
Try!(reader.ConsumeEmpty());
|
||||
|
||||
if (!reader.[Friend]Check('='))
|
||||
reader.[Friend]Error!("Expected equals");
|
||||
|
||||
Try!(reader.ConsumeEmpty());
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private static StringView ParseNestedName(BonReader reader)
|
||||
{
|
||||
var nameLen = 0;
|
||||
for (; nameLen < reader.inStr.Length; nameLen++)
|
||||
{
|
||||
let char = reader.inStr[nameLen];
|
||||
if (!char.IsLetterOrDigit && char != '_' && char != '.')
|
||||
break;
|
||||
}
|
||||
|
||||
let name = reader.inStr.Substring(0, nameLen);
|
||||
reader.inStr.RemoveFromStart(nameLen);
|
||||
return name;
|
||||
}
|
||||
|
||||
public static Result<SerializedObject> BonDeserialize(BonReader reader, Dictionary<UUID, SerializedObject> allObjects, BonEnvironment environment = gBonEnv)
|
||||
{
|
||||
StringView type = Try!(reader.Type());
|
||||
|
||||
Try!(reader.ObjectBlock());
|
||||
|
||||
Try!(Deserialize.Value<UUID>(reader, "ID", let objectId, environment));
|
||||
|
||||
SerializedObject object = new SerializedObject(allObjects, type, objectId);
|
||||
|
||||
while (reader.ObjectHasMore())
|
||||
{
|
||||
// TODO: Consume until we hit a comma. In case the current field couldn't be deserialized
|
||||
Try!(reader.EntryEnd());
|
||||
|
||||
FieldData fieldData = .();
|
||||
SerializationType fieldType = .None;
|
||||
|
||||
StringView fieldName = Try!(NestedIdentifier(reader));
|
||||
|
||||
StringView fieldTypeName = String.Empty;
|
||||
|
||||
if (reader.IsTyped())
|
||||
{
|
||||
fieldTypeName = reader.Type();
|
||||
}
|
||||
|
||||
HandleField: do
|
||||
{
|
||||
if (!fieldTypeName.IsWhiteSpace)
|
||||
{
|
||||
if (fieldTypeName == "float")
|
||||
{
|
||||
float floatValue = 0.0f;
|
||||
|
||||
Deserialize.[Friend]Float!(typeof(float), reader, ValueView(typeof(float), &floatValue));
|
||||
fieldType = .Float;
|
||||
object.SetRawData(fieldType, floatValue, ref fieldData);
|
||||
|
||||
break HandleField;
|
||||
}
|
||||
else if (fieldTypeName == "double")
|
||||
{
|
||||
double doubleValue = 0.0f;
|
||||
|
||||
Deserialize.[Friend]Float!(typeof(double), reader, ValueView(typeof(double), &doubleValue));
|
||||
fieldType = .Double;
|
||||
object.SetRawData(fieldType, doubleValue, ref fieldData);
|
||||
|
||||
break HandleField;
|
||||
}
|
||||
else if (fieldTypeName.StartsWith("int"))
|
||||
{
|
||||
int64 intValue = 0;
|
||||
|
||||
Deserialize.[Friend]Integer!(typeof(int64), reader, ValueView(typeof(int64), &intValue));
|
||||
|
||||
if (fieldTypeName.EndsWith("8"))
|
||||
{
|
||||
fieldType = .Int8;
|
||||
object.SetRawData(fieldType, (int8)intValue, ref fieldData);
|
||||
}
|
||||
else if (fieldTypeName.EndsWith("16"))
|
||||
{
|
||||
fieldType = .Int16;
|
||||
object.SetRawData(fieldType, (int16)intValue, ref fieldData);
|
||||
}
|
||||
else if (fieldTypeName.EndsWith("32"))
|
||||
{
|
||||
fieldType = .Int32;
|
||||
object.SetRawData(fieldType, (int32)intValue, ref fieldData);
|
||||
}
|
||||
else if (fieldTypeName.EndsWith("64"))
|
||||
{
|
||||
fieldType = .Int64;
|
||||
object.SetRawData(fieldType, (int64)intValue, ref fieldData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ClientLogger.Error($"Unknown integer type {fieldTypeName}");
|
||||
}
|
||||
|
||||
break HandleField;
|
||||
}
|
||||
else if (fieldTypeName.StartsWith("uint"))
|
||||
{
|
||||
uint64 intValue = 0;
|
||||
|
||||
Deserialize.[Friend]Integer!(typeof(uint64), reader, ValueView(typeof(uint64), &intValue));
|
||||
|
||||
if (fieldTypeName.EndsWith("8"))
|
||||
{
|
||||
fieldType = .UInt8;
|
||||
object.SetRawData(fieldType, (uint8)intValue, ref fieldData);
|
||||
}
|
||||
else if (fieldTypeName.EndsWith("16"))
|
||||
{
|
||||
fieldType = .UInt16;
|
||||
object.SetRawData(fieldType, (uint16)intValue, ref fieldData);
|
||||
}
|
||||
else if (fieldTypeName.EndsWith("32"))
|
||||
{
|
||||
fieldType = .UInt32;
|
||||
object.SetRawData(fieldType, (uint32)intValue, ref fieldData);
|
||||
}
|
||||
else if (fieldTypeName.EndsWith("64"))
|
||||
{
|
||||
fieldType = .UInt64;
|
||||
object.SetRawData(fieldType, (uint64)intValue, ref fieldData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ClientLogger.Error($"Unknown integer type {fieldTypeName}");
|
||||
}
|
||||
|
||||
break HandleField;
|
||||
}
|
||||
else if (fieldTypeName == "decimal")
|
||||
{
|
||||
StringView numberView = Try!(reader.Floating());
|
||||
|
||||
if (reader.inStr.StartsWith('m'))
|
||||
reader.inStr.RemoveFromStart(1);
|
||||
|
||||
MonoDecimal decimalValue = Try!(MonoDecimal.Parse(numberView));
|
||||
fieldType = .Decimal;
|
||||
object.SetRawData(fieldType, decimalValue, ref fieldData);
|
||||
|
||||
break HandleField;
|
||||
}
|
||||
else if (fieldTypeName == "Enum")
|
||||
{
|
||||
String enumValue = new .();
|
||||
Deserialize.String!(reader, ref enumValue, environment);
|
||||
|
||||
object._ownedString.Add(enumValue);
|
||||
fieldData.StringView = enumValue;
|
||||
fieldType = .Enum;
|
||||
|
||||
break HandleField;
|
||||
}
|
||||
else if (fieldTypeName == "Entity" || fieldTypeName == "Component")
|
||||
{
|
||||
String entityTypeName = null;
|
||||
|
||||
if (reader.IsTyped())
|
||||
{
|
||||
StringView entityTypeNameView = Try!(reader.Type());
|
||||
|
||||
entityTypeName = new String(entityTypeNameView);
|
||||
|
||||
object._ownedString.Add(entityTypeName);
|
||||
}
|
||||
|
||||
// Object Reference
|
||||
uint64 id = 0;
|
||||
|
||||
Deserialize.[Friend]Integer!(typeof(uint64), reader, ValueView(typeof(uint64), &id));
|
||||
|
||||
UUID reference = UUID(id);
|
||||
fieldType = (fieldTypeName == "Entity") ? .EntityReference : .ComponentReference;
|
||||
fieldData.EngineObject = (FullTypeName: entityTypeName, ID: reference);
|
||||
|
||||
break HandleField;
|
||||
}
|
||||
}
|
||||
|
||||
if (Deserialize.IsBool(reader))
|
||||
{
|
||||
bool boolValue = Try!(reader.Bool());
|
||||
fieldType = .Bool;
|
||||
object.SetDataSimple(fieldType, &boolValue, ref fieldData);
|
||||
}
|
||||
else if (reader.[Friend]Check('\'', false))
|
||||
{
|
||||
char32 c = Try!(reader.Char());
|
||||
|
||||
fieldType = .Char;
|
||||
object.SetDataSimple(fieldType, &c, ref fieldData);
|
||||
}
|
||||
else if (reader.[Friend]Check('"', false) || fieldTypeName == "string")
|
||||
{
|
||||
if (reader.inStr.StartsWith("null"))
|
||||
{
|
||||
fieldData.StringView = null;
|
||||
fieldType = .String;
|
||||
reader.inStr.RemoveFromStart(4);
|
||||
}
|
||||
else
|
||||
{
|
||||
String target = new .();
|
||||
Deserialize.String!(reader, ref target, environment);
|
||||
|
||||
object._ownedString.Add(target);
|
||||
fieldData.StringView = target;
|
||||
fieldType = .String;
|
||||
}
|
||||
}
|
||||
// else if (Deserialize.IsNumber(reader, let numberType))
|
||||
// {
|
||||
// if (numberType == .Double)
|
||||
// {
|
||||
// double double = Try!(Deserialize.ParseFloat<double>());
|
||||
// fieldType = .Double;
|
||||
// object.SetDataSimple(fieldType, &reference, ref fieldData);
|
||||
// }
|
||||
// else if (numberType == .Float)
|
||||
// {
|
||||
// float double = Try!(Deserialize.ParseFloat<float>());
|
||||
// fieldType = .Float;
|
||||
// object.SetDataSimple(fieldType, &reference, ref fieldData);
|
||||
// }
|
||||
// else if (numberType == .Integer)
|
||||
// {
|
||||
// bool isNegative = false;
|
||||
|
||||
// if (reader.inStr.StartsWith('-'))
|
||||
// {
|
||||
// isNegative = true;
|
||||
// }
|
||||
|
||||
// float double = Try!(Deserialize.ParseInt<u>());
|
||||
// fieldType = .Float;
|
||||
// object.SetDataSimple(fieldType, &reference, ref fieldData);
|
||||
// }
|
||||
// }
|
||||
else if (reader.IsReference())
|
||||
{
|
||||
// Object Reference
|
||||
StringView referenceView = Try!(reader.Reference());
|
||||
|
||||
let referenceResult = uint64.Parse(referenceView);
|
||||
|
||||
if (referenceResult case .Ok(let referenceInt))
|
||||
{
|
||||
UUID reference = UUID(referenceInt);
|
||||
fieldType = .ObjectReference;
|
||||
object.SetRawData(fieldType, reference, ref fieldData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldType != .None)
|
||||
{
|
||||
object.AddField(fieldName, fieldType, fieldData);
|
||||
}
|
||||
|
||||
Try!(reader.ConsumeEmpty());
|
||||
}
|
||||
|
||||
Try!(reader.ObjectBlockEnd());
|
||||
|
||||
return .Ok(object);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Collections;
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEngine.Content;
|
||||
using GlitchyEngine.Scripting;
|
||||
using GlitchyEngine.Serialization;
|
||||
|
||||
namespace GlitchyEngine.World;
|
||||
|
||||
@@ -26,6 +27,12 @@ class SceneSerializer
|
||||
// Maps from ID in the prefab file to the actual ID in the scene.
|
||||
private Dictionary<UUID, UUID> _fileToSceneId;
|
||||
|
||||
private Dictionary<UUID, SerializedObject> _serializedObjects ~ DeleteDictionaryAndValues!(_);
|
||||
|
||||
private HashSet<UUID> _objectsNotWritten;
|
||||
|
||||
public Dictionary<UUID, SerializedObject> SerializedObjects => _serializedObjects;
|
||||
|
||||
public this(Scene scene)
|
||||
{
|
||||
_scene = scene;
|
||||
@@ -35,6 +42,17 @@ class SceneSerializer
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
_serializedObjects = new .();
|
||||
|
||||
ScriptEngine.SerializeScriptInstances(_serializedObjects);
|
||||
|
||||
_objectsNotWritten = new .(_serializedObjects.Count);
|
||||
|
||||
for (UUID key in _serializedObjects.Keys)
|
||||
{
|
||||
_objectsNotWritten.Add(key);
|
||||
}
|
||||
|
||||
String buffer = scope String();
|
||||
let writer = scope BonWriter(buffer, true);
|
||||
var length = Serialize.Start(writer);
|
||||
@@ -63,10 +81,24 @@ class SceneSerializer
|
||||
}
|
||||
|
||||
writer.EntryEnd();
|
||||
|
||||
writer.Identifier("ReferencedObjects");
|
||||
|
||||
using (writer.ArrayBlock())
|
||||
{
|
||||
for (UUID id in _objectsNotWritten)
|
||||
{
|
||||
Serialize.Value(writer, _serializedObjects[id]);
|
||||
}
|
||||
}
|
||||
|
||||
writer.EntryEnd();
|
||||
}
|
||||
|
||||
|
||||
Serialize.End(writer, length);
|
||||
|
||||
delete _objectsNotWritten;
|
||||
|
||||
String targetDirectory = Path.GetDirectoryPath(filePath, .. scope String());
|
||||
Directory.CreateDirectory(targetDirectory);
|
||||
|
||||
@@ -205,10 +237,19 @@ class SceneSerializer
|
||||
{
|
||||
Serialize.Value(writer, "ScriptClass", component.ScriptClassName);
|
||||
|
||||
if (_serializedObjects.TryGetValue(entity.UUID, let value))
|
||||
{
|
||||
Serialize.Value(writer, "Fields", value);
|
||||
|
||||
_objectsNotWritten.Remove(entity.UUID);
|
||||
}
|
||||
|
||||
//if (component.HasScript)
|
||||
// TODO: Thats not a good check, I think. At least we know the script class is valid
|
||||
if (ScriptEngine.GetScriptClass(component.ScriptClassName) != null)
|
||||
{
|
||||
//if (ScriptEngine.GetScriptClass(component.ScriptClassName) != null)
|
||||
//{
|
||||
//Serialize.Value(writer, "Fields", );
|
||||
|
||||
// TODO: Serialize Script Instance!
|
||||
/*let fields = ScriptEngine.GetScriptFieldMap(entity);
|
||||
|
||||
@@ -236,7 +277,7 @@ class SceneSerializer
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
//}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -268,6 +309,8 @@ class SceneSerializer
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
_serializedObjects = new .();
|
||||
|
||||
_parentIdToChild = scope Dictionary<UUID, Entity>();
|
||||
_entitiesMissingParent = scope List<(Entity Entity, UUID ParentId)>();
|
||||
_fileToSceneId = scope Dictionary<UUID, UUID>();
|
||||
@@ -308,6 +351,28 @@ class SceneSerializer
|
||||
|
||||
Try!(reader.ArrayBlockEnd());
|
||||
|
||||
Try!(reader.EntryEnd());
|
||||
|
||||
if (Try!(reader.Identifier()) != "ReferencedObjects")
|
||||
return .Err;
|
||||
|
||||
Try!(reader.ArrayBlock());
|
||||
|
||||
first = true;
|
||||
while (reader.ArrayHasMore())
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
Try!(reader.EntryEnd());
|
||||
}
|
||||
|
||||
Try!(SerializedObject.BonDeserialize(reader, _serializedObjects, gBonEnv));
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
Try!(reader.ArrayBlockEnd());
|
||||
|
||||
Try!(reader.ObjectBlockEnd());
|
||||
|
||||
Try!(Deserialize.End(reader));
|
||||
@@ -605,112 +670,18 @@ class SceneSerializer
|
||||
|
||||
if (scriptClassName != null)
|
||||
{
|
||||
//if (ScriptEngine.EntityClasses.TryGetValue(scriptClassName, let scriptClass))
|
||||
/*{
|
||||
component.ScriptClass = scriptClass;
|
||||
}*/
|
||||
|
||||
component.ScriptClassName = scriptClassName;
|
||||
|
||||
delete scriptClassName;
|
||||
}
|
||||
|
||||
//if (component.HasScript)
|
||||
if (ScriptEngine.GetScriptClass(component.ScriptClassName) != null)
|
||||
|
||||
if (reader.ObjectHasMore())
|
||||
{
|
||||
// This whole operation is technically a bit junk, because we are not guaranteed to successfully deserialize the scene,
|
||||
// however we are editing the ScriptEngine because it doesn't care about which scene is active right now.
|
||||
// The UUID should be unique enough, however if they do overlap (e.g. loading the current scene or simply because we are unlucky)
|
||||
// we will replace the fields of the active scene, even if deserialization fails...
|
||||
// But that is a bug for me to rediscover in the distant future, so in case this bug occurred and it took ages for you to
|
||||
// figure out what happened: You are welcome :)
|
||||
|
||||
// TODO: We need a new way to Serialize/Deserialize these fields!
|
||||
/*ScriptEngine.CreateScriptFieldMap(entity);
|
||||
|
||||
var fields = ScriptEngine.GetScriptFieldMap(entity);
|
||||
|
||||
Try!(reader.EntryEnd());
|
||||
|
||||
|
||||
if (Try!(reader.Identifier()) == "Fields")
|
||||
{
|
||||
Try!(reader.ArrayBlock());
|
||||
|
||||
bool dontRemoveComma = true;
|
||||
|
||||
// Remove whitespace before the check
|
||||
while (reader..ConsumeEmpty().ArrayHasMore())
|
||||
{
|
||||
if (dontRemoveComma)
|
||||
dontRemoveComma = false;
|
||||
else
|
||||
{
|
||||
if (reader.[Friend]Check(',', false))
|
||||
reader.EntryEnd();
|
||||
else
|
||||
reader..FileEntrySkip(1).ConsumeEmpty();
|
||||
}
|
||||
|
||||
// TODO: We could think about doing the Try! a little smarter...
|
||||
// However we always might just fail to deserialize, so it doesn't really matter.
|
||||
// It does matter... in case of an error we should try to skip to the next entry.
|
||||
|
||||
StringView fieldName = Try!(reader.Identifier());
|
||||
|
||||
// Allocate a string on the stack, because the dictionary uses a string as key
|
||||
String fieldNameString = scope .(fieldName);
|
||||
|
||||
Result<StringView> fieldTypeName = reader.Type();
|
||||
|
||||
if (fieldTypeName case .Err)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to read field type for field \"{fieldName}\" in script \"{component.ScriptClassName}\" of entity {entity.UUID} (\"{entity.Name}\")");
|
||||
reader.FileEntrySkip(1);
|
||||
dontRemoveComma = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
Result<ScriptFieldType> fieldType = Enum.Parse<ScriptFieldType>(fieldTypeName, true);
|
||||
|
||||
if ((fieldType case .Err))
|
||||
{
|
||||
Log.EngineLogger.Error($"Error deserializing field type (Raw string: \"{fieldTypeName}\" of field: \"{fieldName}\" in script \"{component.ScriptClassName}\" of entity {entity.UUID} (\"{entity.Name}\")");
|
||||
reader.FileEntrySkip(1);
|
||||
dontRemoveComma = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8[sizeof(Matrix)] data = .();
|
||||
|
||||
if (Deserialize.Value(reader, ValueView(fieldType.Value.GetBeefType(), &data), gBonEnv) case .Err)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to deserialize data for field: \"{fieldName}\" in script \"{component.ScriptClassName}\" of entity {entity.UUID} (\"{entity.Name}\")");
|
||||
reader.FileEntrySkip(1);
|
||||
dontRemoveComma = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fields.ContainsKey(fieldNameString))
|
||||
{
|
||||
var field = ref fields[fieldNameString];
|
||||
|
||||
// Make sure the type we deserialized actually is correct.
|
||||
if (fieldType != field.Type)
|
||||
{
|
||||
Log.EngineLogger.Error($"Unexpected field type (\"{fieldTypeName}\" instead of \"{field.Type}\" for field: \"{fieldName}\" in script \"{component.ScriptClassName}\" of entity {entity.UUID} (\"{entity.Name}\")");
|
||||
continue;
|
||||
}
|
||||
|
||||
field.SetData(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error($"Script \"{component.ScriptClassName}\" doesn't have a field with name \"{fieldName}\". (Entity {entity.UUID} (\"{entity.Name}\"))");
|
||||
}
|
||||
}
|
||||
|
||||
Try!(reader.ArrayBlockEnd());
|
||||
}*/
|
||||
SerializedObject.BonDeserialize(reader, _serializedObjects, gBonEnv);
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
|
||||
Reference in New Issue
Block a user