Fix start/stop buttons, improve DS init order, add console tab

This commit is contained in:
John Gross
2018-01-21 16:44:10 -08:00
parent 2cb921087f
commit 714824df97
10 changed files with 320 additions and 138 deletions

View File

@@ -9,6 +9,7 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
using NLog; using NLog;
using Torch.Utils; using Torch.Utils;
@@ -84,27 +85,21 @@ quit";
public void Run() public void Run()
{ {
_server = new TorchServer(_config); _server = new TorchServer(_config);
try try
{ {
_server.Init();
var initTask = Task.Run(() => { _server.Init(); });
if (!_config.NoGui) if (!_config.NoGui)
{ {
_server.Init(); _server.Init();
if (!_config.NoGui)
{
var ui = new TorchUI(_server);
if (_config.Autostart) if (_config.Autostart)
_server.Start(); Task.Run(() => _server.Start());
ui.ShowDialog();
} new TorchUI(_server).ShowDialog();
else
_server.Start();
} }
else else
{ {
initTask.Wait();
_server.Start(); _server.Start();
} }
} }
@@ -200,17 +195,21 @@ quit";
{ {
var ex = (Exception)e.ExceptionObject; var ex = (Exception)e.ExceptionObject;
LogException(ex); LogException(ex);
Console.WriteLine("Exiting in 5 seconds.");
Thread.Sleep(5000);
LogManager.Flush(); LogManager.Flush();
if (_config.RestartOnCrash) if (_config.RestartOnCrash)
{ {
Console.WriteLine("Restarting in 5 seconds.");
Thread.Sleep(5000);
var exe = typeof(Program).Assembly.Location; var exe = typeof(Program).Assembly.Location;
_config.WaitForPID = Process.GetCurrentProcess().Id.ToString(); _config.WaitForPID = Process.GetCurrentProcess().Id.ToString();
Process.Start(exe, _config.ToString()); Process.Start(exe, _config.ToString());
} }
else
{
MessageBox.Show("Torch encountered a fatal error and needs to close. Please check the logs for details.");
}
Process.GetCurrentProcess().Kill(); Process.GetCurrentProcess().Kill();
} }
} }
} }

View File

