public SyncField(Type targetType, string memberPath) { this.targetType = targetType; this.memberPath = targetType + "/" + memberPath; fieldType = MpReflection.PathType(this.memberPath); indexType = MpReflection.IndexType(this.memberPath); }
protected void ValidateType(string desc, SyncType type) { if (type.type != null && !SyncSerialization.CanHandle(type)) { throw new Exception($"Sync handler uses a non-serializable type: {type.type}. Details: {desc}"); } }
public static bool CanHandle(SyncType syncType) { var type = syncType.type; if (type == typeof(object)) { return(true); } if (type.IsByRef) { return(true); } if (SyncDictFast.syncWorkers.TryGetValue(type, out _)) { return(true); } if (syncType.expose) { return(typeof(IExposable).IsAssignableFrom(type)); } if (typeof(ISynchronizable).IsAssignableFrom(type)) { return(true); } if (type.IsEnum) { return(CanHandle(Enum.GetUnderlyingType(type))); } if (type.IsArray) { return(type.GetArrayRank() == 1 && CanHandle(type.GetElementType())); } if (type.IsGenericType && type.GetGenericTypeDefinition() is { } gtd) { return ((false || gtd == typeof(List <>) || gtd == typeof(IEnumerable <>) || gtd == typeof(Nullable <>) || gtd == typeof(Dictionary <,>) || gtd == typeof(Pair <,>) || typeof(ITuple).IsAssignableFrom(gtd)) && CanHandleGenericArgs(type)); } if (typeof(ISyncSimple).IsAssignableFrom(type)) { return(AccessTools.GetDeclaredFields(type).All(f => CanHandle(f.FieldType))); } if (typeof(Def).IsAssignableFrom(type)) { return(true); } if (typeof(Designator).IsAssignableFrom(type)) { return(true); } return(SyncDict.syncWorkers.TryGetValue(type, out _)); }
public static object ReadSyncObject(ByteReader data, SyncType syncType) { var log = (data as LoggingByteReader)?.Log; Type type = syncType.type; log?.Enter(type.FullName); try { object val = ReadSyncObjectInternal(data, syncType); log?.AppendToCurrentName($": {val}"); return(val); } finally { log?.Exit(); } }
public static object ReadSyncObject(ByteReader data, SyncType syncType) { SyncLogger log = null; if (data is LoggingByteReader logger) { log = logger.log; log.Enter(syncType.type.FullName); } try { object obj = ReadSyncObjectInternal(data, syncType); log?.AppendToCurrentName($": {obj}"); return(obj); } finally { log?.Exit(); } }
public static object ReadSyncObject(ByteReader data, SyncType syncType) { MpContext context = data.MpContext(); Map map = context.map; Type type = syncType.type; try { if (typeof(object) == type) { return(null); } if (type.IsByRef) { return(null); } if (syncWorkersEarly.TryGetValue(type, out SyncWorkerEntry syncWorkerEntryEarly)) { object res = null; if (syncWorkerEntryEarly.shouldConstruct || type.IsValueType) { res = Activator.CreateInstance(type); } syncWorkerEntryEarly.Invoke(new ReadingSyncWorker(data), ref res); return(res); } if (syncType.expose) { if (!typeof(IExposable).IsAssignableFrom(type)) { throw new SerializationException($"Type {type} can't be exposed because it isn't IExposable"); } byte[] exposableData = data.ReadPrefixedBytes(); return(ReadExposable.MakeGenericMethod(type).Invoke(null, new[] { exposableData, null })); } if (typeof(ISynchronizable).IsAssignableFrom(type)) { var obj = Activator.CreateInstance(type); ((ISynchronizable)obj).Sync(new ReadingSyncWorker(data)); return(obj); } if (type.IsEnum) { Type enumType = Enum.GetUnderlyingType(type); return(ReadSyncObject(data, enumType)); } if (type.IsArray && type.GetArrayRank() == 1) { Type elementType = type.GetElementType(); ushort length = data.ReadUShort(); Array arr = Array.CreateInstance(elementType, length); for (int i = 0; i < length; i++) { arr.SetValue(ReadSyncObject(data, elementType), i); } return(arr); } if (type.IsGenericType) { if (type.GetGenericTypeDefinition() == typeof(List <>)) { ListType specialList = ReadSync <ListType>(data); if (specialList == ListType.MapAllThings) { return(map.listerThings.AllThings); } if (specialList == ListType.MapAllDesignations) { return(map.designationManager.allDesignations); } Type listType = type.GetGenericArguments()[0]; ushort size = data.ReadUShort(); IList list = (IList)Activator.CreateInstance(type, size); for (int j = 0; j < size; j++) { list.Add(ReadSyncObject(data, listType)); } return(list); } if (type.GetGenericTypeDefinition() == typeof(IEnumerable <>)) { Type element = type.GetGenericArguments()[0]; return(ReadSyncObject(data, typeof(List <>).MakeGenericType(element))); } if (type.GetGenericTypeDefinition() == typeof(Nullable <>)) { bool isNull = data.ReadBool(); if (isNull) { return(null); } bool hasValue = data.ReadBool(); if (!hasValue) { return(Activator.CreateInstance(type)); } Type nullableType = type.GetGenericArguments()[0]; return(Activator.CreateInstance(type, ReadSyncObject(data, nullableType))); } } // Def is a special case until the workers can read their own type if (typeof(Def).IsAssignableFrom(type)) { ushort shortHash = data.ReadUShort(); if (shortHash == 0) { return(null); } Def def = (Def)GetDefByIdMethod.MakeGenericMethod(type).Invoke(null, new object[] { shortHash }); if (def == null) { throw new Exception($"Couldn't find {type} with short hash {shortHash}"); } return(def); } // Where the magic happens if (syncWorkers.TryGetValue(type, out var syncWorkerEntry)) { object res = null; if (syncWorkerEntry.shouldConstruct || type.IsValueType) { res = Activator.CreateInstance(type); } syncWorkerEntry.Invoke(new ReadingSyncWorker(data), ref res); return(res); } throw new SerializationException("No reader for type " + type); } catch (Exception e) { MpLog.Error($"Error reading type: {type}, {e}"); throw; } }
public static void WriteSyncObject(ByteWriter data, object obj, SyncType syncType) { MpContext context = data.MpContext(); Type type = syncType.type; LoggingByteWriter log = data as LoggingByteWriter; log?.LogEnter(type.FullName + ": " + (obj ?? "null")); if (obj != null && !type.IsAssignableFrom(obj.GetType())) { throw new SerializationException($"Serializing with type {type} but got object of type {obj.GetType()}"); } try { if (typeof(object) == type) { return; } if (type.IsByRef) { return; } if (syncWorkersEarly.TryGetValue(type, out var syncWorkerEntryEarly)) { syncWorkerEntryEarly.Invoke(new WritingSyncWorker(data), ref obj); return; } if (syncType.expose) { if (!typeof(IExposable).IsAssignableFrom(type)) { throw new SerializationException($"Type {type} can't be exposed because it isn't IExposable"); } IExposable exposable = obj as IExposable; data.WritePrefixedBytes(ScribeUtil.WriteExposable(exposable)); return; } if (typeof(ISynchronizable).IsAssignableFrom(type)) { ((ISynchronizable)obj).Sync(new WritingSyncWorker(data)); return; } if (type.IsEnum) { Type enumType = Enum.GetUnderlyingType(type); WriteSyncObject(data, Convert.ChangeType(obj, enumType), enumType); return; } if (type.IsArray && type.GetArrayRank() == 1) { Type elementType = type.GetElementType(); Array arr = (Array)obj; if (arr.Length > ushort.MaxValue) { throw new Exception($"Tried to serialize a {elementType}[] with too many ({arr.Length}) items."); } data.WriteUShort((ushort)arr.Length); foreach (object e in arr) { WriteSyncObject(data, e, elementType); } return; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List <>)) { ListType specialList = ListType.Normal; Type listType = type.GetGenericArguments()[0]; if (listType == typeof(Thing) && obj == Find.CurrentMap.listerThings.AllThings) { context.map = Find.CurrentMap; specialList = ListType.MapAllThings; } else if (listType == typeof(Designation) && obj == Find.CurrentMap.designationManager.allDesignations) { context.map = Find.CurrentMap; specialList = ListType.MapAllDesignations; } WriteSync(data, specialList); if (specialList == ListType.Normal) { IList list = (IList)obj; if (list.Count > ushort.MaxValue) { throw new Exception($"Tried to serialize a {type} with too many ({list.Count}) items."); } data.WriteUShort((ushort)list.Count); foreach (object e in list) { WriteSyncObject(data, e, listType); } } return; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable <>)) { bool isNull = obj == null; data.WriteBool(isNull); if (isNull) { return; } bool hasValue = (bool)obj.GetPropertyOrField("HasValue"); data.WriteBool(hasValue); Type nullableType = type.GetGenericArguments()[0]; if (hasValue) { WriteSyncObject(data, obj.GetPropertyOrField("Value"), nullableType); } return; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable <>)) { IEnumerable e = (IEnumerable)obj; Type elementType = type.GetGenericArguments()[0]; var listType = typeof(List <>).MakeGenericType(elementType); IList list = (IList)Activator.CreateInstance(listType); foreach (var o in e) { if (list.Count > ushort.MaxValue) { throw new Exception($"Tried to serialize a {type} with too many ({list.Count}) items."); } list.Add(o); } WriteSyncObject(data, list, listType); return; } // special case if (typeof(Def).IsAssignableFrom(type)) { Def def = obj as Def; data.WriteUShort(def != null ? def.shortHash : (ushort)0); return; } // Where the magic happens if (syncWorkers.TryGetValue(type, out var syncWorkerEntry)) { syncWorkerEntry.Invoke(new WritingSyncWorker(data), ref obj); return; } log?.LogNode("No writer for " + type); throw new SerializationException("No writer for type " + type); } catch (Exception e) { MpLog.Error($"Error writing type: {type}, obj: {obj}, {e}"); throw; } finally { log?.LogExit(); } }
public static void WriteSyncObject(ByteWriter data, object obj, SyncType syncType) { MpContext context = data.MpContext(); Type type = syncType.type; var log = (data as LoggingByteWriter)?.Log; log?.Enter($"{type.FullName}: {obj ?? "null"}"); if (obj != null && !type.IsAssignableFrom(obj.GetType())) { throw new SerializationException($"Serializing with type {type} but got object of type {obj.GetType()}"); } try { if (typeof(object) == type) { return; } if (type.IsByRef) { return; } if (SyncDictFast.syncWorkers.TryGetValue(type, out var syncWorkerEntryEarly)) { syncWorkerEntryEarly.Invoke(new WritingSyncWorker(data), ref obj); return; } if (syncType.expose) { if (!typeof(IExposable).IsAssignableFrom(type)) { throw new SerializationException($"Type {type} can't be exposed because it isn't IExposable"); } IExposable exposable = obj as IExposable; byte[] xmlData = ScribeUtil.WriteExposable(exposable); LogXML(log, xmlData); data.WritePrefixedBytes(xmlData); return; } if (typeof(ISynchronizable).IsAssignableFrom(type)) { ((ISynchronizable)obj).Sync(new WritingSyncWorker(data)); return; } if (type.IsEnum) { Type enumType = Enum.GetUnderlyingType(type); WriteSyncObject(data, Convert.ChangeType(obj, enumType), enumType); return; } if (type.IsArray) { if (type.GetArrayRank() != 1) { throw new SerializationException("Multi dimensional arrays aren't supported."); } Type elementType = type.GetElementType(); Array arr = (Array)obj; if (arr == null) { data.WriteUShort(ushort.MaxValue); return; } if (arr.Length >= ushort.MaxValue) { throw new SerializationException($"Tried to serialize a {elementType}[] with too many ({arr.Length}) items."); } data.WriteUShort((ushort)arr.Length); foreach (object e in arr) { WriteSyncObject(data, e, elementType); } return; } if (type.IsGenericType) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(List <>)) { IList list = (IList)obj; Type listObjType = type.GetGenericArguments()[0]; if (list == null) { data.WriteUShort(ushort.MaxValue); return; } if (list.Count >= ushort.MaxValue) { throw new SerializationException($"Tried to serialize a {type} with too many ({list.Count}) items."); } data.WriteUShort((ushort)list.Count); foreach (object e in list) { WriteSyncObject(data, e, listObjType); } return; } if (genericTypeDefinition == typeof(IEnumerable <>)) { IEnumerable e = (IEnumerable)obj; Type elementType = type.GetGenericArguments()[0]; var listType = typeof(List <>).MakeGenericType(elementType); if (e == null) { WriteSyncObject(data, null, listType); return; } IList list = (IList)Activator.CreateInstance(listType); foreach (var o in e) { list.Add(o); } WriteSyncObject(data, list, listType); return; } if (genericTypeDefinition == typeof(Nullable <>)) { bool isNull = obj == null; data.WriteBool(isNull); if (isNull) { return; } WriteSyncObject(data, obj, Nullable.GetUnderlyingType(type)); return; } if (genericTypeDefinition == typeof(Dictionary <,>)) { IDictionary dictionary = (IDictionary)obj; Type[] arguments = type.GetGenericArguments(); if (dictionary == null) { WriteSyncObject(data, null, arguments[0].MakeArrayType()); return; } Array keyArray = Array.CreateInstance(arguments[0], dictionary.Count); dictionary.Keys.CopyTo(keyArray, 0); Array valueArray = Array.CreateInstance(arguments[1], dictionary.Count); dictionary.Values.CopyTo(valueArray, 0); WriteSyncObject(data, keyArray, keyArray.GetType()); WriteSyncObject(data, valueArray, valueArray.GetType()); return; } if (genericTypeDefinition == typeof(Pair <,>)) { Type[] arguments = type.GetGenericArguments(); WriteSyncObject(data, AccessTools.DeclaredField(type, "first").GetValue(obj), arguments[0]); WriteSyncObject(data, AccessTools.DeclaredField(type, "second").GetValue(obj), arguments[1]); return; } if (typeof(ITuple).IsAssignableFrom(genericTypeDefinition)) // ValueTuple or Tuple { Type[] arguments = type.GetGenericArguments(); ITuple tuple = (ITuple)obj; data.WriteInt32(tuple.Length); for (int i = 0; i < tuple.Length; i++) { WriteSyncObject(data, tuple[i], arguments[i]); } return; } } if (typeof(ISyncSimple).IsAssignableFrom(type)) { foreach (var field in AccessTools.GetDeclaredFields(type)) { WriteSyncObject(data, field.GetValue(obj), field.FieldType); } return; } // Special case if (typeof(Def).IsAssignableFrom(type)) { if (obj is not Def def) { data.WriteUShort(ushort.MaxValue); return; } var defTypeIndex = Array.IndexOf(DefSerialization.DefTypes, def.GetType()); if (defTypeIndex == -1) { throw new SerializationException($"Unknown def type {def.GetType()}"); } data.WriteUShort((ushort)defTypeIndex); data.WriteUShort(def.shortHash); return; } // Special case for Designators to change the type if (typeof(Designator).IsAssignableFrom(type)) { data.WriteUShort((ushort)Array.IndexOf(ImplSerialization.designatorTypes, obj.GetType())); } // Where the magic happens if (SyncDict.syncWorkers.TryGetValue(type, out var syncWorkerEntry)) { syncWorkerEntry.Invoke(new WritingSyncWorker(data), ref obj); return; } log?.Node("No writer for " + type); throw new SerializationException("No writer for type " + type); } catch { Log.Error($"Multiplayer: Error writing type: {type}, obj: {obj}, obj type: {obj?.GetType()}"); throw; } finally { log?.Exit(); } }
private static object ReadSyncObjectInternal(ByteReader data, SyncType syncType) { Type type = syncType.type; try { if (typeof(object) == type) { return(null); } if (type.IsByRef) { return(null); } if (SyncDictFast.syncWorkers.TryGetValue(type, out SyncWorkerEntry syncWorkerEntryEarly)) { object res = null; if (syncWorkerEntryEarly.shouldConstruct || type.IsValueType) { res = Activator.CreateInstance(type); } syncWorkerEntryEarly.Invoke(new ReadingSyncWorker(data), ref res); return(res); } if (syncType.expose) { if (!typeof(IExposable).IsAssignableFrom(type)) { throw new SerializationException($"Type {type} can't be exposed because it isn't IExposable"); } byte[] exposableData = data.ReadPrefixedBytes(); return(ExposableSerialization.ReadExposable(type, exposableData)); } if (typeof(ISynchronizable).IsAssignableFrom(type)) { var obj = Activator.CreateInstance(type); ((ISynchronizable)obj).Sync(new ReadingSyncWorker(data)); return(obj); } if (type.IsEnum) { Type underlyingType = Enum.GetUnderlyingType(type); return(Enum.ToObject(type, ReadSyncObject(data, underlyingType))); } if (type.IsArray) { if (type.GetArrayRank() != 1) { throw new SerializationException("Multi dimensional arrays aren't supported."); } ushort length = data.ReadUShort(); if (length == ushort.MaxValue) { return(null); } Type elementType = type.GetElementType(); Array arr = Array.CreateInstance(elementType, length); for (int i = 0; i < length; i++) { arr.SetValue(ReadSyncObject(data, elementType), i); } return(arr); } if (type.IsGenericType) { var genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(List <>)) { ushort size = data.ReadUShort(); if (size == ushort.MaxValue) { return(null); } Type listObjType = type.GetGenericArguments()[0]; IList list = (IList)Activator.CreateInstance(type, size); for (int j = 0; j < size; j++) { list.Add(ReadSyncObject(data, listObjType)); } return(list); } if (genericTypeDefinition == typeof(IEnumerable <>)) { Type element = type.GetGenericArguments()[0]; return(ReadSyncObject(data, typeof(List <>).MakeGenericType(element))); } if (genericTypeDefinition == typeof(Nullable <>)) { bool isNull = data.ReadBool(); if (isNull) { return(null); } return(Activator.CreateInstance(type, ReadSyncObject(data, Nullable.GetUnderlyingType(type)))); } if (genericTypeDefinition == typeof(Dictionary <,>)) { Type[] arguments = type.GetGenericArguments(); Array keys = (Array)ReadSyncObject(data, arguments[0].MakeArrayType()); if (keys == null) { return(null); } Array values = (Array)ReadSyncObject(data, arguments[1].MakeArrayType()); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); for (int i = 0; i < keys.Length; i++) { var key = keys.GetValue(i); if (key != null) { dictionary.Add(key, values.GetValue(i)); } } return(dictionary); } if (genericTypeDefinition == typeof(Pair <,>)) { Type[] arguments = type.GetGenericArguments(); object[] parameters = { ReadSyncObject(data, arguments[0]), ReadSyncObject(data, arguments[1]), }; return(type.GetConstructors().First().Invoke(parameters)); } if (typeof(ITuple).IsAssignableFrom(genericTypeDefinition)) // ValueTuple or Tuple { Type[] arguments = type.GetGenericArguments(); int size = data.ReadInt32(); object[] values = new object[size]; for (int i = 0; i < size; i++) { values[i] = ReadSyncObject(data, arguments[i]); } return(type.GetConstructors().First().Invoke(values)); } } if (typeof(ISyncSimple).IsAssignableFrom(type)) { var obj = MpUtil.NewObjectNoCtor(type); foreach (var field in AccessTools.GetDeclaredFields(type)) { field.SetValue(obj, ReadSyncObject(data, field.FieldType)); } return(obj); } // Def is a special case until the workers can read their own type if (typeof(Def).IsAssignableFrom(type)) { ushort defTypeIndex = data.ReadUShort(); if (defTypeIndex == ushort.MaxValue) { return(null); } ushort shortHash = data.ReadUShort(); var defType = DefSerialization.DefTypes[defTypeIndex]; var def = DefSerialization.GetDef(defType, shortHash); if (def == null) { throw new SerializationException($"Couldn't find {defType} with short hash {shortHash}"); } return(def); } // Designators can't be handled by SyncWorkers due to the type change if (typeof(Designator).IsAssignableFrom(type)) { ushort desId = ReadSync <ushort>(data); type = ImplSerialization.designatorTypes[desId]; // Replaces the type! } // Where the magic happens if (SyncDict.syncWorkers.TryGetValue(type, out var syncWorkerEntry)) { object res = null; if (syncWorkerEntry.shouldConstruct || type.IsValueType) { res = Activator.CreateInstance(type); } syncWorkerEntry.Invoke(new ReadingSyncWorker(data), ref res); return(res); } throw new SerializationException("No reader for type " + type); } catch { Log.Error($"Multiplayer: Error reading type: {type}"); throw; } }
public void Bind <T>(ref T obj, SyncType type) { SyncSerialization.WriteSyncObject(writer, obj, type); }
public void Bind <T>(ref T obj, SyncType type) { obj = (T)SyncSerialization.ReadSyncObject(reader, type); }