Add entity management viewmodels

This commit is contained in:
John Gross
2017-05-11 11:22:47 -07:00
parent 135d1f4be8
commit d4649ea8ef
21 changed files with 360 additions and 47 deletions

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Torch.Server
{
public class CommandLine
{
public TorchConfig Config { get; }
private string _argPrefix;
[Arg("instancepath", "Server data folder where saves and mods are stored")]
public string InstancePath { get => Config.InstancePath; set => Config.InstancePath = value; }
public CommandLine(TorchConfig config, string argPrefix)
{
Config = config;
_argPrefix = argPrefix;
}
public PropertyInfo[] GetArgs()
{
return typeof(CommandLine).GetProperties().Where(p => p.HasAttribute<ArgAttribute>()).ToArray();
}
public string GetHelp()
{
var sb = new StringBuilder();
foreach (var property in GetArgs())
{
var attr = property.GetCustomAttribute<ArgAttribute>();
sb.AppendLine($"{_argPrefix}{attr.Name.PadRight(20)}{attr.Description}");
}
return sb.ToString();
}
public void Run(string[] args)
{
if (args[0] == $"{_argPrefix}help")
{
Console.WriteLine(GetHelp());
return;
}
var properties = GetArgs();
for (var i = 0; i < args.Length; i++)
{
if (!args[i].StartsWith(_argPrefix))
continue;
foreach (var property in properties)
{
var argName = property.GetCustomAttribute<ArgAttribute>()?.Name;
if (argName == null)
continue;
try
{
if (string.Compare(argName, 0, args[i], 1, argName.Length, StringComparison.InvariantCultureIgnoreCase) == 0)
{
if (property.PropertyType == typeof(bool))
property.SetValue(this, true);
if (property.PropertyType == typeof(string))
property.SetValue(this, args[++i]);
}
}
catch (Exception e)
{
Console.WriteLine($"Error parsing arg {argName}");
}
}
}
}
private class ArgAttribute : Attribute
{
public string Name { get; }
public string Description { get; }
public ArgAttribute(string name, string description)
{
Name = name;
Description = description;
}
}
}
}

View File

@@ -160,6 +160,7 @@
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<Compile Include="CommandLine.cs" />
<Compile Include="NativeMethods.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<AutoGen>True</AutoGen>
@@ -178,19 +179,18 @@
<Compile Include="ViewModels\ConfigDedicatedViewModel.cs" />
<Compile Include="ViewModels\EntityTreeViewModel.cs" />
<Compile Include="ViewModels\EntityViewModel.cs" />
<Compile Include="ViewModels\FloatingObjectViewModel.cs" />
<Compile Include="ViewModels\GridViewModel.cs" />
<Compile Include="ViewModels\PluginManagerViewModel.cs" />
<Compile Include="ViewModels\PluginViewModel.cs" />
<Compile Include="ViewModels\SessionSettingsViewModel.cs" />
<Compile Include="ViewModels\VoxelMapViewModel.cs" />
<Compile Include="Views\AddWorkshopItemsDialog.xaml.cs">
<DependentUpon>AddWorkshopItemsDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ChatControl.xaml.cs">
<DependentUpon>ChatControl.xaml</DependentUpon>
</Compile>
<Compile Include="Views\CollectionEditor.xaml.cs">
<DependentUpon>CollectionEditor.xaml</DependentUpon>
</Compile>
<Compile Include="Views\ConfigControl.xaml.cs">
<DependentUpon>ConfigControl.xaml</DependentUpon>
</Compile>
@@ -255,10 +255,6 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Views\CollectionEditor.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\ConfigControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@@ -67,7 +67,7 @@ namespace Torch.Server
/// </summary>
public override void Start()
{
if (State > 0)
if (State != ServerState.Stopped)
throw new InvalidOperationException("Server is already running.");
Config.Save();
@@ -115,14 +115,12 @@ namespace Torch.Server
Log.Info("Stopping server.");
MySession.Static.Save();
MySession.Static.Unload();
MySandboxGame.Static.Exit();
//Unload all the static junk.
//TODO: Finish unloading all server data so it's in a completely clean state.
MyFileSystem.Reset();
VRage.Input.MyGuiGameControlsHelpers.Reset();
VRage.Input.MyInput.UnloadData();
//CleanupProfilers();
Log.Info("Server stopped.");
_stopHandle.Set();

View File

@@ -3,11 +3,16 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Game.Entities.Character;
using VRage.Game.ModAPI;
namespace Torch.Server.ViewModels
{
/*
public class CharacterViewModel : EntityViewModel
{
}*/
public CharacterViewModel(MyCharacter character) : base(character)
{
}
}
}

View File

@@ -3,18 +3,71 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Game.Entities;
using Sandbox.Game.Entities.Character;
using VRage.Game.ModAPI;
using VRage.ModAPI;
namespace Torch.Server.ViewModels
{
/*
public class EntityTreeViewModel : ViewModel
{
public string GridsHeader => null;
public MTObservableCollection<>
public MTObservableCollection<GridViewModel> Grids { get; set; } = new MTObservableCollection<GridViewModel>();
public MTObservableCollection<CharacterViewModel> Characters { get; set; } = new MTObservableCollection<CharacterViewModel>();
public MTObservableCollection<EntityViewModel> FloatingObjects { get; set; } = new MTObservableCollection<EntityViewModel>();
public MTObservableCollection<VoxelMapViewModel> VoxelMaps { get; set; } = new MTObservableCollection<VoxelMapViewModel>();
public void Refresh()
private EntityViewModel _currentEntity;
public EntityViewModel CurrentEntity
{
get => _currentEntity;
set { _currentEntity = value; OnPropertyChanged(); }
}
}*/
public EntityTreeViewModel()
{
MyEntities.OnEntityAdd += MyEntities_OnEntityAdd;
MyEntities.OnEntityRemove += MyEntities_OnEntityRemove;
}
private void MyEntities_OnEntityRemove(VRage.Game.Entity.MyEntity obj)
{
switch (obj)
{
case MyCubeGrid grid:
Grids.RemoveWhere(m => m.Id == grid.EntityId);
break;
case MyCharacter character:
Characters.RemoveWhere(m => m.Id == character.EntityId);
break;
case MyFloatingObject floating:
FloatingObjects.RemoveWhere(m => m.Id == floating.EntityId);
break;
case MyVoxelBase voxel:
VoxelMaps.RemoveWhere(m => m.Id == voxel.EntityId);
break;
}
}
private void MyEntities_OnEntityAdd(VRage.Game.Entity.MyEntity obj)
{
//TODO: make view models
switch (obj)
{
case MyCubeGrid grid:
Grids.Add(new GridViewModel(grid));
break;
case MyCharacter character:
Characters.Add(new CharacterViewModel(character));
break;
case MyFloatingObject floating:
FloatingObjects.Add(new FloatingObjectViewModel(floating));
break;
case MyVoxelBase voxel:
VoxelMaps.Add(new VoxelMapViewModel(voxel));
break;
}
}
}
}

View File

@@ -3,29 +3,47 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VRage.Game.ModAPI;
using VRage.ModAPI;
using VRageMath;
namespace Torch.Server.ViewModels
{
/*
public class EntityViewModel : ViewModel
{
public IMyEntity Entity { get; }
public long Id => Entity.EntityId;
public string Name
public virtual string Name
{
get { return Entity.DisplayName; }
set { TorchBase.Instance.i}
get => Entity.DisplayName;
set
{
TorchBase.Instance.InvokeBlocking(() => Entity.DisplayName = value);
OnPropertyChanged();
}
}
public string Position { get; }
public virtual string Position
{
get => Entity.GetPosition().ToString();
set
{
if (!Vector3D.TryParse(value, out Vector3D v))
return;
TorchBase.Instance.InvokeBlocking(() => Entity.SetPosition(v));
OnPropertyChanged();
}
}
public virtual bool CanStop => Entity.Physics?.Enabled ?? false;
public virtual bool CanDelete => !(Entity is IMyCharacter);
public EntityViewModel(IMyEntity entity)
{
Entity = entity;
Name = entity.DisplayName;
Position
}
}*/
}
}

View File

@@ -0,0 +1,13 @@
using Sandbox.Game.Entities;
namespace Torch.Server.ViewModels
{
public class FloatingObjectViewModel : EntityViewModel
{
private MyFloatingObject Floating => (MyFloatingObject)Entity;
public override string Name => $"{base.Name} ({Floating.Amount})";
public FloatingObjectViewModel(MyFloatingObject floating) : base(floating) { }
}
}

View File

