Log errors when a plugin's control throws exceptions instead of just letting WPF die.
Also add some new types for Essentials
This commit is contained in:
@@ -55,6 +55,15 @@ quit";
|
|||||||
AppDomain.CurrentDomain.UnhandledException += HandleException;
|
AppDomain.CurrentDomain.UnhandledException += HandleException;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
//enables logging debug messages when built in debug mode. Amazing.
|
||||||
|
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "main");
|
||||||
|
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "console");
|
||||||
|
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Debug, "wpf");
|
||||||
|
LogManager.ReconfigExistingLoggers();
|
||||||
|
Log.Debug("Debug logging enabled.");
|
||||||
|
#endif
|
||||||
|
|
||||||
// This is what happens when Keen is bad and puts extensions into the System namespace.
|
// This is what happens when Keen is bad and puts extensions into the System namespace.
|
||||||
if (!Enumerable.Contains(args, "-noupdate"))
|
if (!Enumerable.Contains(args, "-noupdate"))
|
||||||
RunSteamCmd();
|
RunSteamCmd();
|
||||||
|
@@ -5,6 +5,7 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using NLog;
|
||||||
using Torch.API;
|
using Torch.API;
|
||||||
using Torch.API.Plugins;
|
using Torch.API.Plugins;
|
||||||
using Torch.Server.Views;
|
using Torch.Server.Views;
|
||||||
@@ -17,12 +18,24 @@ namespace Torch.Server.ViewModels
|
|||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public ITorchPlugin Plugin { get; }
|
public ITorchPlugin Plugin { get; }
|
||||||
|
|
||||||
|
private static Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
public PluginViewModel(ITorchPlugin plugin)
|
public PluginViewModel(ITorchPlugin plugin)
|
||||||
{
|
{
|
||||||
Plugin = plugin;
|
Plugin = plugin;
|
||||||
|
|
||||||
if (Plugin is IWpfPlugin p)
|
if (Plugin is IWpfPlugin p)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
Control = p.GetControl();
|
Control = p.GetControl();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Error(ex, $"Exception loading interface for plugin {Plugin.Name}! Plugin interface will not be available!");
|
||||||
|
Control = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Name = $"{plugin.Name} ({plugin.Version})";
|
Name = $"{plugin.Name} ({plugin.Version})";
|
||||||
|
|
||||||
|
272
Torch/Collections/BinaryMinHeap.cs
Normal file
272
Torch/Collections/BinaryMinHeap.cs
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Torch.Collections
|
||||||
|
{
|
||||||
|
public class BinaryMinHeap<TKey, TValue> where TKey : IComparable
|
||||||
|
{
|
||||||
|
private struct HeapItem
|
||||||
|
{
|
||||||
|
public TKey Key { get; }
|
||||||
|
public TValue Value { get; }
|
||||||
|
|
||||||
|
public HeapItem(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HeapItem[] _store;
|
||||||
|
private readonly IComparer<TKey> _comparer;
|
||||||
|
|
||||||
|
public int Capacity { get; private set; }
|
||||||
|
public int Count { get; private set; }
|
||||||
|
|
||||||
|
public bool Full => Count == Capacity;
|
||||||
|
|
||||||
|
public BinaryMinHeap(int initialCapacity = 32, IComparer<TKey> comparer = null)
|
||||||
|
{
|
||||||
|
_store = new HeapItem[initialCapacity];
|
||||||
|
Count = 0;
|
||||||
|
Capacity = initialCapacity;
|
||||||
|
_comparer = comparer ?? Comparer<TKey>.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Insert(TValue value, TKey key)
|
||||||
|
{
|
||||||
|
EnsureCapacity(Capacity + 1);
|
||||||
|
|
||||||
|
var item = new HeapItem(key, value);
|
||||||
|
|
||||||
|
_store[Count] = item;
|
||||||
|
|
||||||
|
Up(Count);
|
||||||
|
Count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue Min()
|
||||||
|
{
|
||||||
|
return _store[0].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TKey MinKey()
|
||||||
|
{
|
||||||
|
return _store[0].Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue RemoveMin()
|
||||||
|
{
|
||||||
|
TValue toReturn = _store[0].Value;
|
||||||
|
|
||||||
|
if (Count != 1)
|
||||||
|
{
|
||||||
|
SwapIndices(Count - 1, 0);
|
||||||
|
_store[Count - 1] = default(HeapItem);
|
||||||
|
Count--;
|
||||||
|
Down(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Count--;
|
||||||
|
_store[0] = default(HeapItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue RemoveMax()
|
||||||
|
{
|
||||||
|
Debug.Assert(Count > 0);
|
||||||
|
|
||||||
|
var maxIndex = 0;
|
||||||
|
|
||||||
|
var maxItem = _store[0];
|
||||||
|
|
||||||
|
for (var i = 1; i < Count; ++i)
|
||||||
|
{
|
||||||
|
var c = _store[i];
|
||||||
|
if (_comparer.Compare(maxItem.Key, c.Key) < 0)
|
||||||
|
{
|
||||||
|
maxIndex = i;
|
||||||
|
maxItem = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxIndex != Count)
|
||||||
|
{
|
||||||
|
SwapIndices(Count - 1, maxIndex);
|
||||||
|
Up(maxIndex);
|
||||||
|
}
|
||||||
|
Count--;
|
||||||
|
|
||||||
|
return maxItem.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue Remove(TValue value, IEqualityComparer<TValue> comparer = null)
|
||||||
|
{
|
||||||
|
if (Count == 0)
|
||||||
|
return default(TValue);
|
||||||
|
|
||||||
|
if (comparer == null)
|
||||||
|
comparer = EqualityComparer<TValue>.Default;
|
||||||
|
|
||||||
|
var itemIndex = -1;
|
||||||
|
|
||||||
|
for (var i = 0; i < Count; ++i)
|
||||||
|
{
|
||||||
|
if (comparer.Equals(value, _store[i].Value))
|
||||||
|
{
|
||||||
|
itemIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemIndex != Count && itemIndex != -1)
|
||||||
|
{
|
||||||
|
TValue removed = _store[itemIndex].Value;
|
||||||
|
|
||||||
|
SwapIndices(Count - 1, itemIndex);
|
||||||
|
Up(itemIndex);
|
||||||
|
Down(itemIndex);
|
||||||
|
|
||||||
|
Count--;
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return default(TValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue Remove(TKey key)
|
||||||
|
{
|
||||||
|
Debug.Assert(Count > 0);
|
||||||
|
|
||||||
|
var itemIndex = 0;
|
||||||
|
|
||||||
|
for (var i = 1; i < Count; ++i)
|
||||||
|
{
|
||||||
|
if (_comparer.Compare(key, _store[i].Key) == 0)
|
||||||
|
itemIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
TValue removed;
|
||||||
|
|
||||||
|
if (itemIndex != Count)
|
||||||
|
{
|
||||||
|
removed = _store[itemIndex].Value;
|
||||||
|
|
||||||
|
SwapIndices(Count - 1, itemIndex);
|
||||||
|
Up(itemIndex);
|
||||||
|
Down(itemIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
removed = default(TValue);
|
||||||
|
|
||||||
|
Count--;
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Array.Clear(_store, 0, Capacity);
|
||||||
|
Count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Up(int index)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
return;
|
||||||
|
int parentIndex = (index - 1) / 2;
|
||||||
|
HeapItem swap = _store[index];
|
||||||
|
if (_comparer.Compare(_store[parentIndex].Key, swap.Key) <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
SwapIndices(parentIndex, index);
|
||||||
|
index = parentIndex;
|
||||||
|
|
||||||
|
if (index == 0)
|
||||||
|
break;
|
||||||
|
parentIndex = (index - 1) / 2;
|
||||||
|
if (_comparer.Compare(_store[parentIndex].Key, swap.Key) <= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertItem(ref swap, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Down(int index)
|
||||||
|
{
|
||||||
|
if (Count == index + 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int left = index * 2 + 1;
|
||||||
|
int right = left + 1;
|
||||||
|
|
||||||
|
HeapItem swap = _store[index];
|
||||||
|
|
||||||
|
while (right <= Count) // While the current node has children
|
||||||
|
{
|
||||||
|
var nLeft = _store[left];
|
||||||
|
var nRight = _store[right];
|
||||||
|
|
||||||
|
if (right == Count || _comparer.Compare(nLeft.Key, nRight.Key) < 0) // Only the left child exists or the left child is smaller
|
||||||
|
{
|
||||||
|
if (_comparer.Compare(swap.Key, nLeft.Key) <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
SwapIndices(left, index);
|
||||||
|
|
||||||
|
index = left;
|
||||||
|
left = index * 2 + 1;
|
||||||
|
right = left + 1;
|
||||||
|
}
|
||||||
|
else // Right child exists and is smaller
|
||||||
|
{
|
||||||
|
if (_comparer.Compare(swap.Key, nRight.Key) <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
SwapIndices(right, index);
|
||||||
|
|
||||||
|
index = right;
|
||||||
|
left = index * 2 + 1;
|
||||||
|
right = left + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertItem(ref swap, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SwapIndices(int fromIndex, int toIndex)
|
||||||
|
{
|
||||||
|
_store[toIndex] = _store[fromIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InsertItem(ref HeapItem fromItem, int toIndex)
|
||||||
|
{
|
||||||
|
_store[toIndex] = fromItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureCapacity(int capacity)
|
||||||
|
{
|
||||||
|
if (_store.Length >= capacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//double capacity until we reach the minimum requested capacity (or greater)
|
||||||
|
int newcap = Capacity * 2;
|
||||||
|
while (newcap < capacity)
|
||||||
|
newcap *= 2;
|
||||||
|
|
||||||
|
var newArray = new HeapItem[newcap];
|
||||||
|
Array.Copy(_store, newArray, Capacity);
|
||||||
|
|
||||||
|
_store = newArray;
|
||||||
|
Capacity = newcap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2,13 +2,16 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Torch.Annotations;
|
||||||
|
|
||||||
namespace Torch.Collections
|
namespace Torch.Collections
|
||||||
{
|
{
|
||||||
public class SortedView<T>: IReadOnlyCollection<T>, INotifyCollectionChanged
|
public class SortedView<T>: IReadOnlyCollection<T>, INotifyCollectionChanged, INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private readonly MtObservableCollectionBase<T> _backing;
|
private readonly MtObservableCollectionBase<T> _backing;
|
||||||
private IComparer<T> _comparer;
|
private IComparer<T> _comparer;
|
||||||
@@ -21,9 +24,15 @@ namespace Torch.Collections
|
|||||||
_store = new List<T>(_backing.Count);
|
_store = new List<T>(_backing.Count);
|
||||||
_store.AddRange(_backing);
|
_store.AddRange(_backing);
|
||||||
_backing.CollectionChanged += backing_CollectionChanged;
|
_backing.CollectionChanged += backing_CollectionChanged;
|
||||||
|
_backing.PropertyChanged += backing_PropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void backing_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
private void backing_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
OnPropertyChanged(e.PropertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backing_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
@@ -122,5 +131,12 @@ namespace Torch.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
[NotifyPropertyChangedInvocator]
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
134
Torch/Collections/SystemSortedView.cs
Normal file
134
Torch/Collections/SystemSortedView.cs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Torch.Annotations;
|
||||||
|
|
||||||
|
namespace Torch.Collections
|
||||||
|
{
|
||||||
|
public class SystemSortedView<T> : IReadOnlyCollection<T>, INotifyCollectionChanged, INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
private readonly ObservableCollection<T> _backing;
|
||||||
|
private IComparer<T> _comparer;
|
||||||
|
private readonly List<T> _store;
|
||||||
|
|
||||||
|
public SystemSortedView(ObservableCollection<T> backing, IComparer<T> comparer)
|
||||||
|
{
|
||||||
|
_comparer = comparer;
|
||||||
|
_backing = backing;
|
||||||
|
_store = new List<T>(_backing.Count);
|
||||||
|
_store.AddRange(_backing);
|
||||||
|
_backing.CollectionChanged += backing_CollectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backing_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
switch (e.Action)
|
||||||
|
{
|
||||||
|
case NotifyCollectionChangedAction.Add:
|
||||||
|
InsertSorted(e.NewItems);
|
||||||
|
CollectionChanged?.Invoke(this, e);
|
||||||
|
break;
|
||||||
|
case NotifyCollectionChangedAction.Remove:
|
||||||
|
_store.RemoveAll(r => e.OldItems.Contains(r));
|
||||||
|
CollectionChanged?.Invoke(this, e);
|
||||||
|
break;
|
||||||
|
case NotifyCollectionChangedAction.Move:
|
||||||
|
case NotifyCollectionChangedAction.Replace:
|
||||||
|
case NotifyCollectionChangedAction.Reset:
|
||||||
|
Refresh();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _store.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => _backing.Count;
|
||||||
|
|
||||||
|
private void InsertSorted(IEnumerable items)
|
||||||
|
{
|
||||||
|
foreach (var t in items)
|
||||||
|
InsertSorted((T)t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int InsertSorted(T item, IComparer<T> comparer = null)
|
||||||
|
{
|
||||||
|
if (comparer == null)
|
||||||
|
comparer = _comparer;
|
||||||
|
|
||||||
|
if (_store.Count == 0 || comparer == null)
|
||||||
|
{
|
||||||
|
_store.Add(item);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (comparer.Compare(_store[_store.Count - 1], item) <= 0)
|
||||||
|
{
|
||||||
|
_store.Add(item);
|
||||||
|
return _store.Count - 1;
|
||||||
|
}
|
||||||
|
if (comparer.Compare(_store[0], item) >= 0)
|
||||||
|
{
|
||||||
|
_store.Insert(0, item);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int index = _store.BinarySearch(item);
|
||||||
|
if (index < 0)
|
||||||
|
index = ~index;
|
||||||
|
_store.Insert(index, item);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Sort(IComparer<T> comparer = null)
|
||||||
|
{
|
||||||
|
if (comparer == null)
|
||||||
|
comparer = _comparer;
|
||||||
|
|
||||||
|
if (comparer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_store.Sort(comparer);
|
||||||
|
|
||||||
|
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
_store.Clear();
|
||||||
|
_store.AddRange(_backing);
|
||||||
|
Sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComparer(IComparer<T> comparer, bool resort = true)
|
||||||
|
{
|
||||||
|
_comparer = comparer;
|
||||||
|
if (resort)
|
||||||
|
Sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
[NotifyPropertyChangedInvocator]
|
||||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -193,12 +193,14 @@
|
|||||||
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
<Compile Include="..\Versioning\AssemblyVersion.cs">
|
||||||
<Link>Properties\AssemblyVersion.cs</Link>
|
<Link>Properties\AssemblyVersion.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Collections\BinaryMinHeap.cs" />
|
||||||
<Compile Include="Collections\MtObservableCollection.cs" />
|
<Compile Include="Collections\MtObservableCollection.cs" />
|
||||||
<Compile Include="Collections\MtObservableCollectionBase.cs" />
|
<Compile Include="Collections\MtObservableCollectionBase.cs" />
|
||||||
<Compile Include="Collections\MtObservableSortedDictionary.cs" />
|
<Compile Include="Collections\MtObservableSortedDictionary.cs" />
|
||||||
<Compile Include="Collections\MtObservableEvent.cs" />
|
<Compile Include="Collections\MtObservableEvent.cs" />
|
||||||
<Compile Include="Collections\MtObservableList.cs" />
|
<Compile Include="Collections\MtObservableList.cs" />
|
||||||
<Compile Include="Collections\SortedView.cs" />
|
<Compile Include="Collections\SortedView.cs" />
|
||||||
|
<Compile Include="Collections\SystemSortedView.cs" />
|
||||||
<Compile Include="Collections\TransformComparer.cs" />
|
<Compile Include="Collections\TransformComparer.cs" />
|
||||||
<Compile Include="Collections\TransformEnumerator.cs" />
|
<Compile Include="Collections\TransformEnumerator.cs" />
|
||||||
<Compile Include="Commands\ConsoleCommandContext.cs" />
|
<Compile Include="Commands\ConsoleCommandContext.cs" />
|
||||||
|
Reference in New Issue
Block a user