Compare commits
2 Commits
1.0.180.47
...
1.0.182.32
Author | SHA1 | Date | |
---|---|---|---|
![]() |
79fe6a08ab | ||
![]() |
5e0f69e0e6 |
10
NLog.config
10
NLog.config
@@ -3,13 +3,13 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<targets>
|
||||
<target name="logfile" layout="${longdate} [${level:uppercase=true}] ${logger}: ${message}" xsi:type="File" fileName="Torch.log" deleteOldFileOnStartup="true"/>
|
||||
<target name="console" layout="${longdate} [${level:uppercase=true}] ${logger}: ${message}" xsi:type="ColoredConsole" />
|
||||
<target xsi:type="File" name="main" layout="${time} [${level:uppercase=true}] ${logger}: ${message}" fileName="Logs\Torch-${shortdate}.log" />
|
||||
<target xsi:type="File" name="chat" layout="${longdate} ${message}" fileName="Logs\Chat.log" />
|
||||
<target xsi:type="ColoredConsole" name="console" layout="${time} [${level:uppercase=true}] ${logger}: ${message}" />
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<logger name="*" minlevel="Info" writeTo="logfile" />
|
||||
<logger name="*" minlevel="Info" writeTo="console" />
|
||||
<logger name="*" minlevel="Info" writeTo="main, console" />
|
||||
<logger name="Chat" minlevel="Info" writeTo="chat" />
|
||||
</rules>
|
||||
|
||||
</nlog>
|
@@ -7,6 +7,7 @@
|
||||
bool RedownloadPlugins { get; set; }
|
||||
bool AutomaticUpdates { get; set; }
|
||||
bool RestartOnCrash { get; set; }
|
||||
int TickTimeout { get; set; }
|
||||
|
||||
bool Save(string path = null);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.169.376")]
|
||||
[assembly: AssemblyFileVersion("1.0.169.376")]
|
||||
[assembly: AssemblyVersion("1.0.182.329")]
|
||||
[assembly: AssemblyFileVersion("1.0.182.329")]
|
@@ -291,6 +291,8 @@ quit";
|
||||
{
|
||||
var ex = (Exception)e.ExceptionObject;
|
||||
_log.Fatal(ex);
|
||||
Console.WriteLine("Exiting in 5 seconds.");
|
||||
Thread.Sleep(5000);
|
||||
if (_restartOnCrash)
|
||||
{
|
||||
/* Throws an exception somehow and I'm too lazy to debug it.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.169.376")]
|
||||
[assembly: AssemblyFileVersion("1.0.169.376")]
|
||||
[assembly: AssemblyVersion("1.0.182.329")]
|
||||
[assembly: AssemblyFileVersion("1.0.182.329")]
|
@@ -15,15 +15,19 @@ namespace Torch.Server
|
||||
public string InstanceName { get; set; }
|
||||
#warning World Path not implemented
|
||||
public string WorldPath { get; set; }
|
||||
//public int Autosave { get; set; }
|
||||
//public bool AutoRestart { get; set; }
|
||||
//public bool LogChat { get; set; }
|
||||
public bool AutomaticUpdates { get; set; } = true;
|
||||
public bool RedownloadPlugins { get; set; }
|
||||
public bool RestartOnCrash { get; set; }
|
||||
/// <summary>
|
||||
/// How long in seconds to wait before automatically resetting a frozen server.
|
||||
/// </summary>
|
||||
public int TickTimeout { get; set; } = 60;
|
||||
/// <summary>
|
||||
/// A list of plugins to install or update. TODO
|
||||
/// </summary>
|
||||
public List<string> Plugins { get; set; } = new List<string>();
|
||||
public Point WindowSize { get; set; } = new Point(800, 600);
|
||||
public Point WindowPosition { get; set; } = new Point();
|
||||
internal Point WindowSize { get; set; } = new Point(800, 600);
|
||||
internal Point WindowPosition { get; set; } = new Point();
|
||||
[NonSerialized]
|
||||
private string _path;
|
||||
|
||||
|
@@ -11,6 +11,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using Microsoft.Xml.Serialization.GeneratedAssembly;
|
||||
using Sandbox.Engine.Analytics;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.ModAPI;
|
||||
using SteamSDK;
|
||||
@@ -35,9 +36,11 @@ namespace Torch.Server
|
||||
public TimeSpan ElapsedPlayTime { get => _elapsedPlayTime; set { _elapsedPlayTime = value; OnPropertyChanged(); } }
|
||||
public Thread GameThread { get; private set; }
|
||||
public ServerState State { get => _state; private set { _state = value; OnPropertyChanged(); } }
|
||||
public bool IsRunning { get => _isRunning; set { _isRunning = value; OnPropertyChanged(); } }
|
||||
public string InstanceName => Config?.InstanceName;
|
||||
public string InstancePath => Config?.InstancePath;
|
||||
|
||||
private bool _isRunning;
|
||||
private ServerState _state;
|
||||
private TimeSpan _elapsedPlayTime;
|
||||
private float _simRatio;
|
||||
@@ -47,6 +50,7 @@ namespace Torch.Server
|
||||
public TorchServer(TorchConfig config = null)
|
||||
{
|
||||
Config = config ?? new TorchConfig();
|
||||
MyFakes.ENABLE_INFINARIO = false;
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
@@ -55,7 +59,6 @@ namespace Torch.Server
|
||||
|
||||
Log.Info($"Init server '{Config.InstanceName}' at '{Config.InstancePath}'");
|
||||
|
||||
MyFakes.ENABLE_INFINARIO = false;
|
||||
MyPerGameSettings.SendLogToKeen = false;
|
||||
MyPerServerSettings.GameName = MyPerGameSettings.GameName;
|
||||
MyPerServerSettings.GameNameSafe = MyPerGameSettings.GameNameSafe;
|
||||
@@ -171,10 +174,10 @@ namespace Torch.Server
|
||||
SimulationRatio = Sync.ServerSimulationRatio;
|
||||
ElapsedPlayTime = MySession.Static?.ElapsedPlayTime ?? default(TimeSpan);
|
||||
|
||||
if (_watchdog == null)
|
||||
if (_watchdog == null && Instance.Config.TickTimeout > 0)
|
||||
{
|
||||
Log.Info("Starting server watchdog.");
|
||||
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero, TimeSpan.FromSeconds(30));
|
||||
_watchdog = new Timer(CheckServerResponding, this, TimeSpan.Zero, TimeSpan.FromSeconds(Instance.Config.TickTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,12 +185,12 @@ namespace Torch.Server
|
||||
{
|
||||
var mre = new ManualResetEvent(false);
|
||||
((TorchServer)state).Invoke(() => mre.Set());
|
||||
if (!mre.WaitOne(TimeSpan.FromSeconds(30)))
|
||||
if (!mre.WaitOne(TimeSpan.FromSeconds(Instance.Config.TickTimeout)))
|
||||
{
|
||||
var mainThread = MySandboxGame.Static.UpdateThread;
|
||||
mainThread.Suspend();
|
||||
var stackTrace = new StackTrace(mainThread, true);
|
||||
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least 30 seconds.\n{stackTrace}");
|
||||
throw new TimeoutException($"Server watchdog detected that the server was frozen for at least {Instance.Config.TickTimeout} seconds.\n{stackTrace}");
|
||||
}
|
||||
|
||||
Log.Debug("Server watchdog responded");
|
||||
|
@@ -41,7 +41,7 @@ namespace Torch.Server.Views
|
||||
{
|
||||
Entities.CurrentEntity = vm;
|
||||
if (e.NewValue is GridViewModel gvm)
|
||||
EditorFrame.Content = new Entities.GridView { DataContext = gvm};
|
||||
EditorFrame.Content = new Entities.GridView {DataContext = gvm};
|
||||
if (e.NewValue is BlockViewModel bvm)
|
||||
EditorFrame.Content = new BlockView {DataContext = bvm};
|
||||
if (e.NewValue is VoxelMapViewModel vvm)
|
||||
|
@@ -14,9 +14,9 @@
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<WrapPanel>
|
||||
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding Value.Name}" FontWeight="Bold"/>
|
||||
<TextBlock Text=" ("/>
|
||||
<TextBlock Text="{Binding State}"/>
|
||||
<TextBlock Text="{Binding Value.State}"/>
|
||||
<TextBlock Text=")"/>
|
||||
</WrapPanel>
|
||||
</DataTemplate>
|
||||
|
@@ -21,6 +21,7 @@ using Sandbox.ModAPI;
|
||||
using SteamSDK;
|
||||
using Torch.API;
|
||||
using Torch.Managers;
|
||||
using Torch.ViewModels;
|
||||
using VRage.Game.ModAPI;
|
||||
|
||||
namespace Torch.Server
|
||||
@@ -45,20 +46,14 @@ namespace Torch.Server
|
||||
|
||||
private void KickButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var player = PlayerList.SelectedItem as IMyPlayer;
|
||||
if (player != null)
|
||||
{
|
||||
_server.Multiplayer.KickPlayer(player.SteamUserId);
|
||||
}
|
||||
var player = (KeyValuePair<ulong, PlayerViewModel>)PlayerList.SelectedItem;
|
||||
_server.Multiplayer.KickPlayer(player.Key);
|
||||
}
|
||||
|
||||
private void BanButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var player = PlayerList.SelectedItem as IMyPlayer;
|
||||
if (player != null)
|
||||
{
|
||||
_server.Multiplayer.BanPlayer(player.SteamUserId);
|
||||
}
|
||||
var player = (KeyValuePair<ulong, PlayerViewModel>) PlayerList.SelectedItem;
|
||||
_server.Multiplayer.BanPlayer(player.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -42,8 +42,8 @@
|
||||
</TabItem>
|
||||
<TabItem Header="Chat/Players">
|
||||
<DockPanel>
|
||||
<local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" IsEnabled="False" />
|
||||
<local:ChatControl x:Name="Chat" IsEnabled="False" />
|
||||
<local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" />
|
||||
<local:ChatControl x:Name="Chat" />
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="Entity Manager">
|
||||
|
@@ -65,8 +65,6 @@ namespace Torch.Server
|
||||
private void BtnStart_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_config.Save();
|
||||
Chat.IsEnabled = true;
|
||||
PlayerList.IsEnabled = true;
|
||||
((Button) sender).IsEnabled = false;
|
||||
BtnStop.IsEnabled = true;
|
||||
ConfigControl.SaveConfig();
|
||||
@@ -76,8 +74,6 @@ namespace Torch.Server
|
||||
private void BtnStop_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_config.Save();
|
||||
Chat.IsEnabled = false;
|
||||
PlayerList.IsEnabled = false;
|
||||
((Button) sender).IsEnabled = false;
|
||||
//HACK: Uncomment when restarting is possible.
|
||||
//BtnStart.IsEnabled = true;
|
||||
|
62
Torch/Collections/ObservableDictionary.cs
Normal file
62
Torch/Collections/ObservableDictionary.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace Torch.Collections
|
||||
{
|
||||
public class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public new void Add(TKey key, TValue value)
|
||||
{
|
||||
base.Add(key, value);
|
||||
var kv = new KeyValuePair<TKey, TValue>(key, value);
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, kv));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public new bool Remove(TKey key)
|
||||
{
|
||||
if (!ContainsKey(key))
|
||||
return false;
|
||||
|
||||
var kv = new KeyValuePair<TKey, TValue>(key, this[key]);
|
||||
base.Remove(key);
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, kv));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
NotifyCollectionChangedEventHandler collectionChanged = CollectionChanged;
|
||||
if (collectionChanged != null)
|
||||
foreach (NotifyCollectionChangedEventHandler nh in collectionChanged.GetInvocationList())
|
||||
{
|
||||
var dispObj = nh.Target as DispatcherObject;
|
||||
|
||||
var dispatcher = dispObj?.Dispatcher;
|
||||
if (dispatcher != null && !dispatcher.CheckAccess())
|
||||
{
|
||||
dispatcher.BeginInvoke(
|
||||
(Action)(() => nh.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
|
||||
DispatcherPriority.DataBind);
|
||||
continue;
|
||||
}
|
||||
|
||||
nh.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
}
|
||||
}
|
@@ -76,6 +76,8 @@ namespace Torch.Commands
|
||||
{
|
||||
var cmdText = new string(message.Skip(1).ToArray());
|
||||
var command = Commands.GetCommand(cmdText, out string argText);
|
||||
if (command == null)
|
||||
return null;
|
||||
var cmdPath = string.Join(".", command.Path);
|
||||
|
||||
var splitArgs = Regex.Matches(argText, "(\"[^\"]+\"|\\S+)").Cast<Match>().Select(x => x.ToString().Replace("\"", "")).ToList();
|
||||
|
@@ -14,6 +14,7 @@ namespace Torch.Commands
|
||||
public class TorchCommands : CommandModule
|
||||
{
|
||||
[Command("help", "Displays help for a command")]
|
||||
[Permission(MyPromoteLevel.None)]
|
||||
public void Help()
|
||||
{
|
||||
var commandManager = ((TorchBase)Context.Torch).Commands;
|
||||
@@ -45,6 +46,7 @@ namespace Torch.Commands
|
||||
}
|
||||
|
||||
[Command("ver", "Shows the running Torch version.")]
|
||||
[Permission(MyPromoteLevel.None)]
|
||||
public void Version()
|
||||
{
|
||||
var ver = Context.Torch.TorchVersion;
|
||||
@@ -52,6 +54,7 @@ namespace Torch.Commands
|
||||
}
|
||||
|
||||
[Command("plugins", "Lists the currently loaded plugins.")]
|
||||
[Permission(MyPromoteLevel.None)]
|
||||
public void Plugins()
|
||||
{
|
||||
var plugins = Context.Torch.Plugins.Select(p => p.Name);
|
||||
@@ -59,7 +62,6 @@ namespace Torch.Commands
|
||||
}
|
||||
|
||||
[Command("stop", "Stops the server.")]
|
||||
[Permission(MyPromoteLevel.Admin)]
|
||||
public void Stop()
|
||||
{
|
||||
Context.Respond("Stopping server.");
|
||||
|
@@ -19,10 +19,10 @@ using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.ModAPI;
|
||||
using SharpDX.Toolkit.Collections;
|
||||
using SteamSDK;
|
||||
using Torch.API;
|
||||
using Torch.API.Managers;
|
||||
using Torch.Collections;
|
||||
using Torch.Commands;
|
||||
using Torch.ViewModels;
|
||||
using VRage.Game;
|
||||
@@ -126,6 +126,7 @@ namespace Torch.Managers
|
||||
|
||||
private void OnSessionLoaded()
|
||||
{
|
||||
_log.Info("Initializing Steam auth");
|
||||
MyMultiplayer.Static.ClientKicked += OnClientKicked;
|
||||
MyMultiplayer.Static.ClientLeft += OnClientLeft;
|
||||
|
||||
@@ -137,6 +138,7 @@ namespace Torch.Managers
|
||||
SteamServerAPI.Instance.GameServer.UserGroupStatus += UserGroupStatus;
|
||||
_members = (List<ulong>)typeof(MyDedicatedServerBase).GetField("m_members", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(MyMultiplayer.Static);
|
||||
_waitingForGroup = (HashSet<ulong>)typeof(MyDedicatedServerBase).GetField("m_waitingForGroup", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(MyMultiplayer.Static);
|
||||
_log.Info("Steam auth initialized");
|
||||
}
|
||||
|
||||
private void OnClientKicked(ulong steamId)
|
||||
@@ -146,9 +148,11 @@ namespace Torch.Managers
|
||||
|
||||
private void OnClientLeft(ulong steamId, ChatMemberStateChangeEnum stateChange)
|
||||
{
|
||||
_log.Info($"{GetSteamUsername(steamId)} disconnected ({(ConnectionState)stateChange}).");
|
||||
Players.TryGetValue(steamId, out PlayerViewModel vm);
|
||||
PlayerLeft?.Invoke(vm ?? new PlayerViewModel(steamId));
|
||||
if (vm == null)
|
||||
vm = new PlayerViewModel(steamId);
|
||||
_log.Info($"{vm.Name} ({vm.SteamId}) {(ConnectionState)stateChange}.");
|
||||
PlayerLeft?.Invoke(vm);
|
||||
Players.Remove(steamId);
|
||||
}
|
||||
|
||||
@@ -174,6 +178,7 @@ namespace Torch.Managers
|
||||
if (handle.Method.Name == "GameServer_ValidateAuthTicketResponse")
|
||||
{
|
||||
SteamServerAPI.Instance.GameServer.ValidateAuthTicketResponse -= handle as ValidateAuthTicketResponse;
|
||||
_log.Debug("Removed GameServer_ValidateAuthTicketResponse");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,6 +191,7 @@ namespace Torch.Managers
|
||||
if (handle.Method.Name == "GameServer_UserGroupStatus")
|
||||
{
|
||||
SteamServerAPI.Instance.GameServer.UserGroupStatus -= handle as UserGroupStatus;
|
||||
_log.Debug("Removed GameServer_UserGroupStatus");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,7 +305,8 @@ namespace Torch.Managers
|
||||
private void UserAccepted(ulong steamId)
|
||||
{
|
||||
typeof(MyDedicatedServerBase).GetMethod("UserAccepted", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(MyMultiplayer.Static, new object[] {steamId});
|
||||
var vm = new PlayerViewModel(steamId);
|
||||
var vm = new PlayerViewModel(steamId) {State = ConnectionState.Connected};
|
||||
_log.Info($"Player {vm.Name} joined ({vm.SteamId})");
|
||||
Players.Add(steamId, vm);
|
||||
PlayerJoined?.Invoke(vm);
|
||||
}
|
||||
|
@@ -129,7 +129,7 @@ namespace Torch.Managers
|
||||
throw new TypeLoadException($"Plugin '{type.FullName}' is missing a {nameof(PluginAttribute)}");
|
||||
|
||||
_log.Info($"Loading plugin {plugin.Name} ({plugin.Version})");
|
||||
plugin.StoragePath = new FileInfo(asm.Location).Directory.FullName;
|
||||
plugin.StoragePath = _torch.Config.InstancePath;
|
||||
Plugins.Add(plugin);
|
||||
|
||||
commands.RegisterPluginCommands(plugin);
|
||||
|
@@ -145,6 +145,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="ChatMessage.cs" />
|
||||
<Compile Include="Collections\KeyTree.cs" />
|
||||
<Compile Include="Collections\ObservableDictionary.cs" />
|
||||
<Compile Include="Collections\RollingAverage.cs" />
|
||||
<Compile Include="CommandLine.cs" />
|
||||
<Compile Include="Commands\CategoryAttribute.cs" />
|
||||
|
@@ -231,15 +231,41 @@ namespace Torch
|
||||
Log.Info($"Executing assembly: {Assembly.GetEntryAssembly().FullName}");
|
||||
Log.Info($"Executing directory: {AppDomain.CurrentDomain.BaseDirectory}");
|
||||
|
||||
MySession.OnLoading += () => SessionLoading?.Invoke();
|
||||
MySession.AfterLoading += () => SessionLoaded?.Invoke();
|
||||
MySession.OnUnloading += () => SessionUnloading?.Invoke();
|
||||
MySession.OnUnloaded += () => SessionUnloaded?.Invoke();
|
||||
MySession.OnLoading += OnSessionLoading;
|
||||
MySession.AfterLoading += OnSessionLoaded;
|
||||
MySession.OnUnloading += OnSessionUnloading;
|
||||
MySession.OnUnloaded += OnSessionUnloaded;
|
||||
RegisterVRagePlugin();
|
||||
|
||||
_init = true;
|
||||
}
|
||||
|
||||
private void OnSessionLoading()
|
||||
{
|
||||
Log.Debug("Session loading");
|
||||
foreach (var manager in _managers)
|
||||
manager.Init();
|
||||
SessionLoading?.Invoke();
|
||||
}
|
||||
|
||||
private void OnSessionLoaded()
|
||||
{
|
||||
Log.Debug("Session loaded");
|
||||
SessionLoaded?.Invoke();
|
||||
}
|
||||
|
||||
private void OnSessionUnloading()
|
||||
{
|
||||
Log.Debug("Session unloading");
|
||||
SessionUnloading?.Invoke();
|
||||
}
|
||||
|
||||
private void OnSessionUnloaded()
|
||||
{
|
||||
Log.Debug("Session unloaded");
|
||||
SessionUnloaded?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hook into the VRage plugin system for updates.
|
||||
/// </summary>
|
||||
@@ -269,8 +295,7 @@ namespace Torch
|
||||
/// <inheritdoc />
|
||||
public virtual void Init(object gameInstance)
|
||||
{
|
||||
foreach (var manager in _managers)
|
||||
manager.Init();
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@@ -3,8 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using SteamSDK;
|
||||
using Torch.API;
|
||||
using VRage.Replication;
|
||||
|
||||
namespace Torch.ViewModels
|
||||
{
|
||||
@@ -18,7 +20,7 @@ namespace Torch.ViewModels
|
||||
public PlayerViewModel(ulong steamId, string name = null)
|
||||
{
|
||||
SteamId = steamId;
|
||||
Name = name ?? SteamAPI.Instance?.Friends?.GetPersonaName(steamId) ?? "???";
|
||||
Name = name ?? ((MyDedicatedServerBase)MyMultiplayerMinimalBase.Instance).GetMemberName(steamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ namespace Torch
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propName = "")
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propName = "")
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
|
||||
}
|
||||
|
Reference in New Issue
Block a user