private List <Entity> DeepCloneEntities(List <Entity> entities)
        {
            entities = entities.Where(entity => {
                // 不恢复设置了 IgnoreSaveLoadComponent 的物体
                // SpeedrunTool 里有 ConfettiRenderer 和一些 MiniTextbox
                if (entity.IsIgnoreSaveLoad())
                {
                    return(false);
                }

                // 不恢复 CelesteNet 的物体
                if (entity.GetType().FullName is string name && name.StartsWith("Celeste.Mod.CelesteNet"))
                {
                    return(false);
                }

                return(true);
            }).ToList();

            Dictionary <int, Dictionary <string, object> > dynDataDict = new Dictionary <int, Dictionary <string, object> >();
            EntitiesWrapper entitiesWrapper = new EntitiesWrapper(entities, dynDataDict);

            // Find the dynData.Data that need to be cloned
            for (int i = 0; i < entities.Count; i++)
            {
                Entity entity = entities[i];
                if (DynDataUtils.GetDataMap(entity.GetType())?.Count == 0)
                {
                    continue;
                }

                if (DynDataUtils.GetDate(entity) is Dictionary <string, object> data && data.Count > 0)
                {
                    dynDataDict.Add(i, data);
                }
            }

            // DeepClone together make them share same object.
            EntitiesWrapper clonedEntitiesWrapper = entitiesWrapper.DeepClone();

            // Copy dynData.Data
            Dictionary <int, Dictionary <string, object> > clonedDynDataDict = entitiesWrapper.DynDataDict;

            foreach (int i in clonedDynDataDict.Keys)
            {
                Entity clonedEntity = clonedEntitiesWrapper.Entities[i];
                if (DynDataUtils.GetDate(clonedEntity) is Dictionary <string, object> data)
                {
                    Dictionary <string, object> clonedData = clonedEntitiesWrapper.DynDataDict[i];
                    foreach (string key in clonedData.Keys)
                    {
                        data[key] = clonedData[key];
                    }
                }
            }

            return(clonedEntitiesWrapper.Entities);
        }
