public SyncField(Type targetType, string memberPath) { this.targetType = targetType; this.memberPath = targetType + "/" + memberPath; fieldType = MpReflection.PathType(this.memberPath); indexType = MpReflection.IndexType(this.memberPath); }
public override void Handle(ByteReader data) { object target = null; if (targetType != null) { target = SyncSerialization.ReadSyncObject(data, targetType); if (target == null) { return; } } object value = SyncSerialization.ReadSyncObject(data, fieldType); if (cancelIfValueNull && value == null) { return; } object index = null; if (indexType != null) { index = SyncSerialization.ReadSyncObject(data, indexType); } preApply?.Invoke(target, value); MpLog.Debug($"Set {memberPath} in {target} to {value}, map {data.MpContext().map}, index {index}"); MpReflection.SetValue(target, memberPath, value, index); postApply?.Invoke(target, value); }
public override void Bind(object obj, string name) { object value = MpReflection.GetValue(obj, name); Type type = value.GetType(); SyncSerialization.WriteSyncObject(writer, value, type); }
// todo support methods with arguments (currently there has been no need for it) public static SyncDelegate RegisterSyncDelegate(Type inType, string nestedType, string methodName, string[] fields, Type[] args = null) { string typeName = $"{inType}+{nestedType}"; Type type = MpReflection.GetTypeByName(typeName); if (type == null) { throw new Exception($"Couldn't find type {typeName}"); } MethodInfo method = AccessTools.Method(type, methodName, args); if (method == null) { throw new Exception($"Couldn't find method {typeName}::{methodName}"); } MpUtil.MarkNoInlining(method); SyncDelegate handler = new SyncDelegate(type, method, fields); syncMethods[handler.method] = handler; handlers.Add(handler); PatchMethodForSync(method); return(handler); }
public override void Handle(ByteReader data) { object target = Activator.CreateInstance(delegateType); for (int i = 0; i < fieldPaths.Length; i++) { string path = fieldPaths[i]; string noTypePath = MpReflection.RemoveType(path); Type fieldType = fieldTypes[i]; object value; if (fieldType.IsCompilerGenerated()) { value = Activator.CreateInstance(fieldType); } else { value = Sync.ReadSyncObject(data, fieldType); } if (value == null) { if (cancelIfAnyNullBlacklist != null && !cancelIfAnyNullBlacklist.Contains(noTypePath)) { return; } if (path.EndsWith("$this")) { return; } if (cancelIfNull != null && cancelIfNull.Contains(noTypePath)) { return; } } if (removeNullsFromLists != null && removeNullsFromLists.Contains(noTypePath) && value is IList list) { list.RemoveNulls(); } MpReflection.SetValue(target, path, value); } if (context.HasFlag(SyncContext.MapSelected) && cancelIfNoSelectedObjects && Find.Selector.selected.Count == 0) { return; } object[] parameters = Sync.ReadSyncObjects(data, argTypes); MpLog.Log("Invoked delegate method " + method + " " + delegateType); method.Invoke(target, parameters); }
public override void Bind(object obj, string name) { object value = MpReflection.GetValue(obj, name); Type type = value.GetType(); var res = SyncSerialization.ReadSyncObject(reader, type); MpReflection.SetValue(obj, name, res); }
// todo what happens on exceptions? public static void FieldWatchPostfix() { if (Multiplayer.Client == null) { return; } while (watchedStack.Count > 0) { FieldData data = watchedStack.Pop(); if (data == null) { break; // The marker } SyncField handler = data.handler; object newValue = MpReflection.GetValue(data.target, handler.memberPath, data.index); bool changed = !Equals(newValue, data.oldValue); var cache = (handler.bufferChanges && !Multiplayer.IsReplay) ? bufferedChanges.GetValueSafe(handler) : null; if (cache != null && cache.TryGetValue(new Pair <object, object>(data.target, data.index), out BufferData cached)) { if (changed && cached.sent) { cached.sent = false; } cached.toSend = newValue; MpReflection.SetValue(data.target, handler.memberPath, cached.actualValue, data.index); continue; } if (!changed) { continue; } if (cache != null) { BufferData bufferData = new BufferData(data.oldValue, newValue); cache[new Pair <object, object>(data.target, data.index)] = bufferData; } else { handler.DoSync(data.target, newValue, data.index); } MpReflection.SetValue(data.target, handler.memberPath, data.oldValue, data.index); } }
protected override object ReadTarget(ByteReader data) { object target = Activator.CreateInstance(targetType); for (int i = 0; i < fieldPaths.Length; i++) { string path = fieldPaths[i]; string noTypePath = fieldPathsNoTypes[i]; Type fieldType = fieldTypes[i]; object value; if (fieldTransformers[i] is SyncTransformer tr) { value = tr.Reader.DynamicInvoke(SyncSerialization.ReadSyncObject(data, tr.NetworkType)); } else if (fieldType.IsCompilerGenerated()) { value = Activator.CreateInstance(fieldType); } else { value = SyncSerialization.ReadSyncObject(data, fieldType); } if (value == null) { if (allowedNull != null && !allowedNull.Contains(noTypePath)) { return(null); } if (noTypePath.EndsWith(DELEGATE_THIS)) { return(null); } if (cancelIfNull != null && cancelIfNull.Contains(noTypePath)) { return(null); } } if (removeNullsFromLists != null && removeNullsFromLists.Contains(noTypePath) && value is IList list) { list.RemoveNulls(); } MpReflection.SetValue(target, path, value); } return(target); }
public SyncMethod(Type targetType, string instancePath, string methodName, SyncType[] argTypes) { this.targetType = targetType; Type instanceType = targetType; if (!instancePath.NullOrEmpty()) { this.instancePath = instanceType + "/" + instancePath; instanceType = MpReflection.PathType(this.instancePath); } method = AccessTools.Method(instanceType, methodName, argTypes?.Select(t => t.type).ToArray()) ?? throw new Exception($"Couldn't find method {instanceType}::{methodName}"); this.argTypes = CheckArgs(argTypes); }
static void PatchIfExists(string typeName, string methodName, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null) { var type = MpReflection.GetTypeByName(typeName); if (type == null) { return; } var method = AccessTools.Method(type, methodName); if (method == null) { return; } Multiplayer.harmony.Patch(method, prefix, postfix, transpiler); }
public static void Init() { var harmony = Multiplayer.harmony; // Compat with Fluffy's mod loader var fluffysModButtonType = MpReflection.GetTypeByName("ModManager.ModButton_Installed"); if (fluffysModButtonType != null) { harmony.Patch( fluffysModButtonType.GetMethod("DoModButton"), new HarmonyMethod(typeof(PageModsPatch), nameof(PageModsPatch.ModManager_ButtonPrefix)), new HarmonyMethod(typeof(PageModsPatch), nameof(PageModsPatch.Postfix)) ); } var cancelForArbiter = new HarmonyMethod(typeof(CancelForArbiter), "Prefix"); // Fix PrisonLabor breaking the arbiter { PatchIfExists("PrisonLabor.Behaviour_MotivationIcon", "Update", cancelForArbiter); PatchIfExists("PrisonLabor.Core.GUI_Components.PawnIcons", "MapComponentTick", cancelForArbiter); PatchIfExists("PrisonLabor.MapComponent_Icons", "MapComponentTick", cancelForArbiter); } // PawnsAreCapable compat // Replace workSettings.SetPriority(def, p) with (workSettings.priorities[def] = p) // Minimizes side effects and goes around syncing // Also cache the dictionary to improve performance { var pawnsAreCapablePatch = new HarmonyMethod(typeof(ModPatches), nameof(PawnsAreCapable_FloatMenu_Patch_Transpiler)); PatchIfExists("PawnsAreCapable.FloatMenuMakerMap_ChoicesAtFor", "Prefix", transpiler: pawnsAreCapablePatch); PatchIfExists("PawnsAreCapable.FloatMenuMakerMap_ChoicesAtFor", "Postfix", transpiler: pawnsAreCapablePatch); } var randPatchPrefix = new HarmonyMethod(typeof(RandPatches), "Prefix"); var randPatchPostfix = new HarmonyMethod(typeof(RandPatches), "Postfix"); // Facial Stuff compat { PatchIfExists("FacialStuff.Harmony.HarmonyPatchesFS", "TryInteractWith_Postfix", randPatchPrefix, randPatchPostfix); } }
public SyncDelegate(Type delegateType, MethodInfo method, string[] fieldPaths) { this.delegateType = delegateType; this.method = method; argTypes = method.GetParameters().Types(); if (fieldPaths == null) { List <string> fieldList = new List <string>(); Sync.AllDelegateFieldsRecursive(delegateType, path => { fieldList.Add(path); return(false); }); this.fieldPaths = fieldList.ToArray(); } else { var temp = new UniqueList <string>(); foreach (string path in fieldPaths.Select(p => MpReflection.AppendType(p, delegateType))) { string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); string increment = parts[0] + "/" + parts[1]; for (int i = 2; i < parts.Length; i++) { if (!MpReflection.PathType(increment).IsCompilerGenerated()) { break; } temp.Add(increment); increment += "/" + parts[i]; } temp.Add(path); } this.fieldPaths = temp.ToArray(); } fieldTypes = this.fieldPaths.Select(path => MpReflection.PathType(path)).ToArray(); }
public SyncDelegate(Type delegateType, MethodInfo method, string[] inPaths) : base(delegateType, null, method, null) { if (inPaths == null) { List <string> fieldList = new List <string>(); AllDelegateFieldsRecursive(delegateType, path => { fieldList.Add(path); return(false); }); fieldPaths = fieldList.ToArray(); } else { var temp = new UniqueList <string>(); foreach (string path in inPaths.Select(p => MpReflection.AppendType(p, delegateType))) { string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); string increment = parts[0] + "/" + parts[1]; for (int i = 2; i < parts.Length; i++) { if (!MpReflection.PathType(increment).IsCompilerGenerated()) { break; } temp.Add(increment); increment += "/" + parts[i]; } temp.Add(path); } fieldPaths = temp.ToArray(); } fieldTypes = fieldPaths.Select(path => MpReflection.PathType(path)).ToArray(); fieldPathsNoTypes = fieldPaths.Select(path => MpReflection.RemoveType(path)).ToArray(); fieldTransformers = new SyncTransformer[fieldTypes.Length]; }
public static void SetPropertyOrField(this object obj, string memberPath, object value, object index = null) { MpReflection.SetValue(obj, memberPath, value, index); }
public static object GetPropertyOrField(this object obj, string memberPath, object index = null) { return(MpReflection.GetValue(obj, memberPath, index)); }