@@ -3,11 +3,21 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Game.Entities;
using VRage.Game.ModAPI;
namespace Torch.Server.ViewModels
{
/*
public class GridViewModel : EntityViewModel
{
}*/
private MyCubeGrid Grid => (MyCubeGrid)Entity;
/// <inheritdoc />
public override string Name => $"{base.Name} ({Grid.BlocksCount} blocks)";
public GridViewModel(MyCubeGrid grid) : base(grid)
{
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Medieval.ObjectBuilders.Definitions;
using Sandbox.Game.Entities;
using VRage.ModAPI;
namespace Torch.Server.ViewModels
{
public class VoxelMapViewModel : EntityViewModel
{
private MyVoxelBase Voxel => (MyVoxelBase)Entity;
public override string Name => Voxel.StorageName;
public override bool CanStop => false;
public VoxelMapViewModel(MyVoxelBase e) : base(e) { }
}
}

View File

@@ -16,6 +16,7 @@
<ComboBox Text="{Binding LoadWorld}" ItemsSource="{Binding WorldPaths}" IsEditable="True" Margin="3" />
</DockPanel>
<DockPanel DockPanel.Dock="Bottom">
<StackPanel>
<ScrollViewer IsEnabled="True">
<StackPanel Orientation="Horizontal">
<StackPanel Margin="3" DockPanel.Dock="Left">
@@ -47,6 +48,8 @@
</StackPanel>
</StackPanel>
</ScrollViewer>
<Button Content="Save Config" Margin="3" Click="Save_OnClick"></Button>
</StackPanel>
<ScrollViewer Margin="3" DockPanel.Dock="Right" IsEnabled="True">
<StackPanel DataContext="{Binding SessionSettings}">
<Expander Header="Multipliers">

View File

@@ -20,6 +20,7 @@ using Sandbox;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Utils;
using Torch.Server.ViewModels;
using Torch.Views;
using VRage.Dedicated;
using VRage.Game;
using Path = System.IO.Path;
@@ -43,18 +44,14 @@ namespace Torch.Server.Views
public void SaveConfig()
{
Config.Save(_configPath);
//TODO: make this work
try
{
var checkpoint = MyLocalCache.LoadCheckpoint(_viewModel.LoadWorld, out ulong size);
checkpoint.SessionName = _viewModel.WorldName;
checkpoint.Settings = _viewModel.SessionSettings;
checkpoint.Mods.Clear();
foreach (var modId in _viewModel.Mods)
checkpoint.Mods.Add(new MyObjectBuilder_Checkpoint.ModItem(modId));
Debug.Assert(checkpoint != null);
Debug.Assert(_viewModel.LoadWorld != null);
MyLocalCache.SaveCheckpoint(checkpoint, _viewModel.LoadWorld);
}
catch (Exception e)
@@ -106,5 +103,10 @@ namespace Torch.Server.Views
var editor = new CollectionEditor { Owner = Window.GetWindow(this) };
editor.Edit(_viewModel.Mods, "Mods");
}
private void Save_OnClick(object sender, RoutedEventArgs e)
{
SaveConfig();
}
}
}

View File

@@ -4,19 +4,61 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Torch.Server.Views"
xmlns:viewModels="clr-namespace:Torch.Server.ViewModels"
mc:Ignorable="d">
<UserControl.DataContext>
<viewModels:EntityTreeViewModel />
</UserControl.DataContext>
<DockPanel>
<DockPanel DockPanel.Dock="Left">
<StackPanel DockPanel.Dock="Bottom">
<Button Content="Refresh" Margin="3"></Button>
<Button Content="Delete Selected" Margin="3"></Button>
<Button Content="Delete" Click="Delete_OnClick" IsEnabled="{Binding CurrentEntity.CanDelete}"
Margin="3" />
<Button Content="Stop" Click="Stop_OnClick" IsEnabled="{Binding CurrentEntity.CanStop}" Margin="3" />
</StackPanel>
<TreeView Width="300" Margin="3" DockPanel.Dock="Top">
<TreeViewItem Header="Grids"/>
<TreeViewItem Header="Characters"/>
<TreeViewItem Header="Floating Objects"/>
<TreeView Width="300" Margin="3" DockPanel.Dock="Top" SelectedItemChanged="TreeView_OnSelectedItemChanged">
<TreeViewItem ItemsSource="{Binding Grids}" IsExpanded="true">
<TreeViewItem.Header>
<TextBlock Text="{Binding Grids.Count, StringFormat=Grids ({0})}" />
</TreeViewItem.Header>
<TreeViewItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
<TreeViewItem ItemsSource="{Binding Characters}" IsExpanded="true">
<TreeViewItem.Header>
<TextBlock Text="{Binding Characters.Count, StringFormat=Characters ({0})}" />
</TreeViewItem.Header>
<TreeViewItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
<TreeViewItem ItemsSource="{Binding VoxelMaps}" IsExpanded="true">
<TreeViewItem.Header>
<TextBlock Text="{Binding VoxelMaps.Count, StringFormat=Voxel Maps ({0})}" />
</TreeViewItem.Header>
<TreeViewItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
<TreeViewItem ItemsSource="{Binding FloatingObjects}" IsExpanded="true">
<TreeViewItem.Header>
<TextBlock Text="{Binding FloatingObjects.Count, StringFormat=Floating Objects ({0})}" />
</TreeViewItem.Header>
<TreeViewItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</TreeView>
</DockPanel>
<Frame Margin="3"></Frame>
<Frame Margin="3" />
</DockPanel>
</UserControl>