예제 #2
0
        public static void Config()
        {
            // Clone 开始时,判断哪些类型是直接使用原对象而不 DeepClone 的
            // Before cloning, determine which types use the original object directly
            DeepCloner.AddKnownTypesProcessor(type => {
                if (
                    // Celeste Singleton
                    type == typeof(Celeste) ||
                    type == typeof(Settings)

                    // Everest
                    || type.IsSubclassOf(typeof(ModAsset)) ||
                    type.IsSubclassOf(typeof(EverestModule)) ||
                    type == typeof(EverestModuleMetadata)

                    // Monocle
                    || type == typeof(GraphicsDevice) ||
                    type == typeof(GraphicsDeviceManager) ||
                    type == typeof(Monocle.Commands) ||
                    type == typeof(Pooler) ||
                    type == typeof(BitTag) ||
                    type == typeof(Atlas)

                    // XNA GraphicsResource
                    || type.IsSubclassOf(typeof(GraphicsResource))

                    // NLua
                    || type == typeof(Lua) ||
                    type.IsSubclassOf(typeof(LuaBase))

                    // CelesteNet
                    || type.FullName != null && type.FullName.StartsWith("Celeste.Mod.CelesteNet.")
                    )
                {
                    return(true);
                }

                return(null);
            });

            // Clone 对象的字段前,判断哪些类型是直接使用原对象或者自行通过其它方法 clone
            // Before cloning object's field, determine which types are directly used by the original object
            DeepCloner.AddPreCloneProcessor((sourceObj, deepCloneState) => {
                lock (sourceObj) {
                    if (sourceObj is Level)
                    {
                        // 金草莓死亡或者 PageDown/Up 切换房间后等等改变 Level 实例的情况
                        // After golden strawberry deaths or changing rooms w/ Page Down / Up
                        if (Engine.Scene is Level level)
                        {
                            return(level);
                        }
                        return(sourceObj);
                    }

                    if (sourceObj is Entity entity &&
                        entity.TagCheck(Tags.Global) &&
                        !(entity is CassetteBlockManager) &&
                        !(entity is SeekerBarrierRenderer) &&
                        !(entity is LightningRenderer) &&
                        !(entity is SpeedrunTimerDisplay)
                        // Fixes: Glyph Teleport Area Effect
                        && entity.GetType().FullName != "Celeste.Mod.AcidHelper.Entities.InstantTeleporterRenderer" &&
                        entity.GetType().FullName != "VivHelper.Entities.HoldableBarrierRenderer"
                        )
                    {
                        return(sourceObj);
                    }

                    // 不要克隆 RendererList 会造成 BeforeRender 中产生空指针异常
                    // 克隆了 level.RendererList 但不想克隆以下元素
                    // if (sourceObj is GameplayRenderer || sourceObj is LightingRenderer) {
                    // return sourceObj;
                    // }

                    lock (sourceObj) {
                        // 稍后重新创建正在播放的 SoundSource 里的 EventInstance 实例
                        if (sourceObj is SoundSource source
                            // TODO SoundEmitter 的声音会存留在关卡中,切换房间后保存依然会播放
                            && !(source.Entity is SoundEmitter) &&
                            source.Playing &&
                            source.GetFieldValue("instance") is EventInstance instance)
                        {
                            if (string.IsNullOrEmpty(source.EventName))
                            {
                                return(null);
                            }
                            instance.NeedManualClone();
                            instance.SaveTimelinePosition(instance.LoadTimelinePosition());

                            return(null);
                        }

                        if (sourceObj is CassetteBlockManager manager)
                        {
                            // isLevelMusic = true 时 sfx 自动等于 Audio.CurrentMusicEventInstance,无需重建
                            if (manager.GetFieldValue("sfx") is EventInstance sfx &&
                                !(bool)manager.GetFieldValue("isLevelMusic"))
                            {
                                sfx.NeedManualClone();
                            }

                            if (manager.GetFieldValue("snapshot") is EventInstance snapshot)
                            {
                                snapshot.NeedManualClone();
                            }

                            return(null);
                        }

                        // 重新创建正在播放的 EventInstance 实例
                        if (sourceObj is EventInstance eventInstance && eventInstance.IsNeedManualClone())
                        {
                            return(eventInstance.Clone());
                        }

                        // Fixes: 克隆 WeakReference 后 Target 没有一起被克隆的问题,修复 dynData.Weak 克隆不完整导致的一些报错
                        // System.Reflection.TargetException: 非静态字段需要一个目标。
                        // 在 System.Reflection.RtFieldInfo.CheckConsistency(Object target)
                        // 在 System.Reflection.RtFieldInfo.InternalGetValue(Object obj, StackCrawlMark& stackMark)
                        // 在 System.Reflection.RtFieldInfo.GetValue(Object obj)
                        // 在 MonoMod.Utils.DynData`1.<>c__DisplayClass19_0.<.cctor>b__1(TTarget obj)
                        // 在 MonoMod.Utils.DynData`1.get_Item(String name)
                        // 在 Celeste.Mod.MaxHelpingHand.Entities.CustomizableRefill.<>c__DisplayClass0_0.<.ctor>b__0(Player player)
                        if (sourceObj is WeakReference sourceWeak)
                        {
                            return(new WeakReference(sourceWeak.Target.DeepClone(deepCloneState), sourceWeak.TrackResurrection));
                        }

                        // 修复启用 CelesteNet 后保存状态时的崩溃
                        // System.ObjectDisposedException: 无法访问已释放的对象。
                        // 对象名:“RenderTarget2D”。
                        if (sourceObj is RenderTarget2D renderTarget2D && renderTarget2D.IsDisposed)
                        {
                            return(new RenderTarget2D(
                                       renderTarget2D.GraphicsDevice,
                                       renderTarget2D.Width,
                                       renderTarget2D.Height,
                                       renderTarget2D.LevelCount != 1,
                                       renderTarget2D.Format,
                                       renderTarget2D.DepthStencilFormat,
                                       0,
                                       renderTarget2D.RenderTargetUsage
                                       ));
                        }
                    }
                }

                return(null);
            });

            // Clone 对象的字段后,进行自定的处理
            // After cloning, perform custom processing
            DeepCloner.AddPostCloneProcessor((sourceObj, clonedObj, deepCloneState) => {
                if (clonedObj == null)
                {
                    return(null);
                }

                lock (sourceObj) {
                    // 修复:DeepClone 的 hashSet.Containes(里面存在的引用对象) 总是返回 False,Dictionary 无此问题
                    // 原因:没有重写 GetHashCode 方法 https://github.com/force-net/DeepCloner/issues/17#issuecomment-678650032
                    // Fix: DeepClone's hashSet.Contains (ReferenceType) always returns false, Dictionary has no such problem
                    if (clonedObj.GetType().IsHashSet(out Type hashSetElementType) && !hashSetElementType.IsSimple())
                    {
                        IEnumerator enumerator = ((IEnumerable)clonedObj).GetEnumerator();

                        List <object> backup = new List <object>();
                        while (enumerator.MoveNext())
                        {
                            backup.Add(enumerator.Current);
                        }

                        if (backup.Count == 0)
                        {
                            return(clonedObj);
                        }

                        clonedObj.InvokeMethod("Clear");
                        backup.ForEach(obj => {
                            if (obj != null)
                            {
                                clonedObj.InvokeMethod("Add", obj);
                            }
                        });
                    }

                    // 同上
                    if (clonedObj.GetType().IsDictionary(out Type dictKeyType, out Type _) &&
                        !dictKeyType.IsSimple() && clonedObj is IDictionary clonedDict && clonedDict.Count > 0
                        )
                    {
                        Dictionary <object, object> backupDict = new Dictionary <object, object>();
                        backupDict.AddRange(clonedDict);
                        clonedDict.Clear();
                        clonedDict.AddRange(backupDict);
                    }

                    // LightingRenderer 需要,不然不会发光
                    if (clonedObj is VertexLight vertexLight)
                    {
                        vertexLight.Index = -1;
                    }

                    // Clone dynData.Data
                    if (sourceObj.GetType() is Type objType && objType.IsClass)
                    {
                        do
                        {
                            object dataMap = DynDataUtils.GetDataMap(objType);
                            if (dataMap == null)
                            {
                                continue;
                            }

                            object[] parameters = { sourceObj, null };
                            if (false == (bool)dataMap.InvokeMethod("TryGetValue", parameters))
                            {
                                continue;
                            }

                            object sourceValue = parameters[1];
                            if (!(sourceValue.GetFieldValue("Data") is Dictionary <string, object> data) || data.Count == 0)
                            {
                                continue;
                            }

                            dataMap.InvokeMethod("Add", clonedObj, sourceValue.DeepClone(deepCloneState));
                        } while ((objType = objType.BaseType) != null && objType.IsSameOrSubclassOf(typeof(object)));
                    }

                    // CLone DynamicData
                    if (DynDataUtils.DynamicDataMap.Value is object dynamicDataMap)
                    {
                        object[] parameters = { sourceObj, null };
                        if ((bool)dynamicDataMap.InvokeMethod("TryGetValue", parameters))
                        {
                            object sourceValue = parameters[1];
                            if (sourceValue.GetFieldValue("Data") is Dictionary <string, object> data && data.Count != 0)
                            {
                                dynamicDataMap.InvokeMethod("Add", clonedObj, sourceValue.DeepClone(deepCloneState));
                            }
                        }
                    }

                    if (clonedObj is VirtualTexture virtualTexture && virtualTexture.IsDisposed)
                    {
                        virtualTexture.Reload();
                    }

                    if (clonedObj is VirtualRenderTarget virtualRenderTarget && virtualRenderTarget.IsDisposed)
                    {
                        virtualRenderTarget.Reload();
                    }
                }

                return(clonedObj);
            });
        }