Last active
April 15, 2021 14:19
-
-
Save mrtrizer/aafbe9a62d4c1acef56cffd521cc9ab9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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