Add basic, experimental entity sorting
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
using System.Windows.Controls;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Controls;
|
||||
using NLog;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.World;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Collections;
|
||||
using Torch.Server.Managers;
|
||||
using Torch.Utils;
|
||||
using VRage.Game.Entity;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.ModAPI;
|
||||
@@ -14,6 +20,8 @@ namespace Torch.Server.ViewModels.Entities
|
||||
{
|
||||
protected EntityTreeViewModel Tree { get; }
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private IMyEntity _backing;
|
||||
public IMyEntity Entity
|
||||
{
|
||||
@@ -43,6 +51,75 @@ namespace Torch.Server.ViewModels.Entities
|
||||
}
|
||||
}
|
||||
|
||||
private string _descriptiveName;
|
||||
public string DescriptiveName
|
||||
{
|
||||
get => _descriptiveName ?? (_descriptiveName = GetSortedName(EntityTreeViewModel.SortEnum.Name));
|
||||
set => _descriptiveName = value;
|
||||
}
|
||||
|
||||
public virtual string GetSortedName(EntityTreeViewModel.SortEnum sort)
|
||||
{
|
||||
switch (sort)
|
||||
{
|
||||
case EntityTreeViewModel.SortEnum.Name:
|
||||
return Name;
|
||||
case EntityTreeViewModel.SortEnum.Size:
|
||||
return $"{Name} ({Entity.WorldVolume.Radius * 2:N}m)";
|
||||
case EntityTreeViewModel.SortEnum.Speed:
|
||||
return $"{Name} ({Entity.Physics?.LinearVelocity.Length() ?? 0:N}m/s)";
|
||||
case EntityTreeViewModel.SortEnum.BlockCount:
|
||||
if (Entity is MyCubeGrid grid)
|
||||
return $"{Name} ({grid.BlocksCount} blocks)";
|
||||
return Name;
|
||||
case EntityTreeViewModel.SortEnum.DistFromCenter:
|
||||
return $"{Name} ({Entity.GetPosition().Length():N}m)";
|
||||
case EntityTreeViewModel.SortEnum.Owner:
|
||||
if (Entity is MyCubeGrid g)
|
||||
return $"{Name} ({g.GetGridOwnerName()})";
|
||||
return Name;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(sort), sort, null);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int CompareToSort(EntityViewModel other, EntityTreeViewModel.SortEnum sort)
|
||||
{
|
||||
switch (sort)
|
||||
{
|
||||
case EntityTreeViewModel.SortEnum.Name:
|
||||
return string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase);
|
||||
case EntityTreeViewModel.SortEnum.Size:
|
||||
return Entity.WorldVolume.Radius.CompareTo(other.Entity.WorldVolume.Radius);
|
||||
case EntityTreeViewModel.SortEnum.Speed:
|
||||
if (Entity.Physics == null)
|
||||
{
|
||||
if (other.Entity.Physics == null)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if (other.Entity.Physics == null)
|
||||
return 1;
|
||||
return Entity.Physics.LinearVelocity.LengthSquared().CompareTo(other.Entity.Physics.LinearVelocity.LengthSquared());
|
||||
case EntityTreeViewModel.SortEnum.BlockCount:
|
||||
{
|
||||
if (Entity is MyCubeGrid ga && other.Entity is MyCubeGrid gb)
|
||||
return ga.BlocksCount.CompareTo(gb.BlocksCount);
|
||||
goto case EntityTreeViewModel.SortEnum.Name;
|
||||
}
|
||||
case EntityTreeViewModel.SortEnum.DistFromCenter:
|
||||
return Entity.GetPosition().LengthSquared().CompareTo(other.Entity.GetPosition().LengthSquared());
|
||||
case EntityTreeViewModel.SortEnum.Owner:
|
||||
{
|
||||
if (Entity is MyCubeGrid ga && other.Entity is MyCubeGrid gb)
|
||||
return string.Compare(ga.GetGridOwnerName(), gb.GetGridOwnerName(), StringComparison.InvariantCultureIgnoreCase);
|
||||
goto case EntityTreeViewModel.SortEnum.Name;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(sort), sort, null);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Position
|
||||
{
|
||||
get => Entity?.GetPosition().ToString();
|
||||
@@ -76,5 +153,20 @@ namespace Torch.Server.ViewModels.Entities
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class Comparer : IComparer<EntityViewModel>
|
||||
{
|
||||
private EntityTreeViewModel.SortEnum _sort;
|
||||
|
||||
public Comparer(EntityTreeViewModel.SortEnum sort)
|
||||
{
|
||||
_sort = sort;
|
||||
}
|
||||
|
||||
public int Compare(EntityViewModel x, EntityViewModel y)
|
||||
{
|
||||
return x.CompareToSort(y, _sort);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -50,17 +50,14 @@ namespace Torch.Server.ViewModels.Entities
|
||||
Blocks { get; } =
|
||||
new MtObservableSortedDictionary<MyCubeBlockDefinition, MtObservableSortedDictionary<long, BlockViewModel>>(
|
||||
CubeBlockDefinitionComparer.Default);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DescriptiveName { get; }
|
||||
|
||||
|
||||
public GridViewModel()
|
||||
{
|
||||
}
|
||||
|
||||
public GridViewModel(MyCubeGrid grid, EntityTreeViewModel tree) : base(grid, tree)
|
||||
{
|
||||
DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
|
||||
//DescriptiveName = $"{grid.DisplayName} ({grid.BlocksCount} blocks)";
|
||||
Blocks.Add(_fillerDefinition, new MtObservableSortedDictionary<long, BlockViewModel>());
|
||||
}
|
||||
|
||||
|
@@ -12,11 +12,21 @@ using VRage.ModAPI;
|
||||
using System.Windows.Threading;
|
||||
using NLog;
|
||||
using Torch.Collections;
|
||||
using Torch.Server.Views.Entities;
|
||||
|
||||
namespace Torch.Server.ViewModels
|
||||
{
|
||||
public class EntityTreeViewModel : ViewModel
|
||||
{
|
||||
public enum SortEnum
|
||||
{
|
||||
Name,
|
||||
Size,
|
||||
Speed,
|
||||
Owner,
|
||||
BlockCount,
|
||||
DistFromCenter,
|
||||
}
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
//TODO: these should be sorted sets for speed
|
||||
@@ -26,7 +36,13 @@ namespace Torch.Server.ViewModels
|
||||
public MtObservableSortedDictionary<long, VoxelMapViewModel> VoxelMaps { get; set; } = new MtObservableSortedDictionary<long, VoxelMapViewModel>();
|
||||
public Dispatcher ControlDispatcher => _control.Dispatcher;
|
||||
|
||||
public SortedView<GridViewModel> SortedGrids { get; }
|
||||
public SortedView<CharacterViewModel> SortedCharacters { get; }
|
||||
public SortedView<EntityViewModel> SortedFloatingObjects { get; }
|
||||
public SortedView<VoxelMapViewModel> SortedVoxelMaps { get; }
|
||||
|
||||
private EntityViewModel _currentEntity;
|
||||
private SortEnum _currentSort;
|
||||
private UserControl _control;
|
||||
|
||||
public EntityViewModel CurrentEntity
|
||||
@@ -35,6 +51,12 @@ namespace Torch.Server.ViewModels
|
||||
set { _currentEntity = value; OnPropertyChanged(nameof(CurrentEntity)); }
|
||||
}
|
||||
|
||||
public SortEnum CurrentSort
|
||||
{
|
||||
get => _currentSort;
|
||||
set => SetValue(ref _currentSort, value);
|
||||
}
|
||||
|
||||
// I hate you today WPF
|
||||
public EntityTreeViewModel() : this(null)
|
||||
{
|
||||
@@ -43,6 +65,11 @@ namespace Torch.Server.ViewModels
|
||||
public EntityTreeViewModel(UserControl control)
|
||||
{
|
||||
_control = control;
|
||||
var comparer = new EntityViewModel.Comparer(_currentSort);
|
||||
SortedGrids = new SortedView<GridViewModel>(Grids.Values, comparer);
|
||||
SortedCharacters = new SortedView<CharacterViewModel>(Characters.Values, comparer);
|
||||
SortedFloatingObjects = new SortedView<EntityViewModel>(FloatingObjects.Values, comparer);
|
||||
SortedVoxelMaps = new SortedView<VoxelMapViewModel>(VoxelMaps.Values, comparer);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
@@ -85,16 +112,16 @@ namespace Torch.Server.ViewModels
|
||||
switch (obj)
|
||||
{
|
||||
case MyCubeGrid grid:
|
||||
Grids.Add(obj.EntityId, new GridViewModel(grid, this));
|
||||
Grids.Add(grid.EntityId, new GridViewModel(grid, this));
|
||||
break;
|
||||
case MyCharacter character:
|
||||
Characters.Add(obj.EntityId, new CharacterViewModel(character, this));
|
||||
Characters.Add(character.EntityId, new CharacterViewModel(character, this));
|
||||
break;
|
||||
case MyFloatingObject floating:
|
||||
FloatingObjects.Add(obj.EntityId, new FloatingObjectViewModel(floating, this));
|
||||
FloatingObjects.Add(floating.EntityId, new FloatingObjectViewModel(floating, this));
|
||||
break;
|
||||
case MyVoxelBase voxel:
|
||||
VoxelMaps.Add(obj.EntityId, new VoxelMapViewModel(voxel, this));
|
||||
VoxelMaps.Add(voxel.EntityId, new VoxelMapViewModel(voxel, this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -22,10 +22,11 @@
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TreeView Grid.Row="0" Margin="3" DockPanel.Dock="Top" SelectedItemChanged="TreeView_OnSelectedItemChanged"
|
||||
TreeViewItem.Expanded="TreeViewItem_OnExpanded">
|
||||
TreeViewItem.Expanded="TreeViewItem_OnExpanded" Name="EntityTree">
|
||||
<TreeView.Resources>
|
||||
<HierarchicalDataTemplate DataType="{x:Type entities:GridViewModel}"
|
||||
ItemsSource="{Binding Path=Blocks}">
|
||||
@@ -46,46 +47,47 @@
|
||||
</HierarchicalDataTemplate>
|
||||
<HierarchicalDataTemplate DataType="{x:Type entities:VoxelMapViewModel}"
|
||||
ItemsSource="{Binding AttachedGrids}">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
<TextBlock Text="{Binding DescriptiveName}" />
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.Resources>
|
||||
<TreeViewItem ItemsSource="{Binding Path=Grids.Values}">
|
||||
<TreeViewItem ItemsSource="{Binding Path=SortedGrids}">
|
||||
<TreeViewItem.Header>
|
||||
<TextBlock Text="{Binding Grids.Count, StringFormat=Grids ({0})}" />
|
||||
<TextBlock Text="{Binding SortedGrids.Count, StringFormat=Grids ({0})}" />
|
||||
</TreeViewItem.Header>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem ItemsSource="{Binding Characters.Values}">
|
||||
<TreeViewItem ItemsSource="{Binding SortedCharacters}">
|
||||
<TreeViewItem.Header>
|
||||
<TextBlock Text="{Binding Characters.Count, StringFormat=Characters ({0})}" />
|
||||
<TextBlock Text="{Binding SortedCharacters.Count, StringFormat=Characters ({0})}" />
|
||||
</TreeViewItem.Header>
|
||||
<TreeViewItem.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
<TextBlock Text="{Binding DescriptiveName}" />
|
||||
</DataTemplate>
|
||||
</TreeViewItem.ItemTemplate>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem ItemsSource="{Binding VoxelMaps.Values}">
|
||||
<TreeViewItem ItemsSource="{Binding SortedVoxelMaps}">
|
||||
<TreeViewItem.Header>
|
||||
<TextBlock Text="{Binding VoxelMaps.Count, StringFormat=Voxel Maps ({0})}" />
|
||||
<TextBlock Text="{Binding SortedVoxelMaps.Count, StringFormat=Voxel Maps ({0})}" />
|
||||
</TreeViewItem.Header>
|
||||
<TreeViewItem.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
<TextBlock Text="{Binding DescriptiveName}" />
|
||||
</DataTemplate>
|
||||
</TreeViewItem.ItemTemplate>
|
||||
</TreeViewItem>
|
||||
<TreeViewItem ItemsSource="{Binding FloatingObjects.Values}">
|
||||
<TreeViewItem ItemsSource="{Binding SortedFloatingObjects}">
|
||||
<TreeViewItem.Header>
|
||||
<TextBlock Text="{Binding FloatingObjects.Count, StringFormat=Floating Objects ({0})}" />
|
||||
<TextBlock Text="{Binding SortedFloatingObjects.Count, StringFormat=Floating Objects ({0})}" />
|
||||
</TreeViewItem.Header>
|
||||
<TreeViewItem.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
<TextBlock Text="{Binding DescriptiveName}" />
|
||||
</DataTemplate>
|
||||
</TreeViewItem.ItemTemplate>
|
||||
</TreeViewItem>
|
||||
</TreeView>
|
||||
<StackPanel Grid.Row="1" DockPanel.Dock="Bottom">
|
||||
<ComboBox Grid.Row="1" Margin="3" Name="SortCombo" SelectionChanged="SortCombo_SelectionChanged"/>
|
||||
<StackPanel Grid.Row="2" DockPanel.Dock="Bottom">
|
||||
<Button Content="Delete" Click="Delete_OnClick" IsEnabled="{Binding CurrentEntity.CanDelete}"
|
||||
Margin="3" />
|
||||
<Button Content="Stop" Click="Stop_OnClick" IsEnabled="{Binding CurrentEntity.CanStop}" Margin="3" />
|
||||
|
@@ -35,6 +35,7 @@ namespace Torch.Server.Views
|
||||
Entities = new EntityTreeViewModel(this);
|
||||
DataContext = Entities;
|
||||
Entities.Init();
|
||||
SortCombo.ItemsSource = Enum.GetNames(typeof(EntityTreeViewModel.SortEnum));
|
||||
}
|
||||
|
||||
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
@@ -77,5 +78,30 @@ namespace Torch.Server.Views
|
||||
if (item.DataContext is ILazyLoad l)
|
||||
l.Load();
|
||||
}
|
||||
|
||||
private void SortCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
var sort = (EntityTreeViewModel.SortEnum)SortCombo.SelectedIndex;
|
||||
|
||||
var comparer = new EntityViewModel.Comparer(sort);
|
||||
|
||||
Task[] sortTasks = new Task[4];
|
||||
|
||||
Entities.CurrentSort = sort;
|
||||
Entities.SortedCharacters.Sort(comparer);
|
||||
Entities.SortedFloatingObjects.Sort(comparer);
|
||||
Entities.SortedGrids.Sort(comparer);
|
||||
Entities.SortedVoxelMaps.Sort(comparer);
|
||||
|
||||
foreach (var i in Entities.SortedCharacters)
|
||||
i.DescriptiveName = i.GetSortedName(sort);
|
||||
foreach (var i in Entities.SortedFloatingObjects)
|
||||
i.DescriptiveName = i.GetSortedName(sort);
|
||||
foreach (var i in Entities.SortedGrids)
|
||||
i.DescriptiveName = i.GetSortedName(sort);
|
||||
foreach (var i in Entities.SortedVoxelMaps)
|
||||
i.DescriptiveName = i.GetSortedName(sort);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ namespace Torch.Collections
|
||||
/// Multithread safe, observable list
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Value type</typeparam>
|
||||
public class MtObservableList<T> : MtObservableCollection<IList<T>, T>, IList<T>, IList
|
||||
public class MtObservableList<T> : MtObservableCollection<List<T>, T>, IList<T>, IList
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MtObservableList class that is empty and has the default initial capacity.
|
||||
@@ -114,16 +114,34 @@ namespace Torch.Collections
|
||||
using (Lock.WriteUsing())
|
||||
{
|
||||
comparer = comparer ?? Comparer<TKey>.Default;
|
||||
if (Backing is List<T> lst)
|
||||
lst.Sort(new TransformComparer<T, TKey>(selector, comparer));
|
||||
else
|
||||
{
|
||||
List<T> sortedItems = Backing.OrderBy(selector, comparer).ToList();
|
||||
Backing.Clear();
|
||||
foreach (T v in sortedItems)
|
||||
Backing.Add(v);
|
||||
}
|
||||
Backing.Sort(new TransformComparer<T, TKey>(selector, comparer));
|
||||
}
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the list using the given comparer./>
|
||||
/// </summary>
|
||||
public void Sort(IComparer<T> comparer)
|
||||
{
|
||||
using (DeferredUpdate())
|
||||
using (Lock.WriteUsing())
|
||||
{
|
||||
Backing.Sort(comparer);
|
||||
}
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the entire list for an element using the specified comparer and returns the zero-based index of the element.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public int BinarySearch(T item, IComparer<T> comparer = null)
|
||||
{
|
||||
using(Lock.ReadUsing())
|
||||
return Backing.BinarySearch(item, comparer ?? Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
122
Torch/Collections/SortedView.cs
Normal file
122
Torch/Collections/SortedView.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Torch.Collections
|
||||
{
|
||||
public class SortedView<T>: IReadOnlyCollection<T>, INotifyCollectionChanged
|
||||
{
|
||||
private readonly MtObservableCollectionBase<T> _backing;
|
||||
private IComparer<T> _comparer;
|
||||
private readonly List<T> _store;
|
||||
|
||||
public SortedView(MtObservableCollectionBase<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, System.Collections.Specialized.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 _backing.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;
|
||||
}
|
||||
}
|
@@ -34,6 +34,8 @@
|
||||
<DocumentationFile>$(SolutionDir)\bin\x64\Release\Torch.xml</DocumentationFile>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<!-- I don't know why this needs to exist -->
|
||||
<ItemGroup> <Reference Include="netstandard" /> </ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ControlzEx, Version=3.0.2.4, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ControlzEx.3.0.2.4\lib\net45\ControlzEx.dll</HintPath>
|
||||
@@ -182,6 +184,7 @@
|
||||
<Compile Include="Collections\MtObservableSortedDictionary.cs" />
|
||||
<Compile Include="Collections\MtObservableEvent.cs" />
|
||||
<Compile Include="Collections\MtObservableList.cs" />
|
||||
<Compile Include="Collections\SortedView.cs" />
|
||||
<Compile Include="Collections\TransformComparer.cs" />
|
||||
<Compile Include="Collections\TransformEnumerator.cs" />
|
||||
<Compile Include="Event\EventShimAttribute.cs" />
|
||||
|
@@ -6,7 +6,12 @@ using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.World;
|
||||
using Steamworks;
|
||||
using VRage.Game.ModAPI;
|
||||
|
||||
namespace Torch.Utils
|
||||
{
|
||||
@@ -57,5 +62,12 @@ namespace Torch.Utils
|
||||
// What is endianness anyway?
|
||||
return new IPAddress(BitConverter.GetBytes(state.m_nRemoteIP).Reverse().ToArray());
|
||||
}
|
||||
|
||||
public static string GetGridOwnerName(this MyCubeGrid grid)
|
||||
{
|
||||
if (grid.BigOwners.Count == 0 || grid.BigOwners[0] == 0)
|
||||
return "nobody";
|
||||
return MyMultiplayer.Static.GetMemberName(MySession.Static.Players.TryGetSteamId(grid.BigOwners[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user