diff --git a/Torch.API/Managers/IMultiplayerManagerServer.cs b/Torch.API/Managers/IMultiplayerManagerServer.cs
index d2670c6..3571918 100644
--- a/Torch.API/Managers/IMultiplayerManagerServer.cs
+++ b/Torch.API/Managers/IMultiplayerManagerServer.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using VRage.Game.ModAPI;
namespace Torch.API.Managers
{
@@ -21,6 +22,25 @@ namespace Torch.API.Managers
///
void BanPlayer(ulong steamId, bool banned = true);
+ ///
+ /// Promotes user if possible.
+ ///
+ ///
+ void PromoteUser(ulong steamId);
+
+ ///
+ /// Demotes user if possible.
+ ///
+ ///
+ void DemoteUser(ulong steamId);
+
+ ///
+ /// Gets a user's promote level.
+ ///
+ ///
+ ///
+ MyPromoteLevel GetUserPromoteLevel(ulong steamId);
+
///
/// List of the banned SteamID's
///
@@ -42,5 +62,10 @@ namespace Torch.API.Managers
/// Raised when a player is banned or unbanned. Passes SteamID of player, and true if banned, false if unbanned.
///
event Action PlayerBanned;
+
+ ///
+ /// Raised when a player is promoted or demoted. Passes SteamID of player, and new promote level.
+ ///
+ event Action PlayerPromoted;
}
}
diff --git a/Torch.Client/Manager/MultiplayerManagerLobby.cs b/Torch.Client/Manager/MultiplayerManagerLobby.cs
index 731f00e..2b5ad11 100644
--- a/Torch.Client/Manager/MultiplayerManagerLobby.cs
+++ b/Torch.Client/Manager/MultiplayerManagerLobby.cs
@@ -4,9 +4,11 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Multiplayer;
+using Sandbox.Game.World;
using Torch.API;
using Torch.API.Managers;
using Torch.Managers;
+using VRage.Game.ModAPI;
namespace Torch.Client.Manager
{
@@ -24,6 +26,34 @@ namespace Torch.Client.Manager
///
public void BanPlayer(ulong steamId, bool banned = true) => Torch.Invoke(() => MyMultiplayer.Static.BanClient(steamId, banned));
+ ///
+ public void PromoteUser(ulong steamId)
+ {
+ Torch.Invoke(() =>
+ {
+ var p = MySession.Static.GetUserPromoteLevel(steamId);
+ if (p < MyPromoteLevel.Admin) //cannot promote to owner by design
+ MySession.Static.SetUserPromoteLevel(steamId, p + 1);
+ });
+ }
+
+ ///
+ public void DemoteUser(ulong steamId)
+ {
+ Torch.Invoke(() =>
+ {
+ var p = MySession.Static.GetUserPromoteLevel(steamId);
+ if (p > MyPromoteLevel.None && p < MyPromoteLevel.Owner) //owner cannot be demoted by design
+ MySession.Static.SetUserPromoteLevel(steamId, p - 1);
+ });
+ }
+
+ ///
+ public MyPromoteLevel GetUserPromoteLevel(ulong steamId)
+ {
+ return MySession.Static.GetUserPromoteLevel(steamId);
+ }
+
///
public bool IsBanned(ulong steamId) => false;
@@ -41,6 +71,13 @@ namespace Torch.Client.Manager
remove => throw new NotImplementedException();
}
+ ///
+ public event Action PlayerPromoted
+ {
+ add => throw new NotImplementedException();
+ remove => throw new NotImplementedException();
+ }
+
///
public override void Attach()
{
diff --git a/Torch.Server/Managers/MultiplayerManagerDedicated.cs b/Torch.Server/Managers/MultiplayerManagerDedicated.cs
index 4a704bd..002938b 100644
--- a/Torch.Server/Managers/MultiplayerManagerDedicated.cs
+++ b/Torch.Server/Managers/MultiplayerManagerDedicated.cs
@@ -10,6 +10,7 @@ using NLog.Fluent;
using Sandbox;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
+using Sandbox.Game.Gui;
using Sandbox.Game.World;
using Steamworks;
using Torch.API;
@@ -62,6 +63,36 @@ namespace Torch.Server.Managers
});
}
+ ///
+ public void PromoteUser(ulong steamId)
+ {
+ Torch.Invoke(() =>
+ {
+ var p = MySession.Static.GetUserPromoteLevel(steamId);
+ if (p < MyPromoteLevel.Admin) //cannot promote to owner by design
+ //MySession.Static.SetUserPromoteLevel(steamId, p + 1);
+ MyGuiScreenPlayers.PromoteImplementation(steamId, true);
+ });
+ }
+
+ ///
+ public void DemoteUser(ulong steamId)
+ {
+ Torch.Invoke(() =>
+ {
+ var p = MySession.Static.GetUserPromoteLevel(steamId);
+ if (p > MyPromoteLevel.None && p < MyPromoteLevel.Owner) //owner cannot be demoted by design
+ //MySession.Static.SetUserPromoteLevel(steamId, p - 1);
+ MyGuiScreenPlayers.PromoteImplementation(steamId, false);
+ });
+ }
+
+ ///
+ public MyPromoteLevel GetUserPromoteLevel(ulong steamId)
+ {
+ return MySession.Static.GetUserPromoteLevel(steamId);
+ }
+
internal void RaiseClientBanned(ulong user, bool banned)
{
PlayerBanned?.Invoke(user, banned);
@@ -87,6 +118,14 @@ namespace Torch.Server.Managers
///
public event Action PlayerBanned;
+ ///
+ public event Action PlayerPromoted;
+
+ internal void RaisePromoteChanged(ulong steamId, MyPromoteLevel level)
+ {
+ PlayerPromoted?.Invoke(steamId, level);
+ }
+
///
public override void Attach()
{
diff --git a/Torch.Server/Patches/PromotePatch.cs b/Torch.Server/Patches/PromotePatch.cs
new file mode 100644
index 0000000..7de35bf
--- /dev/null
+++ b/Torch.Server/Patches/PromotePatch.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using NLog;
+using Sandbox.Game.World;
+using Torch.Managers.PatchManager;
+using VRage.Game.ModAPI;
+using Torch.API.Managers;
+using Torch.Server.Managers;
+
+namespace Torch.Patches
+{
+ [PatchShim]
+ internal static class PromotePatch
+ {
+ private static Logger _log = LogManager.GetCurrentClassLogger();
+ private static IMultiplayerManagerServer _backing;
+
+ private static IMultiplayerManagerServer ServerManager => _backing ?? (_backing = TorchBase.Instance?.CurrentSession?.Managers.GetManager());
+
+ public static void Patch(PatchContext ctx)
+ {
+ _log.Info("patching promote");
+ ctx.GetPattern(typeof(MySession).GetMethod("OnPromoteLevelSet", BindingFlags.NonPublic | BindingFlags.Static)).Prefixes.Add(typeof(PromotePatch).GetMethod(nameof(PromotePrefix)));
+ }
+
+ public static void PromotePrefix(ulong steamId, MyPromoteLevel level)
+ {
+ if (ServerManager is MultiplayerManagerDedicated d)
+ d.RaisePromoteChanged(steamId, level);
+ else
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/Torch.Server/Torch.Server.csproj b/Torch.Server/Torch.Server.csproj
index 545f740..ed9e330 100644
--- a/Torch.Server/Torch.Server.csproj
+++ b/Torch.Server/Torch.Server.csproj
@@ -227,6 +227,7 @@
+
diff --git a/Torch.Server/Views/ChatControl.xaml.cs b/Torch.Server/Views/ChatControl.xaml.cs
index 7c7755c..242a8d1 100644
--- a/Torch.Server/Views/ChatControl.xaml.cs
+++ b/Torch.Server/Views/ChatControl.xaml.cs
@@ -46,9 +46,9 @@ namespace Torch.Server
private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
- _log.Info($"VisibleChanged: {IsVisible}");
if (IsVisible)
{
+ //I hate this and I hate myself. You should hate me too
Task.Run(() =>
{
Thread.Sleep(100);
diff --git a/Torch.Server/Views/PlayerListControl.xaml b/Torch.Server/Views/PlayerListControl.xaml
index 3a888cc..943f2f5 100644
--- a/Torch.Server/Views/PlayerListControl.xaml
+++ b/Torch.Server/Views/PlayerListControl.xaml
@@ -8,12 +8,14 @@
+
+
-
+
diff --git a/Torch.Server/Views/PlayerListControl.xaml.cs b/Torch.Server/Views/PlayerListControl.xaml.cs
index bc26eb6..f59f670 100644
--- a/Torch.Server/Views/PlayerListControl.xaml.cs
+++ b/Torch.Server/Views/PlayerListControl.xaml.cs
@@ -16,6 +16,7 @@ using NLog;
using Torch;
using Sandbox;
using Sandbox.Engine.Multiplayer;
+using Sandbox.Game.Gui;
using Sandbox.Game.Multiplayer;
using Sandbox.Game.World;
using Sandbox.ModAPI;
@@ -24,6 +25,7 @@ using Torch.API.Managers;
using Torch.API.Session;
using Torch.Managers;
using Torch.Server.Managers;
+using Torch.Utils;
using Torch.ViewModels;
using VRage.Game.ModAPI;
@@ -37,16 +39,22 @@ namespace Torch.Server
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private ITorchServer _server;
+ private IMultiplayerManagerServer _mpServer;
public PlayerListControl()
{
InitializeComponent();
}
+ private void OnPlayerPromoted(ulong arg1, MyPromoteLevel arg2)
+ {
+ Dispatcher.InvokeAsync(() => PlayerList.Items.Refresh());
+ }
+
public void BindServer(ITorchServer server)
{
_server = server;
- _server.Initialized += Server_Initialized ;
+ _server.Initialized += Server_Initialized;
}
private void Server_Initialized(ITorchServer obj)
@@ -61,6 +69,8 @@ namespace Torch.Server
{
case TorchSessionState.Loaded:
Dispatcher.InvokeAsync(() => DataContext = _server?.CurrentSession?.Managers.GetManager());
+ _mpServer = _server.CurrentSession.Managers.GetManager();
+ _mpServer.PlayerPromoted += OnPlayerPromoted;
break;
case TorchSessionState.Unloading:
Dispatcher.InvokeAsync(() => DataContext = null);
@@ -93,5 +103,31 @@ namespace Torch.Server
_log.Warn(ex);
}
}
+
+ private void PromoteButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ var player = (KeyValuePair)PlayerList.SelectedItem;
+ try
+ {
+ _server.CurrentSession.Managers.GetManager().PromoteUser(player.Key);
+ }
+ catch (Exception ex)
+ {
+ _log.Warn(ex);
+ }
+ }
+
+ private void DemoteButton_OnClick(object sender, RoutedEventArgs e)
+ {
+ var player = (KeyValuePair)PlayerList.SelectedItem;
+ try
+ {
+ _server.CurrentSession.Managers.GetManager().DemoteUser(player.Key);
+ }
+ catch (Exception ex)
+ {
+ _log.Warn(ex);
+ }
+ }
}
}
diff --git a/Torch/ViewModels/PlayerViewModel.cs b/Torch/ViewModels/PlayerViewModel.cs
index 857bb41..f070a3a 100644
--- a/Torch/ViewModels/PlayerViewModel.cs
+++ b/Torch/ViewModels/PlayerViewModel.cs
@@ -4,7 +4,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Sandbox.Engine.Multiplayer;
+using Sandbox.Game.World;
using Torch.API;
+using VRage.Game.ModAPI;
using VRage.Replication;
namespace Torch.ViewModels
@@ -15,6 +17,19 @@ namespace Torch.ViewModels
public string Name { get; }
private ConnectionState _state;
public ConnectionState State { get => _state; set { _state = value; OnPropertyChanged(); } }
+ public MyPromoteLevel PromoteLevel => MySession.Static.GetUserPromoteLevel(SteamId);
+
+ public string PromotedName
+ {
+ get
+ {
+ var p = PromoteLevel;
+ if (p <= MyPromoteLevel.None)
+ return Name;
+ else
+ return $"{Name} ({p})";
+ }
+ }
public PlayerViewModel(ulong steamId, string name = null)
{