Skip to content

Instantly share code, notes, and snippets.

@mrtrizer
Last active April 15, 2021 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrtrizer/aafbe9a62d4c1acef56cffd521cc9ab9 to your computer and use it in GitHub Desktop.
Save mrtrizer/aafbe9a62d4c1acef56cffd521cc9ab9 to your computer and use it in GitHub Desktop.
public static void autoDeserialize(ObjectContext context, AutoSerialisedObject autoSerializedObject, RestoreProcess restoreProcess)
{
var cache = getClassReflectionCache(context.type);
foreach (var field in cache.fields)
{
var serializedField = autoSerializedObject.autoSerialiedFields.Find(item => item.name == field.fieldInfo.Name);
if (serializedField.value != null)
{
var value = deserializeValue(serializedField.value, field.fieldInfo.FieldType, restoreProcess, context);
if (value == null || value.GetType() != typeof(object))
{
field.fieldInfo.SetValue(context.obj, value);
}
else
{
Debug.LogError("Can't deserialize, unknown type " + field.fieldInfo.FieldType.Name + " field " + context.type.Name + "." + field.fieldInfo.Name);
}
}
else
{
Debug.LogWarning("No such field in serialized data " + context.type.Name + "." + field.fieldInfo.Name);
}
}
}
static void write(ref byte[] dest, ref int pos, Type type, object val)
{
if (!BitConverter.IsLittleEndian)
throw new Exception("Big endian is not supported!");
if (type.IsSubclassOf(typeof(Component)))
{
if (type.IsAssignableFrom(typeof(PrefabRef)))
{
var prefabRef = val as PrefabRef;
var guid = prefabRef.getGuid();
pos += BinaryUtil.WriteGuid(ref dest, pos, guid);
}
else
{
var behaviour = val as Component;
var guid = SavingModel.extractGuid(behaviour);
pos += BinaryUtil.WriteGuid(ref dest, pos, guid);
}
}
else if (type.IsAssignableFrom(typeof(Translation)))
{
var translation = val as Translation;
var resultStr = translation.uid;
write(ref dest, ref pos, typeof(string), resultStr);
}
else if (type.IsAssignableFrom(typeof(GameObject)))
{
var obj = val as GameObject;
var guid = SavingModel.extractGuid(obj);
pos += BinaryUtil.WriteGuid(ref dest, pos, guid);
}
else if (type.IsValueType)
{
if (val is float)
{
pos += BinaryUtil.WriteSingle(ref dest, pos, (float)val);
}
else if (val is int || type.IsEnum)
{
pos += BinaryUtil.WriteInt32(ref dest, pos, (int)val);
}
else if (val is short)
{
pos += BinaryUtil.WriteInt16(ref dest, pos, (short)val);
}
else if (type == typeof(byte))
{
pos += BinaryUtil.WriteByte(ref dest, pos, (byte)val);
}
else if (type == typeof(bool))
{
pos += BinaryUtil.WriteBoolean(ref dest, pos, (bool)val);
}
else if (type.IsAssignableFrom(typeof(Bounds)))
{
var bounds = (Bounds)val;
var boundsValue = new BoundsValue { position = bounds.center, size = bounds.size };
pos += BinaryUtil.WriteSingle(ref dest, pos, bounds.center.x);
pos += BinaryUtil.WriteSingle(ref dest, pos, bounds.center.y);
pos += BinaryUtil.WriteSingle(ref dest, pos, bounds.center.z);
pos += BinaryUtil.WriteSingle(ref dest, pos, bounds.size.x);
pos += BinaryUtil.WriteSingle(ref dest, pos, bounds.size.y);
pos += BinaryUtil.WriteSingle(ref dest, pos, bounds.size.z);
}
else if (type.IsAssignableFrom(typeof(Vector3)))
{
var vec = (Vector3)val;
pos += BinaryUtil.WriteSingle(ref dest, pos, vec.x);
pos += BinaryUtil.WriteSingle(ref dest, pos, vec.y);
pos += BinaryUtil.WriteSingle(ref dest, pos, vec.z);
}
else if (type.IsAssignableFrom(typeof(Quaternion)))
{
var q = (Quaternion)val;
pos += BinaryUtil.WriteSingle(ref dest, pos, q.x);
pos += BinaryUtil.WriteSingle(ref dest, pos, q.y);
pos += BinaryUtil.WriteSingle(ref dest, pos, q.z);
pos += BinaryUtil.WriteSingle(ref dest, pos, q.w);
}
else // struct
{
autoSerializeBin(ref dest, ref pos, val);
}
}
else if (type == typeof(string))
{
var str = (string)val;
pos += BinaryUtil.WriteInt32(ref dest, pos, str.Length);
pos += BinaryUtil.WriteString(ref dest, pos, str);
}
else if (type == typeof(byte[]))
{
var bytes = (byte[])val;
pos += BinaryUtil.WriteInt32(ref dest, pos, bytes.Length);
pos += BinaryUtil.WriteBytes(ref dest, pos, bytes);
}
else if (Array.Find(type.GetInterfaces(), item => item == typeof(IList)) != null)
{
if (val != null)
{
var list = val as IList;
pos += BinaryUtil.WriteInt32(ref dest, pos, list.Count);
int nullItemPos = pos;
pos += sizeof(int);
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
if (item == null)
{
BinaryUtil.WriteInt32(ref dest, nullItemPos, i);
nullItemPos = pos;
pos += sizeof(int);
}
else
{
write(ref dest, ref pos, type.GenericTypeArguments[0], item);
}
}
BinaryUtil.WriteInt32(ref dest, nullItemPos, -1);
}
}
else if (type.IsClass)
{
autoSerializeBin(ref dest, ref pos, val);
}
else
{
throw new Exception("Unknown value type " + type.Name);
}
}
public static void autoSerializeBin(ref byte[] dest, ref int pos, object obj)
{
var type = obj.GetType();
var cache = getClassReflectionCache(type);
int countPos = pos;
int count = 0;
pos += sizeof(byte);
foreach (var field in cache.fields)
{
var value = field.fieldInfo.GetValue(obj);
if (value != null)
{
pos += BinaryUtil.WriteByte(ref dest, pos, (byte)field.id);
write(ref dest, ref pos, field.fieldInfo.FieldType, value);
count++;
}
}
BinaryUtil.WriteByte(ref dest, countPos, (byte)count);
}
public static unsafe object read(byte[] source, ref int pos, System.Type type, RestoreProcess restoreProcess, ObjectContext context)
{
object result = null;
if (type.IsSubclassOf(typeof(Component)) || type.IsAssignableFrom(typeof(Transform)))
{
if (type == typeof(PrefabRef))
{
var guid = BinaryUtil.ReadGuid(ref source, pos);
pos += sizeof(Guid);
var prefabObj = ApplicationServices.prefabManager.getPrefab(guid.ToString());
result = prefabObj?.GetComponent<PrefabRef>();
}
else
{
var guid = BinaryUtil.ReadGuid(ref source, pos);
pos += sizeof(Guid);
var obj = SavingModel.findObjectById(guid.ToString(), restoreProcess);
result = obj?.GetComponent(type);
}
}
else if (type.IsAssignableFrom(typeof(Translation)))
{
var str = read(source, ref pos, typeof(string), restoreProcess, context) as string;
result = ApplicationServices.translationManager.getTranslation(str);
}
else if (type.IsAssignableFrom(typeof(GameObject)))
{
var guid = BinaryUtil.ReadGuid(ref source, pos);
pos += sizeof(Guid);
var obj = SavingModel.findObjectById(guid.ToString(), restoreProcess);
result = obj;
}
else if (type.IsValueType)
{
if (type == typeof(float))
{
result = BinaryUtil.ReadSingle(ref source, pos);
pos += sizeof(float);
}
else if (type.IsEnum || type == typeof(int))
{
result = BinaryUtil.ReadInt32(ref source, pos);
pos += sizeof(int);
}
else if (type == typeof(short))
{
result = BinaryUtil.ReadInt16(ref source, pos);
pos += sizeof(short);
}
else if (type == typeof(byte))
{
result = BinaryUtil.ReadByte(ref source, pos);
pos += sizeof(byte);
}
else if (type == typeof(bool))
{
result = BinaryUtil.ReadBoolean(ref source, pos);
pos += sizeof(bool);
}
else if (type.IsAssignableFrom(typeof(Bounds)))
{
float x = BinaryUtil.ReadSingle(ref source, pos);
float y = BinaryUtil.ReadSingle(ref source, pos);
float z = BinaryUtil.ReadSingle(ref source, pos);
float sX = BinaryUtil.ReadSingle(ref source, pos);
float sY = BinaryUtil.ReadSingle(ref source, pos);
float sZ = BinaryUtil.ReadSingle(ref source, pos);
pos += 6 * sizeof(int);
result = new Bounds(new Vector3(x, y, z), new Vector3(sX, sY, sZ));
}
else if (type.IsAssignableFrom(typeof(Vector3)))
{
float x = BinaryUtil.ReadSingle(ref source, pos);
float y = BinaryUtil.ReadSingle(ref source, pos);
float z = BinaryUtil.ReadSingle(ref source, pos);
pos += 3 * sizeof(int);
result = new Vector3(x, y, z);
}
else if (type.IsAssignableFrom(typeof(Quaternion)))
{
float x = BinaryUtil.ReadSingle(ref source, pos);
float y = BinaryUtil.ReadSingle(ref source, pos);
float z = BinaryUtil.ReadSingle(ref source, pos);
float w = BinaryUtil.ReadSingle(ref source, pos);
pos += 4 * sizeof(int);
result = new Quaternion(x, y, z, w);
}
else // struct
{
object instance = System.Activator.CreateInstance(type);
var nestedContext = new ObjectContext { obj = instance, type = type, nameOnScene = context.nameOnScene };
autoDeserializeBin(ref source, ref pos, nestedContext, restoreProcess);
result = instance;
}
}
else if (type == typeof(string))
{
int length = BinaryUtil.ReadInt32(ref source, pos);
pos += sizeof(int);
result = BinaryUtil.ReadString(ref source, pos, length);
pos += length;
}
else if (type == typeof(byte[]))
{
int length = BinaryUtil.ReadInt32(ref source, pos);
pos += sizeof(int);
result = BinaryUtil.ReadBytes(ref source, pos, length);
pos += length;
}
else if (System.Array.Find(type.GetInterfaces(), item => item == typeof(IList)) != null)
{
int length = BinaryUtil.ReadInt32(ref source, pos);
pos += sizeof(int);
int nextNullIndex = BinaryUtil.ReadInt32(ref source, pos);
pos += sizeof(int);
IList instance = System.Activator.CreateInstance(type) as IList;
for (int i = 0; i < length; i++)
{
if (i == nextNullIndex)
{
instance.Add(null);
nextNullIndex = BinaryUtil.ReadInt32(ref source, pos);
pos += sizeof(int);
}
else
{
var item = read(source, ref pos, type.GenericTypeArguments[0], restoreProcess, context);
instance.Add(item);
}
}
result = instance;
}
else if (type.IsClass)
{
object instance = System.Activator.CreateInstance(type);
var nestedContext = new ObjectContext { obj = instance, type = type, nameOnScene = context.nameOnScene };
autoDeserializeBin(ref source, ref pos, nestedContext, restoreProcess);
result = instance;
}
return result;
}
public static void autoDeserializeBin(ref byte[] source, ref int pos, ObjectContext context, RestoreProcess restoreProcess)
{
int count = BinaryUtil.ReadByte(ref source, pos);
pos += sizeof(byte);
var cache = getClassReflectionCache(context.type);
for (int i = 0; i < count; i++)
{
int id = BinaryUtil.ReadByte(ref source, pos);
pos += sizeof(byte);
int fieldIndex = cache.fields.FindIndex(item => item.id == id);
if (fieldIndex != -1)
{
var field = cache.fields[fieldIndex];
var value = read(source, ref pos, field.fieldInfo.FieldType, restoreProcess, context);
if (value.GetType() != typeof(object))
{
field.fieldInfo.SetValue(context.obj, value);
}
else
{
Debug.LogError("Can't deserialize, unknown type " + field.fieldInfo.FieldType.Name + " field " + context.type.Name + "." + field.fieldInfo.Name);
}
}
else
{
Debug.LogWarning("No such field in object " + context.type.Name + ":" + id);
throw new UnityException("Field seems to be removed. Data can't be read due to unknown size. ");
}
}
}
struct TestSubStruct
{
[AutoSerialize(0)]
public int fieldInt;
}
class TestSubClass
{
[AutoSerialize(0)]
public int fieldInt;
}
class TestStruct
{
[AutoSerialize(0)]
public int fieldInt;
[AutoSerialize(1)]
public string fieldString;
[AutoSerialize(2)]
public TestSubStruct subStruct;
[AutoSerialize(3)]
public List<string> listOfStrs;
[AutoSerialize(4)]
public List<int> listOfInts;
[AutoSerialize(5)]
public List<TestSubStruct> listOfTestSubStruct;
[AutoSerialize(6)]
public float fieldFloat;
[AutoSerialize(7)]
public bool fieldBool;
[AutoSerialize(8)]
public short fieldShort;
[AutoSerialize(9)]
public byte[] fieldByteArray;
[AutoSerialize(10)]
public byte fieldByte;
[AutoSerialize(11)]
public TestSubClass testSubClass;
[AutoSerialize(11)]
public TestSubClass testSubClassNull;
}
[MenuItem("Tests/Auto Serialize")]
public static void testAutoSerialize()
{
var testObj = new TestStruct {
fieldInt = 100,
fieldString = "200",
subStruct = new TestSubStruct {
fieldInt = 300
},
listOfStrs = new List<string> { "A", "B", null, "D", null, "F" },
listOfInts = new List<int> { 1, 2, 3, 4, 5 },
listOfTestSubStruct = new List<TestSubStruct> { new TestSubStruct { fieldInt = 1 }, new TestSubStruct { fieldInt = 2 }, new TestSubStruct { fieldInt = 3 } },
fieldFloat = 10.010f,
fieldBool = true,
fieldShort = 1000,
fieldByte = 10,
fieldByteArray = new byte[] { 1, 2, 3, 4 },
testSubClass = new TestSubClass { fieldInt = 400 },
testSubClassNull = null
};
var bytes = new byte[1024];
int serializePos = 0;
autoSerializeBin(ref bytes, ref serializePos, testObj);
Array.Resize(ref bytes, serializePos + 1);
Debug.Log(BitConverter.ToString(bytes).Replace("-", ""));
var newTestObj = new TestStruct();
int deserializePos = 0;
var objContext = new ObjectContext { obj = newTestObj, nameOnScene = "Test", type = typeof(TestStruct) };
var restoreProcess = new RestoreProcess { };
autoDeserializeBin(ref bytes, ref deserializePos, objContext, restoreProcess);
Debug.Assert(newTestObj.fieldInt == testObj.fieldInt);
Debug.Assert(newTestObj.fieldString == testObj.fieldString);
Debug.Assert(newTestObj.subStruct.fieldInt == testObj.subStruct.fieldInt);
Debug.Assert(newTestObj.listOfStrs.Count == testObj.listOfStrs.Count);
Debug.Assert(newTestObj.listOfInts.Count == testObj.listOfInts.Count);
Debug.Assert(newTestObj.listOfTestSubStruct.Count == testObj.listOfTestSubStruct.Count);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment