diff --git a/NLog.config b/NLog.config
index deab41a..ec2aee3 100644
--- a/NLog.config
+++ b/NLog.config
@@ -4,14 +4,17 @@
-
+
+
-
-
+
+
-
+
diff --git a/Torch.API/ITorchConfig.cs b/Torch.API/ITorchConfig.cs
index 73a24bf..2156dfd 100644
--- a/Torch.API/ITorchConfig.cs
+++ b/Torch.API/ITorchConfig.cs
@@ -29,7 +29,7 @@ namespace Torch
int WindowHeight { get; set; }
int FontSize { get; set; }
UGCServiceType UgcServiceType { get; set; }
-
+ bool EntityManagerEnabled { get; set; }
void Save(string path = null);
}
}
\ No newline at end of file
diff --git a/Torch.API/Session/ITorchSession.cs b/Torch.API/Session/ITorchSession.cs
index 59add06..0648fcb 100644
--- a/Torch.API/Session/ITorchSession.cs
+++ b/Torch.API/Session/ITorchSession.cs
@@ -22,6 +22,11 @@ namespace Torch.API.Session
/// The Space Engineers game session this session is bound to.
///
MySession KeenSession { get; }
+
+ ///
+ /// Currently running world
+ ///
+ IWorld World { get; }
///
IDependencyManager Managers { get; }
diff --git a/Torch.API/Torch.API.csproj b/Torch.API/Torch.API.csproj
index f5f2440..f29f500 100644
--- a/Torch.API/Torch.API.csproj
+++ b/Torch.API/Torch.API.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/Torch.Server.Tests/Torch.Server.Tests.csproj b/Torch.Server.Tests/Torch.Server.Tests.csproj
index eb8f250..b31bcb2 100644
--- a/Torch.Server.Tests/Torch.Server.Tests.csproj
+++ b/Torch.Server.Tests/Torch.Server.Tests.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/Torch.Server/FlowDocumentTarget.cs b/Torch.Server/FlowDocumentTarget.cs
deleted file mode 100644
index 26a1fba..0000000
--- a/Torch.Server/FlowDocumentTarget.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Documents;
-using System.Windows.Media;
-using NLog;
-using NLog.Targets;
-
-namespace Torch.Server
-{
- ///
- /// NLog target that writes to a .
- ///
- [Target("flowDocument")]
- public sealed class FlowDocumentTarget : TargetWithLayout
- {
- private FlowDocument _document = new FlowDocument { Background = new SolidColorBrush(Colors.Black) };
- private readonly Paragraph _paragraph = new Paragraph();
- private readonly int _maxLines = 500;
-
- public FlowDocument Document => _document;
-
- public FlowDocumentTarget()
- {
- _document.Blocks.Add(_paragraph);
- }
-
- ///
- protected override void Write(LogEventInfo logEvent)
- {
- _document.Dispatcher.BeginInvoke(() =>
- {
- var message = $"{Layout.Render(logEvent)}\n";
- _paragraph.Inlines.Add(new Run(message) {Foreground = LogLevelColors[logEvent.Level]});
-
- // A massive paragraph slows the UI down
- if (_paragraph.Inlines.Count > _maxLines)
- _paragraph.Inlines.Remove(_paragraph.Inlines.FirstInline);
- });
- }
-
- private static readonly Dictionary LogLevelColors = new Dictionary
- {
- [LogLevel.Trace] = new SolidColorBrush(Colors.DimGray),
- [LogLevel.Debug] = new SolidColorBrush(Colors.DarkGray),
- [LogLevel.Info] = new SolidColorBrush(Colors.White),
- [LogLevel.Warn] = new SolidColorBrush(Colors.Magenta),
- [LogLevel.Error] = new SolidColorBrush(Colors.Yellow),
- [LogLevel.Fatal] = new SolidColorBrush(Colors.Red),
- };
- }
-}
diff --git a/Torch.Server/Initializer.cs b/Torch.Server/Initializer.cs
index fa288f0..fe61e95 100644
--- a/Torch.Server/Initializer.cs
+++ b/Torch.Server/Initializer.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading;
@@ -156,6 +157,10 @@ quit";
gameThread.Start();
var ui = new TorchUI(_server);
+
+ SynchronizationContext.SetSynchronizationContext(
+ new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
+
ui.ShowDialog();
}
}
@@ -192,8 +197,9 @@ quit";
try
{
log.Info("Downloading SteamCMD.");
- using (var client = new WebClient())
- client.DownloadFile("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip", STEAMCMD_ZIP);
+ using (var client = new HttpClient())
+ using (var file = File.Create(STEAMCMD_ZIP))
+ client.GetStreamAsync("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip").Result.CopyTo(file);
ZipFile.ExtractToDirectory(STEAMCMD_ZIP, STEAMCMD_DIR);
File.Delete(STEAMCMD_ZIP);
diff --git a/Torch.Server/LogViewerTarget.cs b/Torch.Server/LogViewerTarget.cs
new file mode 100644
index 0000000..dacc1d3
--- /dev/null
+++ b/Torch.Server/LogViewerTarget.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Windows.Media;
+using System.Windows.Threading;
+using NLog;
+using NLog.Targets;
+using Torch.Server.ViewModels;
+using Torch.Server.Views;
+
+namespace Torch.Server
+{
+ ///
+ /// NLog target that writes to a .
+ ///
+ [Target("logViewer")]
+ public sealed class LogViewerTarget : TargetWithLayout
+ {
+ public IList LogEntries { get; set; }
+ public SynchronizationContext TargetContext { get; set; }
+ private readonly int _maxLines = 1000;
+
+ ///
+ protected override void Write(LogEventInfo logEvent)
+ {
+ TargetContext?.Post(_sendOrPostCallback, logEvent);
+ }
+
+ private void WriteCallback(object state)
+ {
+ var logEvent = (LogEventInfo) state;
+ LogEntries?.Add(new(logEvent.TimeStamp, Layout.Render(logEvent), LogLevelColors[logEvent.Level]));
+ }
+
+ private static readonly Dictionary LogLevelColors = new()
+ {
+ [LogLevel.Trace] = new SolidColorBrush(Colors.DimGray),
+ [LogLevel.Debug] = new SolidColorBrush(Colors.DarkGray),
+ [LogLevel.Info] = new SolidColorBrush(Colors.White),
+ [LogLevel.Warn] = new SolidColorBrush(Colors.Magenta),
+ [LogLevel.Error] = new SolidColorBrush(Colors.Yellow),
+ [LogLevel.Fatal] = new SolidColorBrush(Colors.Red),
+ };
+
+ private readonly SendOrPostCallback _sendOrPostCallback;
+
+ public LogViewerTarget()
+ {
+ _sendOrPostCallback = WriteCallback;
+ }
+ }
+}
diff --git a/Torch.Server/Managers/InstanceManager.cs b/Torch.Server/Managers/InstanceManager.cs
index f807754..05fbbdc 100644
--- a/Torch.Server/Managers/InstanceManager.cs
+++ b/Torch.Server/Managers/InstanceManager.cs
@@ -5,6 +5,7 @@ using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Havok;
@@ -30,7 +31,7 @@ using VRage.Plugins;
namespace Torch.Server.Managers
{
- public class InstanceManager : Manager
+ public class InstanceManager : Manager, IInstanceManager
{
private const string CONFIG_NAME = "SpaceEngineers-Dedicated.cfg";
@@ -44,7 +45,9 @@ namespace Torch.Server.Managers
{
}
-
+
+ public IWorld SelectedWorld => DedicatedConfig.SelectedWorld;
+
public void LoadInstance(string path, bool validate = true)
{
Log.Info($"Loading instance {path}");
@@ -221,14 +224,11 @@ namespace Torch.Server.Managers
public void SaveConfig()
{
- if (((TorchServer)Torch).HasRun)
+ if (!((TorchServer)Torch).HasRun)
{
- Log.Warn("Checkpoint cache is stale, not saving dedicated config.");
- return;
+ DedicatedConfig.Save(Path.Combine(Torch.Config.InstancePath, CONFIG_NAME));
+ Log.Info("Saved dedicated config.");
}
-
- DedicatedConfig.Save(Path.Combine(Torch.Config.InstancePath, CONFIG_NAME));
- Log.Info("Saved dedicated config.");
try
{
@@ -255,7 +255,7 @@ namespace Torch.Server.Managers
}
catch (Exception e)
{
- Log.Error("Failed to write sandbox config, changes will not appear on server");
+ Log.Error("Failed to write sandbox config");
Log.Error(e);
}
}
@@ -276,12 +276,14 @@ namespace Torch.Server.Managers
}
}
- public class WorldViewModel : ViewModel
+ public class WorldViewModel : ViewModel, IWorld
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public string FolderName { get; set; }
public string WorldPath { get; }
+ public MyObjectBuilder_SessionSettings KeenSessionSettings => WorldConfiguration.Settings;
+ public MyObjectBuilder_Checkpoint KeenCheckpoint => Checkpoint;
public long WorldSizeKB { get; }
private string _checkpointPath;
private string _worldConfigPath;
@@ -329,13 +331,15 @@ namespace Torch.Server.Managers
public void LoadSandbox()
{
- MyObjectBuilderSerializer.DeserializeXML(_checkpointPath, out MyObjectBuilder_Checkpoint checkpoint);
+ if (!MyObjectBuilderSerializer.DeserializeXML(_checkpointPath, out MyObjectBuilder_Checkpoint checkpoint))
+ throw new SerializationException("Error reading checkpoint, see keen log for details");
Checkpoint = new CheckpointViewModel(checkpoint);
// migrate old saves
if (File.Exists(_worldConfigPath))
{
- MyObjectBuilderSerializer.DeserializeXML(_worldConfigPath, out MyObjectBuilder_WorldConfiguration worldConfig);
+ if (!MyObjectBuilderSerializer.DeserializeXML(_worldConfigPath, out MyObjectBuilder_WorldConfiguration worldConfig))
+ throw new SerializationException("Error reading settings, see keen log for details");
WorldConfiguration = new WorldConfigurationViewModel(worldConfig);
}
else
diff --git a/Torch.Server/Program.cs b/Torch.Server/Program.cs
index a42cc5b..a51ff6e 100644
--- a/Torch.Server/Program.cs
+++ b/Torch.Server/Program.cs
@@ -25,7 +25,7 @@ namespace Torch.Server
[STAThread]
public static void Main(string[] args)
{
- Target.Register("FlowDocument");
+ Target.Register(nameof(LogViewerTarget));
//Ensures that all the files are downloaded in the Torch directory.
var workingDir = new FileInfo(typeof(Program).Assembly.Location).Directory!.FullName;
var binDir = Path.Combine(workingDir, "DedicatedServer64");
diff --git a/Torch.Server/Properties/launchSettings.json b/Torch.Server/Properties/launchSettings.json
index 8236f31..80ab2ae 100644
--- a/Torch.Server/Properties/launchSettings.json
+++ b/Torch.Server/Properties/launchSettings.json
@@ -3,7 +3,7 @@
"profiles": {
"Torch.Server": {
"commandName": "Project",
-
+ "commandLineArgs": "-noupdate",
"use64Bit": true,
"hotReloadEnabled": false
}
diff --git a/Torch.Server/Torch.Server.csproj b/Torch.Server/Torch.Server.csproj
index c5db6e9..d6e2bbc 100644
--- a/Torch.Server/Torch.Server.csproj
+++ b/Torch.Server/Torch.Server.csproj
@@ -39,12 +39,13 @@
+
-
+
diff --git a/Torch.Server/TorchServer.cs b/Torch.Server/TorchServer.cs
index 248087d..e2d3ab1 100644
--- a/Torch.Server/TorchServer.cs
+++ b/Torch.Server/TorchServer.cs
@@ -10,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Diagnostics.Runtime;
using NLog;
+using PropertyChanged;
using Sandbox;
using Sandbox.Engine.Multiplayer;
using Sandbox.Game.Multiplayer;
@@ -212,6 +213,7 @@ namespace Torch.Server
Environment.Exit(0);
}
+ [SuppressPropertyChangedWarnings]
private void OnSessionStateChanged(ITorchSession session, TorchSessionState newState)
{
if (newState == TorchSessionState.Unloading || newState == TorchSessionState.Unloaded)
diff --git a/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs b/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs
index 491660f..ca24e9b 100644
--- a/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs
+++ b/Torch.Server/ViewModels/ConfigDedicatedViewModel.cs
@@ -18,11 +18,6 @@ namespace Torch.Server.ViewModels
private MyConfigDedicated _config;
public MyConfigDedicated Model => _config;
- public ConfigDedicatedViewModel() : this(new MyConfigDedicated(""))
- {
-
- }
-
public ConfigDedicatedViewModel(MyConfigDedicated configDedicated)
{
_config = configDedicated;
@@ -36,8 +31,7 @@ namespace Torch.Server.ViewModels
Validate();
_config.SessionSettings = SessionSettings;
- // Never ever
- //_config.IgnoreLastSession = true;
+ _config.IgnoreLastSession = true;
_config.Save(path);
}
@@ -73,8 +67,9 @@ namespace Torch.Server.ViewModels
}
}
- public async Task UpdateAllModInfosAsync()
+ public Task UpdateAllModInfosAsync()
{
+ return Task.CompletedTask;
/*if (!Mods.Any())
return;
List modInfos;
diff --git a/Torch.Server/ViewModels/ModItemInfo.cs b/Torch.Server/ViewModels/ModItemInfo.cs
index 6872103..5848c55 100644
--- a/Torch.Server/ViewModels/ModItemInfo.cs
+++ b/Torch.Server/ViewModels/ModItemInfo.cs
@@ -85,8 +85,9 @@ namespace Torch.Server.ViewModels
/// via the Steam web API.
///
///
- public async Task UpdateModInfoAsync()
+ public Task UpdateModInfoAsync()
{
+ return Task.FromResult(true);
/*if (UgcService.ToLower() == "mod.io")
return true;
@@ -104,7 +105,6 @@ namespace Torch.Server.ViewModels
Log.Info("Mod Info successfully retrieved!");
FriendlyName = modInfo.Title;
Description = modInfo.Description;*/
- return true;
}
public override string ToString()
diff --git a/Torch.Server/Views/ChatControl.xaml b/Torch.Server/Views/ChatControl.xaml
index 79c1148..45db245 100644
--- a/Torch.Server/Views/ChatControl.xaml
+++ b/Torch.Server/Views/ChatControl.xaml
@@ -2,7 +2,8 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:editors="http://wpfcontrols.com/"
mc:Ignorable="d">
@@ -18,7 +19,7 @@
-
+
diff --git a/Torch.Server/Views/ChatControl.xaml.cs b/Torch.Server/Views/ChatControl.xaml.cs
index 268668d..a9dcc1f 100644
--- a/Torch.Server/Views/ChatControl.xaml.cs
+++ b/Torch.Server/Views/ChatControl.xaml.cs
@@ -28,7 +28,9 @@ using Torch.API.Managers;
using Torch.API.Session;
using Torch.Managers;
using Torch.Server.Managers;
+using Torch.Server.Views;
using VRage.Game;
+using Color = VRageMath.Color;
namespace Torch.Server
{
@@ -38,12 +40,17 @@ namespace Torch.Server
public partial class ChatControl : UserControl
{
private static Logger _log = LogManager.GetCurrentClassLogger();
- private ITorchServer _server;
+#pragma warning disable CS0618
+ private ITorchServer _server = (ITorchServer) TorchBase.Instance;
+#pragma warning restore CS0618
+ private readonly LinkedList _lastMessages = new();
+ private LinkedListNode _currentLastMessageNode;
public ChatControl()
{
InitializeComponent();
this.IsVisibleChanged += OnIsVisibleChanged;
+ MessageBox.Provider = new CommandSuggestionsProvider(_server);
}
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
@@ -57,8 +64,8 @@ namespace Torch.Server
Dispatcher.Invoke(() =>
{
- Message.Focus();
- Keyboard.Focus(Message);
+ MessageBox.Focus();
+ Keyboard.Focus(MessageBox);
});
});
}
@@ -160,35 +167,50 @@ namespace Torch.Server
private void Message_OnKeyDown(object sender, KeyEventArgs e)
{
- if (e.Key == Key.Enter)
- OnMessageEntered();
+ switch (e.Key)
+ {
+ case Key.Enter:
+ OnMessageEntered();
+ break;
+ case Key.Up:
+ _currentLastMessageNode = _currentLastMessageNode?.Previous ?? _lastMessages.Last;
+ MessageBox.Text = _currentLastMessageNode?.Value ?? string.Empty;
+ break;
+ case Key.Down:
+ _currentLastMessageNode = _currentLastMessageNode?.Next ?? _lastMessages.First;
+ MessageBox.Text = _currentLastMessageNode?.Value ?? string.Empty;
+ break;
+ }
}
private void OnMessageEntered()
{
//Can't use Message.Text directly because of object ownership in WPF.
- var text = Message.Text;
+ var text = MessageBox.Text;
if (string.IsNullOrEmpty(text))
return;
var commands = _server.CurrentSession?.Managers.GetManager();
if (commands != null && commands.IsCommand(text))
{
- InsertMessage(new TorchChatMessage(TorchBase.Instance.Config.ChatName, text, TorchBase.Instance.Config.ChatColor));
+ InsertMessage(new(_server.Config.ChatName, text, Color.Red, _server.Config.ChatColor));
_server.Invoke(() =>
{
- if (!commands.HandleCommandFromServer(text, InsertMessage))
- {
- InsertMessage(new TorchChatMessage(TorchBase.Instance.Config.ChatName, "Invalid command.", TorchBase.Instance.Config.ChatColor));
- return;
- }
+ if (commands.HandleCommandFromServer(text, InsertMessage)) return;
+ InsertMessage(new(_server.Config.ChatName, "Invalid command.", Color.Red, _server.Config.ChatColor));
});
}
else
{
_server.CurrentSession?.Managers.GetManager().SendMessageAsSelf(text);
}
- Message.Text = "";
+ if (_currentLastMessageNode is { } && _currentLastMessageNode.Value == text)
+ {
+ _lastMessages.Remove(_currentLastMessageNode);
+ }
+ _lastMessages.AddLast(text);
+ _currentLastMessageNode = null;
+ MessageBox.Text = "";
}
}
}
diff --git a/Torch.Server/Views/ConfigControl.xaml b/Torch.Server/Views/ConfigControl.xaml
index 798c4af..0cb4f02 100644
--- a/Torch.Server/Views/ConfigControl.xaml
+++ b/Torch.Server/Views/ConfigControl.xaml
@@ -16,9 +16,6 @@
-
-
-
@@ -58,7 +55,7 @@
-
+
@@ -133,7 +130,7 @@
-
+
diff --git a/Torch.Server/Views/ConfigControl.xaml.cs b/Torch.Server/Views/ConfigControl.xaml.cs
index 96e17e9..1759ea3 100644
--- a/Torch.Server/Views/ConfigControl.xaml.cs
+++ b/Torch.Server/Views/ConfigControl.xaml.cs
@@ -8,6 +8,8 @@ using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Threading;
+using Sandbox;
+using Torch.API;
using Torch.API.Managers;
using Torch.Server.Annotations;
using Torch.Server.Managers;
@@ -32,15 +34,26 @@ namespace Torch.Server.Views
public ConfigControl()
{
- InitializeComponent();
- _instanceManager = TorchBase.Instance.Managers.GetManager();
+#pragma warning disable CS0618
+ var instance = TorchBase.Instance;
+#pragma warning restore CS0618
+ instance.GameStateChanged += InstanceOnGameStateChanged;
+
+ _instanceManager = instance.Managers.GetManager();
_instanceManager.InstanceLoaded += _instanceManager_InstanceLoaded;
DataContext = _instanceManager.DedicatedConfig;
- TorchSettings.DataContext = (TorchConfig)TorchBase.Instance.Config;
+ InitializeComponent();
+ TorchSettings.DataContext = (TorchConfig)instance.Config;
// Gets called once all children are loaded
Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(ApplyStyles));
}
+ private void InstanceOnGameStateChanged(MySandboxGame game, TorchGameState newState)
+ {
+ if (newState > TorchGameState.Creating)
+ Dispatcher.InvokeAsync(() => DediConfigScrollViewer.IsEnabled = false);
+ }
+
private void CheckValid()
{
ConfigValid = !_bindingExpressions.Any(x => x.HasError);
diff --git a/Torch.Server/Views/ModListControl.xaml b/Torch.Server/Views/ModListControl.xaml
index b24ec07..027528a 100644
--- a/Torch.Server/Views/ModListControl.xaml
+++ b/Torch.Server/Views/ModListControl.xaml
@@ -25,9 +25,6 @@
-
-
-
diff --git a/Torch.Server/Views/TorchUI.xaml b/Torch.Server/Views/TorchUI.xaml
index 5a37ce2..aa2c6b9 100644
--- a/Torch.Server/Views/TorchUI.xaml
+++ b/Torch.Server/Views/TorchUI.xaml
@@ -17,11 +17,6 @@
-
@@ -63,10 +58,10 @@
-
+
-
+
@@ -93,7 +88,7 @@
-
+
diff --git a/Torch.Server/Views/TorchUI.xaml.cs b/Torch.Server/Views/TorchUI.xaml.cs
index 1850351..7bbefd8 100644
--- a/Torch.Server/Views/TorchUI.xaml.cs
+++ b/Torch.Server/Views/TorchUI.xaml.cs
@@ -14,12 +14,14 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
+using System.Windows.Threading;
using NLog;
using NLog.Targets.Wrappers;
using Sandbox;
using Torch.API;
using Torch.API.Managers;
using Torch.Server.Managers;
+using Torch.Server.ViewModels;
using Torch.Server.Views;
using MessageBoxResult = System.Windows.MessageBoxResult;
@@ -30,23 +32,22 @@ namespace Torch.Server
///
public partial class TorchUI : Window
{
- private TorchServer _server;
- private TorchConfig _config;
-
- private bool _autoscrollLog = true;
+ private readonly TorchServer _server;
+ private ITorchConfig Config => _server.Config;
public TorchUI(TorchServer server)
{
- WindowStartupLocation = WindowStartupLocation.Manual;
- _config = (TorchConfig)server.Config;
- Width = _config.WindowWidth;
- Height = _config.WindowHeight;
_server = server;
//TODO: data binding for whole server
DataContext = server;
+
+ WindowStartupLocation = WindowStartupLocation.Manual;
+ Width = Config.WindowWidth;
+ Height = Config.WindowHeight;
InitializeComponent();
+ ConsoleText.FontSize = Config.FontSize;
- AttachConsole();
+ Loaded += OnLoaded;
//Left = _config.WindowPosition.X;
//Top = _config.WindowPosition.Y;
@@ -56,94 +57,35 @@ namespace Torch.Server
Chat.BindServer(server);
PlayerList.BindServer(server);
Plugins.BindServer(server);
- LoadConfig((TorchConfig)server.Config);
+
+ if (Config.EntityManagerEnabled)
+ {
+ EntityManagerTab.Content = new EntitiesControl();
+ }
Themes.uiSource = this;
- Themes.SetConfig(_config);
- Title = $"{_config.InstanceName} - Torch {server.TorchVersion}, SE {server.GameVersion}";
-
- Loaded += TorchUI_Loaded;
+ Themes.SetConfig((TorchConfig) Config);
+ Title = $"{Config.InstanceName} - Torch {server.TorchVersion}, SE {server.GameVersion}";
}
- private void TorchUI_Loaded(object sender, RoutedEventArgs e)
+ private void OnLoaded(object sender, RoutedEventArgs e)
{
- var scrollViewer = FindDescendant(ConsoleText);
- scrollViewer.ScrollChanged += ConsoleText_OnScrollChanged;
+ AttachConsole();
}
private void AttachConsole()
{
- const string target = "wpf";
- var doc = LogManager.Configuration.FindTargetByName(target)?.Document;
- if (doc == null)
+ const string targetName = "wpf";
+ var target = LogManager.Configuration.FindTargetByName(targetName);
+ if (target == null)
{
- var wrapped = LogManager.Configuration.FindTargetByName(target);
- doc = (wrapped?.WrappedTarget as FlowDocumentTarget)?.Document;
+ var wrapped = LogManager.Configuration.FindTargetByName(targetName);
+ target = wrapped?.WrappedTarget as LogViewerTarget;
}
- ConsoleText.FontSize = _config.FontSize;
- ConsoleText.Document = doc ?? new FlowDocument(new Paragraph(new Run("No target!")));
- ConsoleText.TextChanged += ConsoleText_OnTextChanged;
- }
-
- public static T FindDescendant(DependencyObject obj) where T : DependencyObject
- {
- if (obj == null) return default(T);
- int numberChildren = VisualTreeHelper.GetChildrenCount(obj);
- if (numberChildren == 0) return default(T);
-
- for (int i = 0; i < numberChildren; i++)
- {
- DependencyObject child = VisualTreeHelper.GetChild(obj, i);
- if (child is T)
- {
- return (T)child;
- }
- }
-
- for (int i = 0; i < numberChildren; i++)
- {
- DependencyObject child = VisualTreeHelper.GetChild(obj, i);
- var potentialMatch = FindDescendant(child);
- if (potentialMatch != default(T))
- {
- return potentialMatch;
- }
- }
-
- return default(T);
- }
-
- private void ConsoleText_OnTextChanged(object sender, TextChangedEventArgs args)
- {
- var textBox = (RichTextBox) sender;
- if (_autoscrollLog)
- ConsoleText.ScrollToEnd();
- }
-
- private void ConsoleText_OnScrollChanged(object sender, ScrollChangedEventArgs e)
- {
- var scrollViewer = (ScrollViewer) sender;
- if (e.ExtentHeightChange == 0)
- {
- // User change.
- _autoscrollLog = scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight;
- }
- }
-
- public void LoadConfig(TorchConfig config)
- {
- if (!Directory.Exists(config.InstancePath))
- return;
-
- _config = config;
- Dispatcher.Invoke(() =>
- {
- EntityManagerTab.IsEnabled = _config.EntityManagerEnabled;
- if (_config.EntityManagerEnabled)
- {
- EntityManagerTab.Content = new EntitiesControl();
- }
- });
+ if (target is null) return;
+ var viewModel = (LogViewerViewModel)ConsoleText.DataContext;
+ target.LogEntries = viewModel.LogEntries;
+ target.TargetContext = SynchronizationContext.Current;
}
private void BtnStart_Click(object sender, RoutedEventArgs e)
@@ -176,21 +118,5 @@ namespace Torch.Server
Process.GetCurrentProcess().Kill();
}
-
- private void BtnRestart_Click(object sender, RoutedEventArgs e)
- {
- //MySandboxGame.Static.Invoke(MySandboxGame.ReloadDedicatedServerSession); use i
- }
-
- private void InstancePathBox_OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
- {
- var name = ((TextBox)sender).Text;
-
- if (!Directory.Exists(name))
- return;
-
- _config.InstancePath = name;
- _server.Managers.GetManager().LoadInstance(_config.InstancePath);
- }
}
}
diff --git a/Torch.Tests/Torch.Tests.csproj b/Torch.Tests/Torch.Tests.csproj
index d973b51..d3e2c1f 100644
--- a/Torch.Tests/Torch.Tests.csproj
+++ b/Torch.Tests/Torch.Tests.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/Torch/Commands/CommandManager.cs b/Torch/Commands/CommandManager.cs
index 7d5a083..87a25b4 100644
--- a/Torch/Commands/CommandManager.cs
+++ b/Torch/Commands/CommandManager.cs
@@ -49,12 +49,13 @@ namespace Torch.Commands
{
return !string.IsNullOrEmpty(command) && command[0] == Prefix;
}
-
- public void RegisterCommandModule(Type moduleType, ITorchPlugin plugin = null)
+
+ public int RegisterCommandModule(Type moduleType, ITorchPlugin plugin = null)
{
if (!moduleType.IsSubclassOf(typeof(CommandModule)))
- return;
+ return 0;
+ var i = 0;
foreach (var method in moduleType.GetMethods())
{
var commandAttrib = method.GetCustomAttribute();
@@ -63,11 +64,14 @@ namespace Torch.Commands
var command = new Command(plugin, method);
var cmdPath = string.Join(".", command.Path);
- _log.Info($"Registering command '{cmdPath}'");
+ _log.Debug($"Registering command '{cmdPath}'");
+ i++;
if (!Commands.AddCommand(command))
_log.Error($"Command path {cmdPath} is already registered.");
}
+
+ return i;
}
public void UnregisterPluginCommands(ITorchPlugin plugin)
@@ -78,10 +82,9 @@ namespace Torch.Commands
public void RegisterPluginCommands(ITorchPlugin plugin)
{
var assembly = plugin.GetType().Assembly;
- foreach (var type in assembly.ExportedTypes)
- {
- RegisterCommandModule(type, plugin);
- }
+ var count = assembly.ExportedTypes.Sum(type => RegisterCommandModule(type, plugin));
+ if (count > 0)
+ _log.Info($"Registered {count} commands from {plugin.Name}");
}
private List HandleCommandFromServerInternal(string message, Action subscriber = null)
diff --git a/Torch/Managers/PatchManager/DecoratedMethod.cs b/Torch/Managers/PatchManager/DecoratedMethod.cs
index d2cd2c3..5bbb249 100644
--- a/Torch/Managers/PatchManager/DecoratedMethod.cs
+++ b/Torch/Managers/PatchManager/DecoratedMethod.cs
@@ -23,6 +23,12 @@ namespace Torch.Managers.PatchManager
{
private static Action IsAppliedSetter;
+ [ReflectedMethodInfo(typeof(MethodBase), nameof(MethodBase.GetMethodFromHandle), Parameters = new[] {typeof(RuntimeMethodHandle)})]
+ private static MethodInfo _getMethodFromHandle = null!;
+
+ [ReflectedMethodInfo(typeof(MethodBase), nameof(MethodBase.GetMethodFromHandle), Parameters = new[] {typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle)})]
+ private static MethodInfo _getMethodFromHandleGeneric = null!;
+
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private readonly MethodBase _method;
@@ -103,6 +109,7 @@ namespace Torch.Managers.PatchManager
public const string INSTANCE_PARAMETER = "__instance";
public const string RESULT_PARAMETER = "__result";
public const string PREFIX_SKIPPED_PARAMETER = "__prefixSkipped";
+ public const string ORIGINAL_PARAMETER = "__original";
public const string LOCAL_PARAMETER = "__local";
private void SavePatchedMethod(string target)
@@ -320,6 +327,24 @@ namespace Torch.Managers.PatchManager
yield return new MsilInstruction(OpCodes.Ldarg_0);
break;
}
+ case ORIGINAL_PARAMETER:
+ {
+ if (!typeof(MethodBase).IsAssignableFrom(param.ParameterType))
+ throw new PatchException($"Original parameter should be assignable to {nameof(MethodBase)}",
+ _method);
+ yield return new MsilInstruction(OpCodes.Ldtoken).InlineValue(_method);
+ if (_method.DeclaringType!.ContainsGenericParameters)
+ {
+ yield return new MsilInstruction(OpCodes.Ldtoken).InlineValue(_method.DeclaringType);
+ yield return new MsilInstruction(OpCodes.Call).InlineValue(_getMethodFromHandleGeneric);
+ }
+ else
+ yield return new MsilInstruction(OpCodes.Call).InlineValue(_getMethodFromHandle);
+
+ if (param.ParameterType != typeof(MethodBase))
+ yield return new MsilInstruction(OpCodes.Castclass).InlineValue(param.ParameterType);
+ break;
+ }
case PREFIX_SKIPPED_PARAMETER:
{
if (param.ParameterType != typeof(bool))
diff --git a/Torch/Patches/KeenLogPatch.cs b/Torch/Patches/KeenLogPatch.cs
index 13bff83..c6e6e8c 100644
--- a/Torch/Patches/KeenLogPatch.cs
+++ b/Torch/Patches/KeenLogPatch.cs
@@ -9,6 +9,7 @@ using NLog;
using Torch.API;
using Torch.Managers.PatchManager;
using Torch.Utils;
+using VRage;
using VRage.Utils;
namespace Torch.Patches
@@ -42,71 +43,81 @@ namespace Torch.Patches
[ReflectedMethodInfo(typeof(MyLog), nameof(MyLog.WriteLineAndConsole), Parameters = new[] { typeof(string) })]
private static MethodInfo _logWriteLineAndConsole;
+
+ [ReflectedMethodInfo(typeof(MyLog), nameof(MyLog.Init))]
+ private static MethodInfo _logInit;
#pragma warning restore 649
public static void Patch(PatchContext context)
{
- context.GetPattern(_logStringBuilder).Prefixes.Add(Method(nameof(PrefixLogStringBuilder)));
- context.GetPattern(_logFormatted).Prefixes.Add(Method(nameof(PrefixLogFormatted)));
+ context.GetPattern(_logStringBuilder).AddPrefix(nameof(PrefixLogStringBuilder));
+ context.GetPattern(_logFormatted).AddPrefix(nameof(PrefixLogFormatted));
- context.GetPattern(_logWriteLine).Prefixes.Add(Method(nameof(PrefixWriteLine)));
- context.GetPattern(_logAppendToClosedLog).Prefixes.Add(Method(nameof(PrefixAppendToClosedLog)));
- context.GetPattern(_logWriteLineAndConsole).Prefixes.Add(Method(nameof(PrefixWriteLineConsole)));
+ context.GetPattern(_logWriteLine).AddPrefix(nameof(PrefixWriteLine));
+ context.GetPattern(_logAppendToClosedLog).AddPrefix(nameof(PrefixAppendToClosedLog));
+ context.GetPattern(_logWriteLineAndConsole).AddPrefix(nameof(PrefixWriteLineConsole));
- context.GetPattern(_logWriteLineException).Prefixes.Add(Method(nameof(PrefixWriteLineException)));
- context.GetPattern(_logAppendToClosedLogException).Prefixes.Add(Method(nameof(PrefixAppendToClosedLogException)));
+ context.GetPattern(_logWriteLineException).AddPrefix(nameof(PrefixWriteLineException));
+ context.GetPattern(_logAppendToClosedLogException).AddPrefix(nameof(PrefixAppendToClosedLogException));
- context.GetPattern(_logWriteLineOptions).Prefixes.Add(Method(nameof(PrefixWriteLineOptions)));
+ context.GetPattern(_logWriteLineOptions).AddPrefix(nameof(PrefixWriteLineOptions));
- }
-
- private static MethodInfo Method(string name)
- {
- return typeof(KeenLogPatch).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
+ context.GetPattern(_logInit).AddPrefix(nameof(PrefixInit));
}
[ReflectedMethod(Name = "GetIdentByThread")]
- private static Func GetIndentByThread = null!;
+ private static Func _getIndentByThread = null!;
- [ThreadStatic]
- private static StringBuilder _tmpStringBuilder;
-
- private static StringBuilder PrepareLog(MyLog log)
+ [ReflectedGetter(Name = "m_lock")]
+ private static Func _lockGetter = null!;
+
+ [ReflectedSetter(Name = "m_enabled")]
+ private static Action _enabledSetter = null!;
+
+ private static int GetIndentByCurrentThread()
{
- _tmpStringBuilder ??= new();
+ using var l = _lockGetter(MyLog.Default).AcquireExclusiveUsing();
+ return _getIndentByThread(MyLog.Default, Environment.CurrentManagedThreadId);
+ }
+
+ private static bool PrefixInit(MyLog __instance, StringBuilder appVersionString)
+ {
+ __instance.WriteLine("Log Started");
+ var byThreadField =
+ typeof(MyLog).GetField("m_indentsByThread", BindingFlags.Instance | BindingFlags.NonPublic)!;
+ var indentsField = typeof(MyLog).GetField("m_indents", BindingFlags.Instance | BindingFlags.NonPublic)!;
- _tmpStringBuilder.Clear();
- var t = GetIndentByThread(log, Environment.CurrentManagedThreadId);
-
- _tmpStringBuilder.Append(' ', t * 3);
- return _tmpStringBuilder;
+ byThreadField.SetValue(__instance, Activator.CreateInstance(byThreadField.FieldType));
+ indentsField.SetValue(__instance, Activator.CreateInstance(indentsField.FieldType));
+ _enabledSetter(__instance, true);
+ return false;
}
private static bool PrefixWriteLine(MyLog __instance, string msg)
{
- if (__instance.LogEnabled)
- _log.Debug(PrepareLog(__instance).Append(msg));
+ if (__instance.LogEnabled && _log.IsDebugEnabled)
+ _log.Debug($"{" ".PadRight(3 * GetIndentByCurrentThread())}{msg}");
return false;
}
private static bool PrefixWriteLineConsole(MyLog __instance, string msg)
{
- if (__instance.LogEnabled)
- _log.Info(PrepareLog(__instance).Append(msg));
+ if (__instance.LogEnabled && _log.IsInfoEnabled)
+ _log.Info($"{" ".PadRight(3 * GetIndentByCurrentThread())}{msg}");
return false;
}
private static bool PrefixAppendToClosedLog(MyLog __instance, string text)
{
- if (__instance.LogEnabled)
- _log.Info(PrepareLog(__instance).Append(text));
+ if (__instance.LogEnabled && _log.IsDebugEnabled)
+ _log.Debug($"{" ".PadRight(3 * GetIndentByCurrentThread())}{text}");
return false;
}
private static bool PrefixWriteLineOptions(MyLog __instance, string message, LoggingOptions option)
{
- if (__instance.LogEnabled && __instance.LogFlag(option))
- _log.Info(PrepareLog(__instance).Append(message));
+ if (__instance.LogEnabled && __instance.LogFlag(option) && _log.IsDebugEnabled)
+ _log.Info($"{" ".PadRight(3 * GetIndentByCurrentThread())}{message}");
return false;
}
@@ -126,22 +137,22 @@ namespace Torch.Patches
{
if (__instance.LogEnabled)
return false;
- // Sometimes this is called with a pre-formatted string and no args
- // and causes a crash when the format string contains braces
- var sb = PrepareLog(__instance);
- if (args is {Length: > 0})
- sb.AppendFormat(format, args);
- else
- sb.Append(format);
- _log.Log(LogLevelFor(severity), sb);
+ // ReSharper disable once TemplateIsNotCompileTimeConstantProblem
+ _log.Log(new(LogLevelFor(severity), _log.Name, $"{" ".PadRight(3 * GetIndentByCurrentThread())}{string.Format(format, args)}"));
return false;
}
private static bool PrefixLogStringBuilder(MyLog __instance, MyLogSeverity severity, StringBuilder builder)
{
- if (__instance.LogEnabled)
- _log.Log(LogLevelFor(severity), PrepareLog(__instance).Append(builder));
+ if (!__instance.LogEnabled) return false;
+ var indent = GetIndentByCurrentThread() * 3;
+
+ // because append resizes every char
+ builder.EnsureCapacity(indent);
+ builder.Append(' ', indent);
+
+ _log.Log(LogLevelFor(severity), builder);
return false;
}
diff --git a/Torch/Session/TorchSession.cs b/Torch/Session/TorchSession.cs
index 2120ef1..c8b04f4 100644
--- a/Torch/Session/TorchSession.cs
+++ b/Torch/Session/TorchSession.cs
@@ -26,13 +26,16 @@ namespace Torch.Session
///
public MySession KeenSession { get; }
+ public IWorld World { get; }
+
///
public IDependencyManager Managers { get; }
- public TorchSession(ITorchBase torch, MySession keenSession)
+ public TorchSession(ITorchBase torch, MySession keenSession, IWorld world)
{
Torch = torch;
KeenSession = keenSession;
+ World = world;
Managers = new DependencyManager(torch.Managers);
}
diff --git a/Torch/Session/TorchSessionManager.cs b/Torch/Session/TorchSessionManager.cs
index 05b415a..050e177 100644
--- a/Torch/Session/TorchSessionManager.cs
+++ b/Torch/Session/TorchSessionManager.cs
@@ -28,6 +28,9 @@ namespace Torch.Session
private readonly Dictionary _overrideMods;
+ [Dependency]
+ private IInstanceManager _instanceManager = null!;
+
public event Action OverrideModsChanged;
///
@@ -101,15 +104,18 @@ namespace Torch.Session
{
try
{
+ if (_instanceManager.SelectedWorld is null)
+ throw new InvalidOperationException("No valid worlds selected! Please select world first.");
+
if (_currentSession != null)
{
_log.Warn($"Override old torch session {_currentSession.KeenSession.Name}");
_currentSession.Detach();
}
- _log.Info($"Starting new torch session for {MySession.Static.Name}");
+ _log.Info($"Starting new torch session for {_instanceManager.SelectedWorld.FolderName}");
- _currentSession = new TorchSession(Torch, MySession.Static);
+ _currentSession = new TorchSession(Torch, MySession.Static, _instanceManager.SelectedWorld);
SetState(TorchSessionState.Loading);
}
catch (Exception e)
@@ -123,11 +129,9 @@ namespace Torch.Session
{
try
{
- if (_currentSession == null)
- {
- _log.Warn("Session loaded event occurred when we don't have a session.");
- return;
- }
+ if (_currentSession is null)
+ throw new InvalidOperationException("Session loaded event occurred when we don't have a session.");
+
foreach (SessionManagerFactoryDel factory in _factories)
{
IManager manager = factory(CurrentSession);
@@ -135,7 +139,7 @@ namespace Torch.Session
CurrentSession.Managers.AddManager(manager);
}
(CurrentSession as TorchSession)?.Attach();
- _log.Info($"Loaded torch session for {MySession.Static.Name}");
+ _log.Info($"Loaded torch session for {CurrentSession.World.FolderName}");
SetState(TorchSessionState.Loaded);
}
catch (Exception e)
@@ -149,12 +153,10 @@ namespace Torch.Session
{
try
{
- if (_currentSession == null)
- {
- _log.Warn("Session unloading event occurred when we don't have a session.");
- return;
- }
- _log.Info($"Unloading torch session for {_currentSession.KeenSession.Name}");
+ if (_currentSession is null)
+ throw new InvalidOperationException("Session loaded event occurred when we don't have a session.");
+
+ _log.Info($"Unloading torch session for {_currentSession.World.FolderName}");
SetState(TorchSessionState.Unloading);
_currentSession.Detach();
}
@@ -169,12 +171,10 @@ namespace Torch.Session
{
try
{
- if (_currentSession == null)
- {
- _log.Warn("Session unloading event occurred when we don't have a session.");
- return;
- }
- _log.Info($"Unloaded torch session for {_currentSession.KeenSession.Name}");
+ if (_currentSession is null)
+ throw new InvalidOperationException("Session loaded event occurred when we don't have a session.");
+
+ _log.Info($"Unloaded torch session for {_currentSession.World.FolderName}");
SetState(TorchSessionState.Unloaded);
_currentSession = null;
}
diff --git a/Torch/Torch.csproj b/Torch/Torch.csproj
index 8ded7bc..a7d20f7 100644
--- a/Torch/Torch.csproj
+++ b/Torch/Torch.csproj
@@ -20,10 +20,10 @@
-
+
-
+