@@ -10,13 +10,18 @@ using Havok;
using NLog; using NLog;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
using Sandbox.Engine.Utils; using Sandbox.Engine.Utils;
using Sandbox.Game;
using Sandbox.Game.Gui;
using Torch.API; using Torch.API;
using Torch.API.Managers; using Torch.API.Managers;
using Torch.Managers; using Torch.Managers;
using Torch.Server.ViewModels; using Torch.Server.ViewModels;
using VRage;
using VRage.FileSystem; using VRage.FileSystem;
using VRage.Game; using VRage.Game;
using VRage.Game.ObjectBuilder;
using VRage.ObjectBuilders; using VRage.ObjectBuilders;
using VRage.Plugins;
namespace Torch.Server.Managers namespace Torch.Server.Managers
{ {
@@ -37,6 +42,8 @@ namespace Torch.Server.Managers
public void LoadInstance(string path, bool validate = true) public void LoadInstance(string path, bool validate = true)
{ {
Log.Info($"Loading instance {path}");
if (validate) if (validate)
ValidateInstance(path); ValidateInstance(path);
@@ -56,6 +63,7 @@ namespace Torch.Server.Managers
config.Load(configPath); config.Load(configPath);
DedicatedConfig = new ConfigDedicatedViewModel(config); DedicatedConfig = new ConfigDedicatedViewModel(config);
var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves")); var worldFolders = Directory.EnumerateDirectories(Path.Combine(Torch.Config.InstancePath, "Saves"));
foreach (var f in worldFolders) foreach (var f in worldFolders)

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Torch.Server
{
public class MultiTextWriter : TextWriter
{
private IEnumerable<TextWriter> writers;
public MultiTextWriter(IEnumerable<TextWriter> writers)
{
this.writers = writers.ToList();
}
public MultiTextWriter(params TextWriter[] writers)
{
this.writers = writers;
}
public override void Write(char value)
{
foreach (var writer in writers)
writer.Write(value);
}
public override void Write(string value)
{
foreach (var writer in writers)
writer.Write(value);
}
public override void Flush()
{
foreach (var writer in writers)
writer.Flush();
}
public override void Close()
{
foreach (var writer in writers)
writer.Close();
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
namespace Torch.Server
{
public class RichTextBoxWriter : TextWriter
{
private RichTextBox textbox;
private StringBuilder _cache = new StringBuilder();
public RichTextBoxWriter(RichTextBox textbox)
{
this.textbox = textbox;
textbox.Document.Background = new SolidColorBrush(UnpackColor(Console.BackgroundColor));
textbox.Document.Blocks.Clear();
textbox.Document.Blocks.Add(new Paragraph {LineHeight = 12});
}
public override void Write(char value)
{
if (value == '\r')
return;
_cache.Append(value);
if (value == '\n')
{
var str = _cache.ToString();
_cache.Clear();
var brush = _brushes[Console.ForegroundColor];
textbox.Dispatcher.BeginInvoke(() =>
{
var p = (Paragraph)textbox.Document.Blocks.FirstBlock;
p.Inlines.Add(new Run(str) { Foreground = brush });
textbox.ScrollToEnd();
});
}
}
public override void Write(string value)
{
var brush = _brushes[Console.ForegroundColor];
textbox.Dispatcher.BeginInvoke(() =>
{
var p = (Paragraph)textbox.Document.Blocks.FirstBlock;
p.Inlines.Add(new Run(value) { Foreground = brush });
textbox.ScrollToEnd();
});
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
static RichTextBoxWriter()
{
foreach (var value in (ConsoleColor[])Enum.GetValues(typeof(ConsoleColor)))
{
_brushes.Add(value, new SolidColorBrush(UnpackColor(value)));
}
}
private static Dictionary<ConsoleColor, SolidColorBrush> _brushes = new Dictionary<ConsoleColor, SolidColorBrush>();
private static Color UnpackColor(ConsoleColor color)
{
var colorByte = (byte)color;
var isBright = (colorByte & 0b1000) >> 3 > 0;
var brightness = isBright ? (byte)255 : (byte)128;
var red = (colorByte & 0b0100) >> 2;
var green = (colorByte & 0b0010) >> 1;
var blue = (colorByte & 0b0001);
return new Color
{
R = (byte)(brightness * red),
G = (byte)(brightness * green),
B = (byte)(brightness * blue),
A = 255
};
}
}
}

View File

@@ -227,6 +227,8 @@
</Compile> </Compile>
<Compile Include="Views\Converters\BooleanAndConverter.cs" /> <Compile Include="Views\Converters\BooleanAndConverter.cs" />
<Compile Include="Views\Converters\ListConverter.cs" /> <Compile Include="Views\Converters\ListConverter.cs" />
<Compile Include="MultiTextWriter.cs" />
<Compile Include="RichTextBoxWriter.cs" />
<Compile Include="Views\ValidationRules\ListConverterValidationRule.cs" /> <Compile Include="Views\ValidationRules\ListConverterValidationRule.cs" />
<Compile Include="Views\Entities\EntityControlHost.xaml.cs"> <Compile Include="Views\Entities\EntityControlHost.xaml.cs">
<DependentUpon>EntityControlHost.xaml</DependentUpon> <DependentUpon>EntityControlHost.xaml</DependentUpon>

View File

@@ -70,6 +70,11 @@ namespace Torch.Server
} }
} }
private bool _canRun;
public bool CanRun { get => _canRun; set => SetValue(ref _canRun, value); }
private bool _hasRun;
/// <inheritdoc /> /// <inheritdoc />
public InstanceManager DedicatedInstance { get; } public InstanceManager DedicatedInstance { get; }
@@ -107,12 +112,13 @@ namespace Torch.Server
/// <inheritdoc /> /// <inheritdoc />
public override void Init() public override void Init()
{ {
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
Sandbox.Engine.Platform.Game.IsDedicated = true; Sandbox.Engine.Platform.Game.IsDedicated = true;
base.Init(); base.Init();
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged; Managers.GetManager<ITorchSessionManager>().SessionStateChanged += OnSessionStateChanged;
GetManager<InstanceManager>().LoadInstance(Config.InstancePath); GetManager<InstanceManager>().LoadInstance(Config.InstancePath);
CanRun = true;
} }
private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState) private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState)
@@ -129,8 +135,17 @@ namespace Torch.Server
{ {
if (State != ServerState.Stopped) if (State != ServerState.Stopped)
return; return;
if (_hasRun)
{
Restart();
return;
}
State = ServerState.Starting; State = ServerState.Starting;
IsRunning = true; IsRunning = true;
CanRun = false;
_hasRun = true;
Log.Info("Starting server."); Log.Info("Starting server.");
MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model; MySandboxGame.ConfigDedicated = DedicatedInstance.DedicatedConfig.Model;
@@ -150,6 +165,7 @@ namespace Torch.Server
State = ServerState.Stopped; State = ServerState.Stopped;
IsRunning = false; IsRunning = false;
CanRun = true;
} }
/// <summary> /// <summary>
@@ -157,12 +173,17 @@ namespace Torch.Server
/// </summary> /// </summary>
public override void Restart() public override void Restart()
{ {
Save().ContinueWith((task, torchO) => if (IsRunning)
Save().ContinueWith(DoRestart, this, TaskContinuationOptions.RunContinuationsAsynchronously);
else
DoRestart(null, this);
void DoRestart(Task<GameSaveResult> task, object torch0)
{ {
var torch = (TorchServer) torchO; var torch = (TorchServer)torch0;
torch.Stop(); torch.Stop();
// TODO clone this // TODO clone this
var config = (TorchConfig) torch.Config; var config = (TorchConfig)torch.Config;
LogManager.Flush(); LogManager.Flush();
string exe = Assembly.GetExecutingAssembly().Location; string exe = Assembly.GetExecutingAssembly().Location;
@@ -172,7 +193,7 @@ namespace Torch.Server
Process.Start(exe, config.ToString()); Process.Start(exe, config.ToString());
Process.GetCurrentProcess().Kill(); Process.GetCurrentProcess().Kill();
}, this, TaskContinuationOptions.RunContinuationsAsynchronously); }
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -28,22 +28,18 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition/> <RowDefinition/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Menu Grid.Row="0">
<MenuItem Header="File"/>
<MenuItem Header="Tools"/>
</Menu>
<StackPanel Grid.Row="1" Margin="5,5,5,5" Orientation="Horizontal"> <StackPanel Grid.Row="1" Margin="5,5,5,5" Orientation="Horizontal">
<Button x:Name="BtnStart" Content="Start" Height="24" Width="75" Margin="5,0,5,0" <Button x:Name="BtnStart" Content="Start" Height="24" Width="75" Margin="5,0,5,0"
HorizontalAlignment="Left" Click="BtnStart_Click"> HorizontalAlignment="Left" Click="BtnStart_Click" Background="LightGreen">
<Button.IsEnabled> <Button.IsEnabled>
<MultiBinding Converter="{StaticResource BooleanAndConverter}"> <MultiBinding Converter="{StaticResource BooleanAndConverter}">
<Binding ElementName="MainWindow" Path="DataContext.IsRunning" Converter="{StaticResource InverseBool}"/> <Binding ElementName="MainWindow" Path="DataContext.CanRun"/>
<Binding ElementName="ConfigControl" Path="ConfigValid"/> <Binding ElementName="ConfigControl" Path="ConfigValid"/>
</MultiBinding> </MultiBinding>
</Button.IsEnabled> </Button.IsEnabled>
</Button> </Button>
<Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" <Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left"
Click="BtnStop_Click" IsEnabled="{Binding IsRunning}" /> Click="BtnStop_Click" IsEnabled="{Binding IsRunning}" Background="IndianRed"/>
<Label> <Label>
<Label.Content> <Label.Content>
<TextBlock Text="{Binding State, StringFormat=Status: {0}}"></TextBlock> <TextBlock Text="{Binding State, StringFormat=Status: {0}}"></TextBlock>
@@ -60,7 +56,10 @@
</Label.Content> </Label.Content>
</Label> </Label>
</StackPanel> </StackPanel>
<TabControl Grid.Row="2" Height="Auto" x:Name="TabControl" Margin="5,0,5,5"> <TabControl Grid.Row="2" Height="Auto" x:Name="TabControl" Margin="5,10,5,5">
<TabItem Header="Console">
<RichTextBox x:Name="ConsoleText" VerticalScrollBarVisibility="Visible" FontFamily="Consolas"/>
</TabItem>
<TabItem Header="Configuration"> <TabItem Header="Configuration">
<Grid IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBool}}"> <Grid IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBool}}">
<Grid.RowDefinitions> <Grid.RowDefinitions>

View File

@@ -1,25 +1,21 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
using Sandbox; using Sandbox;
using Torch.API; using Torch.API;
using Torch.Server.Managers; using Torch.Server.Managers;
using MessageBoxResult = System.Windows.MessageBoxResult;
using Timer = System.Timers.Timer; using Timer = System.Timers.Timer;
namespace Torch.Server namespace Torch.Server
@@ -49,6 +45,13 @@ namespace Torch.Server
PlayerList.BindServer(server); PlayerList.BindServer(server);
Plugins.BindServer(server); Plugins.BindServer(server);
LoadConfig((TorchConfig)server.Config); LoadConfig((TorchConfig)server.Config);
AttachConsole();
}
private void AttachConsole()
{
Console.SetOut(new MultiTextWriter(new RichTextBoxWriter(ConsoleText), Console.Out));
} }
public void LoadConfig(TorchConfig config) public void LoadConfig(TorchConfig config)
@@ -65,12 +68,15 @@ namespace Torch.Server
private void BtnStart_Click(object sender, RoutedEventArgs e) private void BtnStart_Click(object sender, RoutedEventArgs e)
{ {
_server.Start(); Task.Run(() => _server.Start());
} }
private void BtnStop_Click(object sender, RoutedEventArgs e) private void BtnStop_Click(object sender, RoutedEventArgs e)
{ {
_server.Stop(); var result = MessageBox.Show("Are you sure you want to stop the server?", "Stop Server", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
_server.Invoke(() => _server.Stop());
} }
protected override void OnClosing(CancelEventArgs e) protected override void OnClosing(CancelEventArgs e)
@@ -82,6 +88,8 @@ namespace Torch.Server
if (_server?.State == ServerState.Running) if (_server?.State == ServerState.Running)
_server.Stop(); _server.Stop();
Environment.Exit(0);
} }
private void BtnRestart_Click(object sender, RoutedEventArgs e) private void BtnRestart_Click(object sender, RoutedEventArgs e)