View File

@@ -12,6 +12,8 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Torch.Server.ViewModels;
using VRage.Game.ModAPI;
namespace Torch.Server.Views
{
@@ -20,9 +22,32 @@ namespace Torch.Server.Views
/// </summary>
public partial class EntitiesControl : UserControl
{
public EntityTreeViewModel Entities { get; set; } = new EntityTreeViewModel();
public EntitiesControl()
{
InitializeComponent();
DataContext = Entities;
}
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue is EntityViewModel vm)
Entities.CurrentEntity = vm;
else
Entities.CurrentEntity = null;
}
private void Delete_OnClick(object sender, RoutedEventArgs e)
{
if (Entities.CurrentEntity?.Entity is IMyCharacter)
return;
TorchBase.Instance.Invoke(() => Entities.CurrentEntity?.Entity.Close());
}
private void Stop_OnClick(object sender, RoutedEventArgs e)
{
TorchBase.Instance.Invoke(() => Entities.CurrentEntity?.Entity.Physics?.ClearSpeed());
}
}
}

View File

@@ -6,7 +6,7 @@
xmlns:local="clr-namespace:Torch.Server"
xmlns:views="clr-namespace:Torch.Server.Views"
mc:Ignorable="d"
Title="Torch" Height="800" Width="800">
Title="Torch" Height="560" Width="700">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Margin="5,5,5,5" Orientation="Horizontal">
<Button x:Name="BtnStart" Content="Start" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnStart_Click" IsDefault="True"/>

View File

@@ -17,6 +17,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Sandbox;
using Torch.API;
using Timer = System.Timers.Timer;
@@ -106,7 +107,7 @@ namespace Torch.Server
private void BtnRestart_Click(object sender, RoutedEventArgs e)
{
//MySandboxGame.Static.Invoke(MySandboxGame.ReloadDedicatedServerSession); use i
}
private void InstancePathBox_OnTextChanged(object sender, TextChangedEventArgs e)

View File

@@ -32,5 +32,14 @@ namespace Torch
nh.Invoke(this, e);
}
}
public void RemoveWhere(Func<T, bool> condition)
{
for (var i = Items.Count - 1; i > 0; i--)
{
if (condition(Items[i]))
Items.RemoveAt(i);
}
}
}
}

View File

@@ -43,6 +43,8 @@
<Reference Include="Octokit, Version=0.24.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Octokit.0.24.0\lib\net45\Octokit.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="Sandbox.Common">
<HintPath>..\GameBinaries\Sandbox.Common.dll</HintPath>
</Reference>
@@ -66,6 +68,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -149,6 +152,9 @@
<Compile Include="Managers\PluginManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModels\PluginViewModel.cs" />
<Compile Include="Views\CollectionEditor.xaml.cs">
<DependentUpon>CollectionEditor.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Torch.API\Torch.API.csproj">
@@ -160,6 +166,12 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Page Include="Views\CollectionEditor.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -21,6 +21,7 @@ namespace Torch
//public bool LogChat { get; set; }
public bool EnableAutomaticUpdates { get; set; } = true;
public bool RedownloadPlugins { get; set; }
public List<string> Plugins { get; set; } = new List<string>();
[NonSerialized]
private string _path;

View File

@@ -19,5 +19,15 @@ namespace Torch
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
/// <summary>
/// Fires PropertyChanged for all properties.
/// </summary>
public void RefreshModel()
{
foreach (var propName in GetType().GetProperties().Select(x => x.Name))
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propName);
}
}
}

View File

@@ -1,9 +1,8 @@
<Window x:Class="Torch.Server.Views.CollectionEditor"
<Window x:Class="Torch.Views.CollectionEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Torch.Server.Views"
mc:Ignorable="d"
Title="Edit Collection" Height="300" Width="300">
<DockPanel>

View File

@@ -14,7 +14,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Torch.Server.Views
namespace Torch.Views
{
/// <summary>
/// Interaction logic for CollectionEditor.xaml