diff --git a/Torch.Server/Managers/EntityControlManager.cs b/Torch.Server/Managers/EntityControlManager.cs index 308cb61..5f43884 100644 --- a/Torch.Server/Managers/EntityControlManager.cs +++ b/Torch.Server/Managers/EntityControlManager.cs @@ -15,8 +15,6 @@ using Torch.Managers; using Torch.Server.ViewModels.Entities; using Torch.Utils; -using WeakEntityControlFactoryResult = System.Collections.Generic.KeyValuePair, System.WeakReference>; - namespace Torch.Server.Managers { /// @@ -34,7 +32,59 @@ namespace Torch.Server.Managers { } - private readonly Dictionary> _modelFactories = new Dictionary>(); + private abstract class ModelFactory + { + private readonly ConditionalWeakTable _models = new ConditionalWeakTable(); + + public abstract Delegate Delegate { get; } + + protected abstract EntityControlViewModel Create(EntityViewModel evm); + +#pragma warning disable 649 + [ReflectedGetter(Name = "Keys")] + private static readonly Func, ICollection> _weakTableKeys; +#pragma warning restore 649 + + /// + /// Warning: Creates a giant list, avoid if possible. + /// + internal ICollection Keys => _weakTableKeys(_models); + + internal EntityControlViewModel GetOrCreate(EntityViewModel evm) + { + return _models.GetValue(evm, Create); + } + + internal bool TryGet(EntityViewModel evm, out EntityControlViewModel res) + { + return _models.TryGetValue(evm, out res); + } + } + + private class ModelFactory : ModelFactory where T : EntityViewModel + { + private readonly Func _factory; + public override Delegate Delegate => _factory; + + internal ModelFactory(Func factory) + { + _factory = factory; + } + + + protected override EntityControlViewModel Create(EntityViewModel evm) + { + if (evm is T m) + { + var result = _factory(m); + _log.Debug($"Model factory {_factory.Method} created {result} for {evm}"); + return result; + } + return null; + } + } + + private readonly List _modelFactories = new List(); private readonly List _controlFactories = new List(); private readonly List> _boundEntityViewModels = new List>(); @@ -52,8 +102,8 @@ namespace Torch.Server.Managers throw new ArgumentException("Generic type must match lamda type", nameof(modelFactory)); lock (this) { - var results = new List(); - _modelFactories.Add(modelFactory, results); + var factory = new ModelFactory(modelFactory); + _modelFactories.Add(factory); var i = 0; while (i < _boundEntityViewModels.Count) @@ -62,15 +112,7 @@ namespace Torch.Server.Managers _boundViewModels.TryGetValue(target, out MtObservableList components)) { if (target is TEntityBaseModel tent) - { - EntityControlViewModel result = modelFactory.Invoke(tent); - if (result != null) - { - _log.Debug($"Model factory {modelFactory.Method} created {result} for {tent}"); - components.Add(result); - results.Add(new WeakEntityControlFactoryResult(new WeakReference(target), new WeakReference(result))); - } - } + UpdateBinding(target, components); i++; } else @@ -91,16 +133,16 @@ namespace Torch.Server.Managers throw new ArgumentException("Generic type must match lamda type", nameof(modelFactory)); lock (this) { - if (!_modelFactories.TryGetValue(modelFactory, out var results)) - return; - _modelFactories.Remove(modelFactory); - foreach (WeakEntityControlFactoryResult result in results) + for (var i = 0; i < _modelFactories.Count; i++) { - if (result.Key.TryGetTarget(out EntityViewModel target) && - result.Value.TryGetTarget(out EntityControlViewModel created) - && _boundViewModels.TryGetValue(target, out MtObservableList registered)) + if (_modelFactories[i].Delegate == (Delegate)modelFactory) { - registered.Remove(created); + foreach (var entry in _modelFactories[i].Keys) + if (_modelFactories[i].TryGet(entry, out EntityControlViewModel ecvm) && ecvm != null + && _boundViewModels.TryGetValue(entry, out var binding)) + binding.Remove(ecvm); + _modelFactories.RemoveAt(i); + break; } } } @@ -194,21 +236,30 @@ namespace Torch.Server.Managers var binding = new MtObservableList(); lock (this) { - foreach (KeyValuePair> factory in _modelFactories) - { - Type ptype = factory.Key.Method.GetParameters()[0].ParameterType; - if (ptype.IsInstanceOfType(key) && - factory.Key.DynamicInvoke(key) is EntityControlViewModel result) - { - _log.Debug($"Model factory {factory.Key.Method} created {result} for {key}"); - binding.Add(result); - result.InvalidateControl(); - factory.Value.Add(new WeakEntityControlFactoryResult(new WeakReference(key), new WeakReference(result))); - } - } _boundEntityViewModels.Add(new WeakReference(key)); } + binding.PropertyChanged += (x, args) => + { + if (nameof(binding.IsObserved).Equals(args.PropertyName)) + UpdateBinding(key, binding); + }; return binding; } + + private void UpdateBinding(EntityViewModel key, MtObservableList binding) + { + if (!binding.IsObserved) + return; + + lock (this) + { + foreach (ModelFactory factory in _modelFactories) + { + var result = factory.GetOrCreate(key); + if (result != null && !binding.Contains(result)) + binding.Add(result); + } + } + } } } diff --git a/Torch.Server/TorchServer.cs b/Torch.Server/TorchServer.cs index ab776c1..502d94b 100644 --- a/Torch.Server/TorchServer.cs +++ b/Torch.Server/TorchServer.cs @@ -103,9 +103,19 @@ namespace Torch.Server MyPlugins.Load(); MyGlobalTypeMetadata.Static.Init(); + Managers.GetManager().SessionStateChanged += OnSessionStateChanged; GetManager().LoadInstance(Config.InstancePath); } + private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState) + { + if (newState == TorchSessionState.Unloading || newState == TorchSessionState.Unloaded) + { + _watchdog?.Dispose(); + _watchdog = null; + } + } + private void InvokeBeforeRun() { MySandboxGame.Log.Init("SpaceEngineers-Dedicated.log", MyFinalBuildConstants.APP_VERSION_STRING); diff --git a/Torch/Managers/PatchManager/MSIL/MsilArgument.cs b/Torch/Managers/PatchManager/MSIL/MsilArgument.cs index 1246546..cb2e081 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilArgument.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilArgument.cs @@ -28,9 +28,20 @@ namespace Torch.Managers.PatchManager.MSIL /// public string Name { get; } - internal MsilArgument(ParameterInfo local) + /// + /// Creates an argument from the given parameter info. + /// + /// parameter info to use + public MsilArgument(ParameterInfo local) { - Position = (((MethodBase)local.Member).IsStatic ? 0 : 1) + local.Position; + bool isStatic; + if (local.Member is FieldInfo fi) + isStatic = fi.IsStatic; + else if (local.Member is MethodBase mb) + isStatic = mb.IsStatic; + else + throw new ArgumentException("ParameterInfo.Member must be MethodBase or FieldInfo", nameof(local)); + Position = (isStatic ? 0 : 1) + local.Position; Type = local.ParameterType; Name = local.Name; } diff --git a/Torch/Managers/PatchManager/PatchManager.cs b/Torch/Managers/PatchManager/PatchManager.cs index bcc90c4..3e24fa7 100644 --- a/Torch/Managers/PatchManager/PatchManager.cs +++ b/Torch/Managers/PatchManager/PatchManager.cs @@ -172,12 +172,9 @@ namespace Torch.Managers.PatchManager CommitInternal(); } - /// - /// Commits any existing patches. - /// + /// public override void Attach() { - Commit(); } /// diff --git a/Torch/TorchBase.cs b/Torch/TorchBase.cs index 32561c8..688e49a 100644 --- a/Torch/TorchBase.cs +++ b/Torch/TorchBase.cs @@ -260,7 +260,11 @@ namespace Torch GameStateChanged += (game, state) => { if (state == TorchGameState.Created) + { ObjectFactoryInitPatch.ForceRegisterAssemblies(); + // safe to commit here; all important static ctors have run + PatchManager.CommitInternal(); + } }; Debug.Assert(MyPerGameSettings.BasicGameInfo.GameVersion != null, "MyPerGameSettings.BasicGameInfo.GameVersion != null"); @@ -292,6 +296,10 @@ namespace Torch Managers.GetManager().LoadPlugins(); Managers.Attach(); _init = true; + + if (GameState >= TorchGameState.Created && GameState < TorchGameState.Unloading) + // safe to commit here; all important static ctors have run + PatchManager.CommitInternal(); } private void OnSessionLoading()