View File

@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -26,6 +27,7 @@ using VRage;
using VRage.Audio; using VRage.Audio;
using VRage.FileSystem; using VRage.FileSystem;
using VRage.Game; using VRage.Game;
using VRage.Game.ObjectBuilder;
using VRage.Game.SessionComponents; using VRage.Game.SessionComponents;
using VRage.GameServices; using VRage.GameServices;
using VRage.Network; using VRage.Network;
@@ -200,10 +202,16 @@ namespace Torch
MyRenderProxy.GetRenderProfiler().InitMemoryHack("MainEntryPoint"); MyRenderProxy.GetRenderProfiler().InitMemoryHack("MainEntryPoint");
} }
// var layers = _layerSettings(); // Loads object builder serializers. Intuitive, right?
// layers[layers.Length - 1].Radius *= 4; _log.Info("Setting up serializers");
MyPlugins.RegisterGameAssemblyFile(MyPerGameSettings.GameModAssembly);
_game = new SpaceEngineersGame(_runArgs); if (MyPerGameSettings.GameModBaseObjBuildersAssembly != null)
MyPlugins.RegisterBaseGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModBaseObjBuildersAssembly);
MyPlugins.RegisterGameObjectBuildersAssemblyFile(MyPerGameSettings.GameModObjBuildersAssembly);
MyPlugins.RegisterSandboxAssemblyFile(MyPerGameSettings.SandboxAssembly);
MyPlugins.RegisterSandboxGameAssemblyFile(MyPerGameSettings.SandboxGameAssembly);
//typeof(MySandboxGame).GetMethod("Preallocate", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null);
MyGlobalTypeMetadata.Static.Init(false);
} }
private void Destroy() private void Destroy()
@@ -220,6 +228,8 @@ namespace Torch
private void DoStart() private void DoStart()
{ {
_game = new SpaceEngineersGame(_runArgs);
if (MySandboxGame.FatalErrorDuringInit) if (MySandboxGame.FatalErrorDuringInit)
{ {
_log.Warn("Failed to start sandbox game: fatal error during init"); _log.Warn("Failed to start sandbox game: fatal error during init");

View File

@@ -52,13 +52,12 @@ namespace Torch
/// <summary> /// <summary>
/// Assign a value to the given field and raise PropertyChanged for the caller. /// Assign a value to the given field and raise PropertyChanged for the caller.
/// </summary> /// </summary>
protected virtual void SetValue<T>(ref T backingField, T value, [CallerMemberName] string propName = "") protected virtual void SetValue<T>(ref T field, T value, [CallerMemberName] string propName = "")
{ {
if (backingField != null && backingField.Equals(value)) if (EqualityComparer<T>.Default.Equals(field, value))
return; return;
backingField = value; field = value;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propName); OnPropertyChanged(propName);
} }
@@ -71,7 +70,6 @@ namespace Torch
throw new ArgumentNullException(nameof(setter)); throw new ArgumentNullException(nameof(setter));
setter.Invoke(value); setter.Invoke(value);
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propName); OnPropertyChanged(propName);
} }
@@ -80,9 +78,8 @@ namespace Torch
/// </summary> /// </summary>
public void RefreshModel() public void RefreshModel()
{ {
foreach (var propName in GetType().GetProperties().Select(x => x.Name)) foreach (var property in GetType().GetProperties())
// ReSharper disable once ExplicitCallerInfoArgument OnPropertyChanged(property.Name);
OnPropertyChanged(propName);
} }
} }
} }