private static void WriteDelegate(ByteWriter writer, Delegate del) { writer.WriteBool(del != null); if (del == null) { return; } SyncSerialization.WriteSync(writer, del.GetType()); SyncSerialization.WriteSync(writer, del.Method.DeclaringType); SyncSerialization.WriteSync(writer, del.Method.Name); // todo Handle the signature for ambiguous methods writer.WriteBool(del.Target != null); if (del.Target != null) { var targetType = del.Target.GetType(); SyncSerialization.WriteSync(writer, targetType); var fieldPaths = GetFields(targetType).ToArray(); var fieldTypes = fieldPaths.Select(path => MpReflection.PathType(path)).ToArray(); void SyncObj(object obj, Type type, string debugInfo) { if (type.IsCompilerGenerated()) { return; } if (writer is LoggingByteWriter log1) { log1.Log.Enter(debugInfo); } try { if (typeof(Delegate).IsAssignableFrom(type)) { WriteDelegate(writer, (Delegate)obj); } else { SyncSerialization.WriteSyncObject(writer, obj, type); } } finally { if (writer is LoggingByteWriter log2) { log2.Log.Exit(); } } } for (int i = 0; i < fieldPaths.Length; i++) { SyncObj(del.Target.GetPropertyOrField(fieldPaths[i]), fieldTypes[i], fieldPaths[i]); } } }
/// <summary> /// Returns whether the original should be cancelled /// </summary> public bool DoSync(object target, params object[] args) { if (!Multiplayer.ShouldSync) { return(false); } // todo limit per specific target/argument //if (Utils.MillisNow - lastSendTime < minTime) // return true; LoggingByteWriter writer = new LoggingByteWriter(); MpContext context = writer.MpContext(); writer.Log.Node(ToString()); writer.WriteInt32(syncId); SyncUtil.WriteContext(this, writer); var map = context.map; void SyncObj(object obj, SyncType type, string debugInfo) { writer.Log.Enter(debugInfo); try { SyncSerialization.WriteSyncObject(writer, obj, type); } finally { writer.Log.Exit(); } if (type.contextMap && obj is Map contextMap) { map = contextMap; } if (context.map is Map newMap) { if (map != null && map != newMap) { throw new Exception($"{this}: map mismatch ({map?.uniqueID} and {newMap?.uniqueID})"); } map = newMap; } } if (targetTransformer != null) { SyncObj(targetTransformer.Writer.DynamicInvoke(target, target, args), targetTransformer.NetworkType, "Target (transformed)"); } else { WriteTarget(target, args, SyncObj); } for (int i = 0; i < argTypes.Length; i++) { if (argTransformers[i] == null) { SyncObj(args[i], argTypes[i], $"Arg {i} {argNames[i]}"); } } for (int i = 0; i < argTypes.Length; i++) { if (argTransformers[i] is { } trans) { SyncObj(trans.Writer.DynamicInvoke(args[i], target, args), trans.NetworkType, $"Arg {i} {argNames[i]} (transformed)"); } } int mapId = map?.uniqueID ?? ScheduledCommand.Global; writer.Log.Node("Map id: " + mapId); Multiplayer.WriterLog.AddCurrentNode(writer); Multiplayer.Client.SendCommand(CommandType.Sync, mapId, writer.ToArray()); lastSendTime = Utils.MillisNow; return(true); }