EntityControlManager partial rewrite to do lazy updating
Auto commit patches once SE instance has been created
This commit is contained in:
@@ -15,8 +15,6 @@ using Torch.Managers;
|
|||||||
using Torch.Server.ViewModels.Entities;
|
using Torch.Server.ViewModels.Entities;
|
||||||
using Torch.Utils;
|
using Torch.Utils;
|
||||||
|
|
||||||
using WeakEntityControlFactoryResult = System.Collections.Generic.KeyValuePair<System.WeakReference<Torch.Server.ViewModels.Entities.EntityViewModel>, System.WeakReference<Torch.Server.ViewModels.Entities.EntityControlViewModel>>;
|
|
||||||
|
|
||||||
namespace Torch.Server.Managers
|
namespace Torch.Server.Managers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -34,7 +32,59 @@ namespace Torch.Server.Managers
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Dictionary<Delegate, List<WeakEntityControlFactoryResult>> _modelFactories = new Dictionary<Delegate, List<WeakEntityControlFactoryResult>>();
|
private abstract class ModelFactory
|
||||||
|
{
|
||||||
|
private readonly ConditionalWeakTable<EntityViewModel, EntityControlViewModel> _models = new ConditionalWeakTable<EntityViewModel, EntityControlViewModel>();
|
||||||
|
|
||||||
|
public abstract Delegate Delegate { get; }
|
||||||
|
|
||||||
|
protected abstract EntityControlViewModel Create(EntityViewModel evm);
|
||||||
|
|
||||||
|
#pragma warning disable 649
|
||||||
|
[ReflectedGetter(Name = "Keys")]
|
||||||
|
private static readonly Func<ConditionalWeakTable<EntityViewModel, EntityControlViewModel>, ICollection<EntityViewModel>> _weakTableKeys;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Warning: Creates a giant list, avoid if possible.
|
||||||
|
/// </summary>
|
||||||
|
internal ICollection<EntityViewModel> 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<T> : ModelFactory where T : EntityViewModel
|
||||||
|
{
|
||||||
|
private readonly Func<T, EntityControlViewModel> _factory;
|
||||||
|
public override Delegate Delegate => _factory;
|
||||||
|
|
||||||
|
internal ModelFactory(Func<T, EntityControlViewModel> 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<ModelFactory> _modelFactories = new List<ModelFactory>();
|
||||||
private readonly List<Delegate> _controlFactories = new List<Delegate>();
|
private readonly List<Delegate> _controlFactories = new List<Delegate>();
|
||||||
|
|
||||||
private readonly List<WeakReference<EntityViewModel>> _boundEntityViewModels = new List<WeakReference<EntityViewModel>>();
|
private readonly List<WeakReference<EntityViewModel>> _boundEntityViewModels = new List<WeakReference<EntityViewModel>>();
|
||||||
@@ -52,8 +102,8 @@ namespace Torch.Server.Managers
|
|||||||
throw new ArgumentException("Generic type must match lamda type", nameof(modelFactory));
|
throw new ArgumentException("Generic type must match lamda type", nameof(modelFactory));
|
||||||
lock (this)
|
lock (this)
|
||||||
{
|
{
|
||||||
var results = new List<WeakEntityControlFactoryResult>();
|
var factory = new ModelFactory<TEntityBaseModel>(modelFactory);
|
||||||
_modelFactories.Add(modelFactory, results);
|
_modelFactories.Add(factory);
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
while (i < _boundEntityViewModels.Count)
|
while (i < _boundEntityViewModels.Count)
|
||||||
@@ -62,15 +112,7 @@ namespace Torch.Server.Managers
|
|||||||
_boundViewModels.TryGetValue(target, out MtObservableList<EntityControlViewModel> components))
|
_boundViewModels.TryGetValue(target, out MtObservableList<EntityControlViewModel> components))
|
||||||
{
|
{
|
||||||
if (target is TEntityBaseModel tent)
|
if (target is TEntityBaseModel tent)
|
||||||
{
|
UpdateBinding(target, components);
|
||||||
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<EntityViewModel>(target), new WeakReference<EntityControlViewModel>(result)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -91,16 +133,16 @@ namespace Torch.Server.Managers
|
|||||||
throw new ArgumentException("Generic type must match lamda type", nameof(modelFactory));
|
throw new ArgumentException("Generic type must match lamda type", nameof(modelFactory));
|
||||||
lock (this)
|
lock (this)
|
||||||
{
|
{
|
||||||
if (!_modelFactories.TryGetValue(modelFactory, out var results))
|
for (var i = 0; i < _modelFactories.Count; i++)
|
||||||
return;
|
|
||||||
_modelFactories.Remove(modelFactory);
|
|
||||||
foreach (WeakEntityControlFactoryResult result in results)
|
|
||||||
{
|
{
|
||||||
if (result.Key.TryGetTarget(out EntityViewModel target) &&
|
if (_modelFactories[i].Delegate == (Delegate)modelFactory)
|
||||||
result.Value.TryGetTarget(out EntityControlViewModel created)
|
|
||||||
&& _boundViewModels.TryGetValue(target, out MtObservableList<EntityControlViewModel> registered))
|
|
||||||
{
|
{
|
||||||
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<EntityControlViewModel>();
|
var binding = new MtObservableList<EntityControlViewModel>();
|
||||||
lock (this)
|
lock (this)
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<Delegate, List<WeakEntityControlFactoryResult>> 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<EntityViewModel>(key), new WeakReference<EntityControlViewModel>(result)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_boundEntityViewModels.Add(new WeakReference<EntityViewModel>(key));
|
_boundEntityViewModels.Add(new WeakReference<EntityViewModel>(key));
|
||||||
}
|
}
|
||||||
|
binding.PropertyChanged += (x, args) =>
|
||||||
|
{
|
||||||
|
if (nameof(binding.IsObserved).Equals(args.PropertyName))
|
||||||
|
UpdateBinding(key, binding);
|
||||||
|
};
|
||||||
return binding;
|
return binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateBinding(EntityViewModel key, MtObservableList<EntityControlViewModel> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -103,9 +103,19 @@ namespace Torch.Server
|
|||||||
MyPlugins.Load();
|
MyPlugins.Load();
|
||||||
MyGlobalTypeMetadata.Static.Init();
|
MyGlobalTypeMetadata.Static.Init();
|
||||||
|
|
||||||
|
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
|
||||||
GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
|
GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState)
|
||||||
|
{
|
||||||
|
if (newState == TorchSessionState.Unloading || newState == TorchSessionState.Unloaded)
|
||||||
|
{
|
||||||
|
_watchdog?.Dispose();
|
||||||
|
_watchdog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void InvokeBeforeRun()
|
private void InvokeBeforeRun()
|
||||||
{
|
{
|
||||||
MySandboxGame.Log.Init("SpaceEngineers-Dedicated.log", MyFinalBuildConstants.APP_VERSION_STRING);
|
MySandboxGame.Log.Init("SpaceEngineers-Dedicated.log", MyFinalBuildConstants.APP_VERSION_STRING);
|
||||||
|
@@ -28,9 +28,20 @@ namespace Torch.Managers.PatchManager.MSIL
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
internal MsilArgument(ParameterInfo local)
|
/// <summary>
|
||||||
|
/// Creates an argument from the given parameter info.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="local">parameter info to use</param>
|
||||||
|
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;
|
Type = local.ParameterType;
|
||||||
Name = local.Name;
|
Name = local.Name;
|
||||||
}
|
}
|
||||||
|
@@ -172,12 +172,9 @@ namespace Torch.Managers.PatchManager
|
|||||||
CommitInternal();
|
CommitInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc cref="Manager.Attach"/>
|
||||||
/// Commits any existing patches.
|
|
||||||
/// </summary>
|
|
||||||
public override void Attach()
|
public override void Attach()
|
||||||
{
|
{
|
||||||
Commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -260,7 +260,11 @@ namespace Torch
|
|||||||
GameStateChanged += (game, state) =>
|
GameStateChanged += (game, state) =>
|
||||||
{
|
{
|
||||||
if (state == TorchGameState.Created)
|
if (state == TorchGameState.Created)
|
||||||
|
{
|
||||||
ObjectFactoryInitPatch.ForceRegisterAssemblies();
|
ObjectFactoryInitPatch.ForceRegisterAssemblies();
|
||||||
|
// safe to commit here; all important static ctors have run
|
||||||
|
PatchManager.CommitInternal();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Debug.Assert(MyPerGameSettings.BasicGameInfo.GameVersion != null, "MyPerGameSettings.BasicGameInfo.GameVersion != null");
|
Debug.Assert(MyPerGameSettings.BasicGameInfo.GameVersion != null, "MyPerGameSettings.BasicGameInfo.GameVersion != null");
|
||||||
@@ -292,6 +296,10 @@ namespace Torch
|
|||||||
Managers.GetManager<PluginManager>().LoadPlugins();
|
Managers.GetManager<PluginManager>().LoadPlugins();
|
||||||
Managers.Attach();
|
Managers.Attach();
|
||||||
_init = true;
|
_init = true;
|
||||||
|
|
||||||
|
if (GameState >= TorchGameState.Created && GameState < TorchGameState.Unloading)
|
||||||
|
// safe to commit here; all important static ctors have run
|
||||||
|
PatchManager.CommitInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSessionLoading()
|
private void OnSessionLoading()
|
||||||
|
Reference in New Issue
Block a user