Finish chat/players functionality, refactor logic to non-static classes with a single static entry point, add data binding
This commit is contained in:
@@ -5,11 +5,11 @@ VisualStudioVersion = 15.0.25618.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piston", "Piston\Piston.csproj", "{7E01635C-3B67-472E-BCD6-C5539564F214}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PistonAPI", "PistonAPI\PistonAPI.csproj", "{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piston.API", "PistonAPI\Piston.API.csproj", "{FBA5D932-6254-4A1E-BAF4-E229FA94E3C2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PistonClient", "PistonClient\PistonClient.csproj", "{E36DF745-260B-4956-A2E8-09F08B2E7161}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piston.Client", "PistonClient\Piston.Client.csproj", "{E36DF745-260B-4956-A2E8-09F08B2E7161}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PistonServer", "PistonServer\PistonServer.csproj", "{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piston.Server", "PistonServer\Piston.Server.csproj", "{CA50886B-7B22-4CD8-93A0-C06F38D4F77D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
37
Piston/MTObservableCollection.cs
Normal file
37
Piston/MTObservableCollection.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace Piston
|
||||
{
|
||||
public class MTObservableCollection<T> : ObservableCollection<T>
|
||||
{
|
||||
public override event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
NotifyCollectionChangedEventHandler collectionChanged = CollectionChanged;
|
||||
if (collectionChanged != null)
|
||||
foreach (NotifyCollectionChangedEventHandler nh in collectionChanged.GetInvocationList())
|
||||
{
|
||||
var dispObj = nh.Target as DispatcherObject;
|
||||
|
||||
Dispatcher 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
Piston/ObservableType.cs
Normal file
20
Piston/ObservableType.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Piston
|
||||
{
|
||||
public class ObservableType : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propName = "")
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
|
||||
}
|
||||
}
|
||||
}
|
@@ -111,17 +111,20 @@
|
||||
<Reference Include="VRage.Scripting">
|
||||
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.Scripting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Logger.cs" />
|
||||
<Compile Include="MTObservableCollection.cs" />
|
||||
<Compile Include="MyPlayerCollectionExtensions.cs" />
|
||||
<Compile Include="ObservableType.cs" />
|
||||
<Compile Include="PluginManager.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PistonAPI\PistonAPI.csproj">
|
||||
<ProjectReference Include="..\PistonAPI\Piston.API.csproj">
|
||||
<Project>{fba5d932-6254-4a1e-baf4-e229fa94e3c2}</Project>
|
||||
<Name>PistonAPI</Name>
|
||||
<Name>Piston.API</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using PistonAPI;
|
||||
using Piston.API;
|
||||
using Sandbox;
|
||||
using VRage.Plugins;
|
||||
using VRage.Collections;
|
||||
@@ -17,13 +17,12 @@ namespace Piston
|
||||
{
|
||||
//TODO: Disable reloading if the plugin has static elements because they prevent a full reload.
|
||||
|
||||
public static PluginManager Static { get; } = new PluginManager();
|
||||
public ListReader<IPlugin> Plugins => MyPlugins.Plugins;
|
||||
|
||||
private List<IPlugin> _plugins;
|
||||
public const string PluginDir = "Plugins";
|
||||
|
||||
private PluginManager()
|
||||
public PluginManager()
|
||||
{
|
||||
if (!Directory.Exists(PluginDir))
|
||||
Directory.CreateDirectory(PluginDir);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
namespace PistonAPI
|
||||
namespace Piston.API
|
||||
{
|
||||
public interface IEnvironmentInfo
|
||||
{
|
||||
|
@@ -6,7 +6,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Plugins;
|
||||
|
||||
namespace PistonAPI
|
||||
namespace Piston.API
|
||||
{
|
||||
public interface IPistonPlugin : IPlugin
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace PistonAPI
|
||||
namespace Piston.API
|
||||
{
|
||||
public interface IServerControls
|
||||
{
|
||||
|
@@ -5,7 +5,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace PistonAPI
|
||||
namespace Piston.API
|
||||
{
|
||||
public static class PistonAPI
|
||||
{
|
||||
|
@@ -1,15 +1,27 @@
|
||||
<UserControl x:Class="PistonServer.ChatControl"
|
||||
<UserControl x:Class="Piston.Server.ChatControl"
|
||||
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:local="clr-namespace:PistonServer"
|
||||
xmlns:local="clr-namespace:Piston.Server"
|
||||
mc:Ignorable="d">
|
||||
<DockPanel>
|
||||
<DockPanel DockPanel.Dock="Bottom">
|
||||
<Button x:Name="Send" Content="Send" DockPanel.Dock="Right" Width="50" Margin="5,5,5,5" Click="SendButton_Click"></Button>
|
||||
<TextBox x:Name="Message" DockPanel.Dock="Left" Margin="5,5,5,5" KeyDown="Message_OnKeyDown"></TextBox>
|
||||
</DockPanel>
|
||||
<TextBox x:Name="Chat" IsReadOnly="True" Margin="5,5,5,5"/>
|
||||
<ListView x:Name="ChatItems" Margin="5,5,5,5">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<WrapPanel>
|
||||
<TextBlock Text="{Binding Time}"/>
|
||||
<TextBlock Text=" "/>
|
||||
<TextBlock Text="{Binding Sender.Name}" FontWeight="Bold"/>
|
||||
<TextBlock Text=": "/>
|
||||
<TextBlock Text="{Binding Message}"/>
|
||||
</WrapPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
|
@@ -19,45 +19,17 @@ using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using SteamSDK;
|
||||
|
||||
namespace PistonServer
|
||||
namespace Piston.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ChatControl.xaml
|
||||
/// </summary>
|
||||
public partial class ChatControl : UserControl
|
||||
{
|
||||
public event Action<string> MessageEntered;
|
||||
public ChatControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
ServerManager.Static.SessionReady += InitChatHandler;
|
||||
}
|
||||
|
||||
public void InitChatHandler()
|
||||
{
|
||||
MyMultiplayer.Static.ChatMessageReceived += MessageReceived;
|
||||
}
|
||||
|
||||
public void MessageReceived(ulong steamId, string message, ChatEntryTypeEnum chatType)
|
||||
{
|
||||
//Messages sent from server loop back around.
|
||||
if (steamId == MyMultiplayer.Static.ServerId)
|
||||
return;
|
||||
|
||||
var name = MyMultiplayer.Static.GetMemberName(steamId);
|
||||
Dispatcher.Invoke(() => AddMessage(name, message), DispatcherPriority.Normal);
|
||||
}
|
||||
|
||||
public void AddMessage(string sender, string message)
|
||||
{
|
||||
Chat.Text += $"{DateTime.Now.ToLongTimeString()} | {sender}: {message}\n";
|
||||
Program.UserInterface.Players.RefreshNames();
|
||||
}
|
||||
|
||||
public void SendMessage(string message)
|
||||
{
|
||||
MyMultiplayer.Static.SendChatMessage(message);
|
||||
Dispatcher.Invoke(() => AddMessage("Server", message));
|
||||
ChatItems.ItemsSource = PistonServer.Multiplayer.ChatView;
|
||||
}
|
||||
|
||||
private void SendButton_Click(object sender, RoutedEventArgs e)
|
||||
@@ -65,18 +37,18 @@ namespace PistonServer
|
||||
OnMessageEntered();
|
||||
}
|
||||
|
||||
private void OnMessageEntered()
|
||||
{
|
||||
var text = Message.Text;
|
||||
SendMessage(text);
|
||||
MessageEntered?.Invoke(Message.Text);
|
||||
Message.Text = "";
|
||||
}
|
||||
|
||||
private void Message_OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
OnMessageEntered();
|
||||
}
|
||||
|
||||
private void OnMessageEntered()
|
||||
{
|
||||
//Can't use Message.Text directly because of object ownership in WPF.
|
||||
var text = Message.Text;
|
||||
PistonServer.Multiplayer.SendMessage(text);
|
||||
Message.Text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,25 +1,25 @@
|
||||
<Window x:Class="PistonServer.MainWindow"
|
||||
<Window x:Class="Piston.Server.MainWindow"
|
||||
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:PistonServer"
|
||||
xmlns:local="clr-namespace:Piston.Server"
|
||||
mc:Ignorable="d"
|
||||
Title="Piston" Height="600" Width="900">
|
||||
<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"/>
|
||||
<Button x:Name="BtnStop" Content="Stop" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnStop_Click" IsEnabled="False"/>
|
||||
<Button x:Name="BtnRestart" Content="Restart" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left"/>
|
||||
<Button x:Name="BtnRestart" Content="Restart" Height="24" Width="75" Margin="5,0,5,0" HorizontalAlignment="Left" Click="BtnRestart_Click"/>
|
||||
<Label x:Name="LabelSimulation" Content="Sim Ratio: 0.00"/>
|
||||
<Label x:Name="LabelUptime" Content="Uptime: 0d 0h 0m"/>
|
||||
</StackPanel>
|
||||
<TabControl x:Name="TabControl" DockPanel.Dock="Bottom" Margin="5,0,5,5">
|
||||
<TabItem Header="Configuration">
|
||||
</TabItem>
|
||||
<TabItem Header="Chat/Players">
|
||||
<TabItem Header="Chat/PlayerList">
|
||||
<DockPanel>
|
||||
<local:PlayersControl x:Name="Players" DockPanel.Dock="Right" Width="250" IsEnabled="False"></local:PlayersControl>
|
||||
<local:PlayerListControl x:Name="PlayerList" DockPanel.Dock="Right" Width="250" IsEnabled="False"/>
|
||||
<local:ChatControl x:Name="Chat" IsEnabled="False"/>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
@@ -30,3 +30,4 @@
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
||||
|
@@ -17,7 +17,7 @@ using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace PistonServer
|
||||
namespace Piston.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
@@ -57,27 +57,32 @@ namespace PistonServer
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
Chat.IsEnabled = true;
|
||||
Players.IsEnabled = true;
|
||||
PlayerList.IsEnabled = true;
|
||||
((Button) sender).IsEnabled = false;
|
||||
BtnStop.IsEnabled = true;
|
||||
uiUpdate.Start();
|
||||
ServerManager.Static.StartServerThread();
|
||||
PistonServer.Server.StartServerThread();
|
||||
}
|
||||
|
||||
private void BtnStop_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Chat.IsEnabled = false;
|
||||
Players.IsEnabled = false;
|
||||
PlayerList.IsEnabled = false;
|
||||
((Button) sender).IsEnabled = false;
|
||||
//HACK: Uncomment when restarting is possible.
|
||||
//BtnStart.IsEnabled = true;
|
||||
uiUpdate.Stop();
|
||||
ServerManager.Static.StopServer();
|
||||
PistonServer.Server.StopServer();
|
||||
}
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
ServerManager.Static.StopServer();
|
||||
PistonServer.Server.StopServer();
|
||||
}
|
||||
|
||||
private void BtnRestart_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Program.FullRestart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
187
PistonServer/MultiplayerManager.cs
Normal file
187
PistonServer/MultiplayerManager.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Threading;
|
||||
using Piston;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using SteamSDK;
|
||||
using VRage.Library.Collections;
|
||||
|
||||
namespace Piston.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a proxy to the game's multiplayer-related functions.
|
||||
/// </summary>
|
||||
public class MultiplayerManager
|
||||
{
|
||||
public event Action<PlayerInfo> PlayerJoined;
|
||||
public event Action<PlayerInfo> PlayerLeft;
|
||||
public event Action<ChatItemInfo> ChatMessageReceived;
|
||||
|
||||
public MTObservableCollection<PlayerInfo> PlayersView { get; } = new MTObservableCollection<PlayerInfo>();
|
||||
public MTObservableCollection<ChatItemInfo> ChatView { get; } = new MTObservableCollection<ChatItemInfo>();
|
||||
public PlayerInfo LocalPlayer { get; private set; }
|
||||
|
||||
internal MultiplayerManager(ServerManager serverManager)
|
||||
{
|
||||
serverManager.SessionLoaded += OnSessionLoaded;
|
||||
}
|
||||
|
||||
public void KickPlayer(ulong steamId) => MyMultiplayer.Static.KickClient(steamId);
|
||||
|
||||
public void BanPlayer(ulong steamId, bool banned = true) => MyMultiplayer.Static.BanClient(steamId, banned);
|
||||
|
||||
/// <summary>
|
||||
/// Send a message in chat.
|
||||
/// </summary>
|
||||
public void SendMessage(string message)
|
||||
{
|
||||
MyMultiplayer.Static.SendChatMessage(message);
|
||||
ChatView.Add(new ChatItemInfo(LocalPlayer, message));
|
||||
}
|
||||
|
||||
private void OnSessionLoaded()
|
||||
{
|
||||
LocalPlayer = new PlayerInfo(MyMultiplayer.Static.ServerId) { Name = "Server", State = ConnectionState.Connected };
|
||||
|
||||
MyMultiplayer.Static.ChatMessageReceived += OnChatMessage;
|
||||
MyMultiplayer.Static.ClientKicked += OnClientKicked;
|
||||
MyMultiplayer.Static.ClientLeft += OnClientLeft;
|
||||
MySession.Static.Players.PlayerRequesting += OnPlayerRequesting;
|
||||
}
|
||||
|
||||
private void OnChatMessage(ulong steamId, string message, ChatEntryTypeEnum chatType)
|
||||
{
|
||||
var player = PlayersView.FirstOrDefault(p => p.SteamId == steamId);
|
||||
if (player == null || player == LocalPlayer)
|
||||
return;
|
||||
|
||||
var info = new ChatItemInfo(player, message);
|
||||
ChatView.Add(info);
|
||||
ChatMessageReceived?.Invoke(info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a client logs in and hits the respawn screen.
|
||||
/// </summary>
|
||||
private void OnPlayerRequesting(PlayerRequestArgs args)
|
||||
{
|
||||
var steamId = args.PlayerId.SteamId;
|
||||
var player = new PlayerInfo(steamId) {State = ConnectionState.Connected};
|
||||
PlayersView.Add(player);
|
||||
PlayerJoined?.Invoke(player);
|
||||
|
||||
}
|
||||
|
||||
private void OnClientKicked(ulong steamId)
|
||||
{
|
||||
OnClientLeft(steamId, ChatMemberStateChangeEnum.Kicked);
|
||||
}
|
||||
|
||||
private void OnClientLeft(ulong steamId, ChatMemberStateChangeEnum stateChange)
|
||||
{
|
||||
var player = PlayersView.FirstOrDefault(p => p.SteamId == steamId);
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
player.State = (ConnectionState)stateChange;
|
||||
PlayersView.Remove(player);
|
||||
PlayerLeft?.Invoke(player);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores player information in an observable format.
|
||||
/// </summary>
|
||||
public class PlayerInfo : ObservableType
|
||||
{
|
||||
private ulong _steamId;
|
||||
private string _name;
|
||||
private ConnectionState _state;
|
||||
|
||||
public ulong SteamId
|
||||
{
|
||||
get { return _steamId; }
|
||||
set { _steamId = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set { _name = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public ConnectionState State
|
||||
{
|
||||
get { return _state; }
|
||||
set { _state = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public PlayerInfo(ulong steamId)
|
||||
{
|
||||
_steamId = steamId;
|
||||
_name = MyMultiplayer.Static.GetMemberName(steamId);
|
||||
_state = ConnectionState.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatItemInfo : ObservableType
|
||||
{
|
||||
private PlayerInfo _sender;
|
||||
private string _message;
|
||||
private DateTime _timestamp;
|
||||
|
||||
public PlayerInfo Sender
|
||||
{
|
||||
get { return _sender; }
|
||||
set { _sender = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public string Message
|
||||
{
|
||||
get { return _message; }
|
||||
set { _message = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get { return _timestamp; }
|
||||
set { _timestamp = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public string Time => Timestamp.ToShortTimeString();
|
||||
|
||||
public ChatItemInfo(PlayerInfo sender, string message)
|
||||
{
|
||||
_sender = sender;
|
||||
_message = message;
|
||||
_timestamp = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies a player's current connection state.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ConnectionState
|
||||
{
|
||||
Unknown,
|
||||
Connected = 1,
|
||||
Left = 2,
|
||||
Disconnected = 4,
|
||||
Kicked = 8,
|
||||
Banned = 16,
|
||||
}
|
||||
}
|
@@ -112,8 +112,10 @@
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="PlayersControl.xaml.cs">
|
||||
<DependentUpon>PlayersControl.xaml</DependentUpon>
|
||||
<Compile Include="MultiplayerManager.cs" />
|
||||
<Compile Include="PistonServer.cs" />
|
||||
<Compile Include="PlayerListControl.xaml.cs">
|
||||
<DependentUpon>PlayerListControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="ServerManager.cs" />
|
||||
@@ -157,7 +159,7 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="PlayersControl.xaml">
|
||||
<Page Include="PlayerListControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
42
PistonServer/PistonServer.cs
Normal file
42
PistonServer/PistonServer.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Piston;
|
||||
|
||||
namespace Piston.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry point for all Piston server functionality.
|
||||
/// </summary>
|
||||
public static class PistonServer
|
||||
{
|
||||
public static ServerManager Server;
|
||||
public static MultiplayerManager Multiplayer;
|
||||
public static PluginManager Plugins;
|
||||
|
||||
private static bool _init;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (!_init)
|
||||
{
|
||||
Logger.Write("Initializing Piston");
|
||||
_init = true;
|
||||
Server = new ServerManager();
|
||||
Multiplayer = new MultiplayerManager(Server);
|
||||
Plugins = new PluginManager();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
Logger.Write("Resetting Piston");
|
||||
Server = null;
|
||||
Multiplayer = null;
|
||||
Plugins = null;
|
||||
_init = false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,15 +1,26 @@
|
||||
<UserControl x:Class="PistonServer.PlayersControl"
|
||||
<UserControl x:Class="Piston.Server.PlayerListControl"
|
||||
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:local="clr-namespace:PistonServer"
|
||||
xmlns:local="clr-namespace:Piston.Server"
|
||||
mc:Ignorable="d">
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Bottom">
|
||||
<Button x:Name="KickButton" Content="Kick" Margin="5,5,5,5" Click="KickButton_Click"/>
|
||||
<Button x:Name="BanButton" Content="Ban" Margin="5,5,5,5" Click="BanButton_Click"/>
|
||||
</StackPanel>
|
||||
<ListBox x:Name="PlayerList" DockPanel.Dock="Top" Margin="5,5,5,5"/>
|
||||
<ListView x:Name="PlayerList" DockPanel.Dock="Top" Margin="5,5,5,5">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<WrapPanel>
|
||||
<TextBlock Text="{Binding Name}" FontWeight="Bold"/>
|
||||
<TextBlock Text=" ("/>
|
||||
<TextBlock Text="{Binding State}"/>
|
||||
<TextBlock Text=")"/>
|
||||
</WrapPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</DockPanel>
|
||||
</UserControl>
|
53
PistonServer/PlayerListControl.xaml.cs
Normal file
53
PistonServer/PlayerListControl.xaml.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using Piston;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using SteamSDK;
|
||||
|
||||
namespace Piston.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for PlayerListControl.xaml
|
||||
/// </summary>
|
||||
public partial class PlayerListControl : UserControl
|
||||
{
|
||||
public PlayerListControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
PlayerList.ItemsSource = PistonServer.Multiplayer.PlayersView;
|
||||
}
|
||||
|
||||
private void KickButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var player = PlayerList.SelectedItem as PlayerInfo;
|
||||
if (player != null)
|
||||
{
|
||||
PistonServer.Multiplayer.KickPlayer(player.SteamId);
|
||||
}
|
||||
}
|
||||
|
||||
private void BanButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var player = PlayerList.SelectedItem as PlayerInfo;
|
||||
if (player != null)
|
||||
{
|
||||
PistonServer.Multiplayer.BanPlayer(player.SteamId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,114 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using Piston;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using SteamSDK;
|
||||
|
||||
namespace PistonServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for PlayersControl.xaml
|
||||
/// </summary>
|
||||
public partial class PlayersControl : UserControl
|
||||
{
|
||||
public PlayersControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
ServerManager.Static.SessionReady += Static_SessionReady;
|
||||
}
|
||||
|
||||
public void RefreshNames()
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
foreach (var player in PlayerList.Items)
|
||||
{
|
||||
var p = (PlayerItem)player;
|
||||
p.Name = MyMultiplayer.Static.GetMemberName(p.SteamId);
|
||||
}
|
||||
|
||||
PlayerList.Items.Refresh();
|
||||
});
|
||||
}
|
||||
|
||||
private void Static_SessionReady()
|
||||
{
|
||||
MyMultiplayer.Static.ClientKicked += OnClientKicked;
|
||||
MyMultiplayer.Static.ClientLeft += OnClientLeft;
|
||||
MySession.Static.Players.PlayerRequesting += OnPlayerRequesting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a client logs in and hits the respawn screen.
|
||||
/// </summary>
|
||||
private void OnPlayerRequesting(PlayerRequestArgs args)
|
||||
{
|
||||
var steamId = args.PlayerId.SteamId;
|
||||
var player = new PlayerItem { Name = MyMultiplayer.Static.GetMemberName(steamId), SteamId = steamId };
|
||||
Program.UserInterface.Chat.SendMessage($"{player.Name} connected.");
|
||||
Dispatcher.Invoke(() => PlayerList.Items.Add(player));
|
||||
}
|
||||
private void OnClientKicked(ulong steamId)
|
||||
{
|
||||
OnClientLeft(steamId, ChatMemberStateChangeEnum.Kicked);
|
||||
}
|
||||
|
||||
private void OnClientLeft(ulong steamId, ChatMemberStateChangeEnum stateChange)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
var player = PlayerList.Items.Cast<PlayerItem>().FirstOrDefault(x => x.SteamId == steamId);
|
||||
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
Program.UserInterface.Chat.SendMessage($"{player.Name} {stateChange.ToString().ToLower()}.");
|
||||
PlayerList.Items.Remove(player);
|
||||
});
|
||||
}
|
||||
|
||||
public class PlayerItem
|
||||
{
|
||||
public ulong SteamId;
|
||||
public string Name;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name} ({SteamId})";
|
||||
}
|
||||
}
|
||||
|
||||
private void KickButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (PlayerList.SelectedItem == null)
|
||||
return;
|
||||
|
||||
var player = (PlayerItem)PlayerList.SelectedItem;
|
||||
MyMultiplayer.Static.KickClient(player.SteamId);
|
||||
}
|
||||
|
||||
private void BanButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (PlayerList.SelectedItem == null)
|
||||
return;
|
||||
|
||||
var player = (PlayerItem)PlayerList.SelectedItem;
|
||||
MyMultiplayer.Static.BanClient(player.SteamId, true);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -9,7 +10,7 @@ using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using Piston;
|
||||
|
||||
namespace PistonServer
|
||||
namespace Piston.Server
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
@@ -19,18 +20,19 @@ namespace PistonServer
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
ServerManager.Static.RunArgs = new[] { "-console" };
|
||||
Logger.Write("Initializing");
|
||||
PistonServer.Server.RunArgs = new[] { "-console" };
|
||||
MainDispatcher = Dispatcher.CurrentDispatcher;
|
||||
Console.WriteLine(MainDispatcher.Thread.ManagedThreadId);
|
||||
#if DEBUG
|
||||
Directory.SetCurrentDirectory(@"C:\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\DedicatedServer64");
|
||||
#endif
|
||||
|
||||
if (args.Contains("-nogui"))
|
||||
ServerManager.Static.StartServer();
|
||||
|
||||
PistonServer.Server.StartServer();
|
||||
else
|
||||
StartUI();
|
||||
|
||||
if (args.Contains("-autostart") && !PistonServer.Server.Running)
|
||||
PistonServer.Server.StartServerThread();
|
||||
|
||||
Dispatcher.Run();
|
||||
}
|
||||
|
||||
@@ -39,5 +41,12 @@ namespace PistonServer
|
||||
Thread.CurrentThread.Name = "UI Thread";
|
||||
UserInterface.Show();
|
||||
}
|
||||
|
||||
public static void FullRestart()
|
||||
{
|
||||
PistonServer.Server.StopServer();
|
||||
Process.Start("PistonServer.exe", "-autostart");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
PistonServer/Properties/Resources.Designer.cs
generated
2
PistonServer/Properties/Resources.Designer.cs
generated
@@ -8,7 +8,7 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PistonServer.Properties {
|
||||
namespace Piston.Server.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
|
2
PistonServer/Properties/Settings.Designer.cs
generated
2
PistonServer/Properties/Settings.Designer.cs
generated
@@ -8,7 +8,7 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PistonServer.Properties {
|
||||
namespace Piston.Server.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
|
@@ -13,20 +13,21 @@ using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using VRage.Profiler;
|
||||
|
||||
namespace PistonServer
|
||||
namespace Piston.Server
|
||||
{
|
||||
public class ServerManager
|
||||
{
|
||||
public static ServerManager Static { get; } = new ServerManager();
|
||||
public Thread ServerThread { get; private set; }
|
||||
public string[] RunArgs { get; set; } = new string[0];
|
||||
public bool Running { get; private set; }
|
||||
|
||||
public event Action SessionLoading;
|
||||
public event Action SessionReady;
|
||||
public event Action SessionLoaded;
|
||||
|
||||
private readonly Assembly _dsAssembly;
|
||||
private readonly ManualResetEvent _stopHandle = new ManualResetEvent(false);
|
||||
|
||||
private ServerManager()
|
||||
internal ServerManager()
|
||||
{
|
||||
using (var f = File.OpenRead("SpaceEngineersDedicated.exe"))
|
||||
{
|
||||
@@ -47,7 +48,7 @@ namespace PistonServer
|
||||
|
||||
private void OnSessionReady()
|
||||
{
|
||||
SessionReady?.Invoke();
|
||||
SessionLoaded?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,6 +70,7 @@ namespace PistonServer
|
||||
/// </summary>
|
||||
public void StartServer()
|
||||
{
|
||||
Running = true;
|
||||
Logger.Write("Starting server.");
|
||||
|
||||
if (MySandboxGame.Log.LogEnabled)
|
||||
@@ -89,15 +91,19 @@ namespace PistonServer
|
||||
/// <summary>
|
||||
/// Stop the server.
|
||||
/// </summary>
|
||||
/// <param name="abortThread"></param>
|
||||
public void StopServer(bool abortThread = false)
|
||||
public void StopServer()
|
||||
{
|
||||
if (Thread.CurrentThread != ServerThread)
|
||||
if (Thread.CurrentThread.ManagedThreadId != ServerThread.ManagedThreadId)
|
||||
{
|
||||
MySandboxGame.Static?.Invoke(() => StopServer(true));
|
||||
Logger.Write("Requesting server stop.");
|
||||
MySandboxGame.Static.Invoke(StopServer);
|
||||
_stopHandle.WaitOne();
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Write("Stopping server.");
|
||||
MySession.Static.Save();
|
||||
MySession.Static.Unload();
|
||||
MySandboxGame.Static.Exit();
|
||||
|
||||
//Unload all the static junk.
|
||||
@@ -106,18 +112,10 @@ namespace PistonServer
|
||||
VRage.Input.MyGuiGameControlsHelpers.Reset();
|
||||
VRage.Input.MyInput.UnloadData();
|
||||
CleanupProfilers();
|
||||
GC.Collect(2);
|
||||
|
||||
Logger.Write("Server stopped.");
|
||||
if (abortThread)
|
||||
{
|
||||
try { ServerThread.Abort(); }
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
Logger.Write("Server thread aborted.");
|
||||
}
|
||||
ServerThread = null;
|
||||
}
|
||||
_stopHandle.Set();
|
||||
Running = false;
|
||||
}
|
||||
|
||||
private void CleanupProfilers()
|
||||
|
Reference in New Issue
Block a user