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 { /// /// Provides a proxy to the game's multiplayer-related functions. /// public class MultiplayerManager { public event Action PlayerJoined; public event Action PlayerLeft; public event Action ChatMessageReceived; public MTObservableCollection PlayersView { get; } = new MTObservableCollection(); public MTObservableCollection ChatView { get; } = new MTObservableCollection(); 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); /// /// Send a message in chat. /// 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); } /// /// Invoked when a client logs in and hits the respawn screen. /// 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); } } /// /// Stores player information in an observable format. /// 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; } } /// /// Identifies a player's current connection state. /// [Flags] public enum ConnectionState { Unknown, Connected = 1, Left = 2, Disconnected = 4, Kicked = 8, Banned = 16, } }