Separate IFrontendClient from ITcpClient

This commit is contained in:
Crypto137 2025-04-19 19:57:11 +03:00
parent 78c73cbbd8
commit 07dfd11fce
21 changed files with 169 additions and 158 deletions

View file

@ -1,7 +1,6 @@
using Gazillion;
using Google.ProtocolBuffers;
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.Core.System.Time;
namespace MHServerEmu.Core.Network
@ -25,7 +24,7 @@ namespace MHServerEmu.Core.Network
/// <summary>
/// Deserializes the provided <see cref="MessageBuffer"/> instance and adds its contents to this <see cref="CoreNetworkMailbox{TClient}"/> as a <see cref="MailboxMessage"/>.
/// </summary>
public bool Post(ITcpClient client, MessageBuffer messageBuffer)
public bool Post(IFrontendClient client, in MessageBuffer messageBuffer)
{
uint messageId = messageBuffer.MessageId;

View file

@ -1,6 +1,4 @@
using MHServerEmu.Core.Network.Tcp;
namespace MHServerEmu.Core.Network
namespace MHServerEmu.Core.Network
{
/// <summary>
/// Marker interface for <see cref="IGameService"/> messages.
@ -16,32 +14,32 @@ namespace MHServerEmu.Core.Network
// out a more performant way to send messages without overcomplicating everything
// (e.g. using the visitor pattern here would probably work, but it may be too cumbersome).
public readonly struct AddClient(ITcpClient client) : IGameServiceMessage
public readonly struct AddClient(IFrontendClient client) : IGameServiceMessage
{
public readonly ITcpClient Client = client;
public readonly IFrontendClient Client = client;
}
public readonly struct RemoveClient(ITcpClient client) : IGameServiceMessage
public readonly struct RemoveClient(IFrontendClient client) : IGameServiceMessage
{
public readonly ITcpClient Client = client;
public readonly IFrontendClient Client = client;
}
public readonly struct RouteMessageBufferList(ITcpClient client, ushort muxId, IReadOnlyList<MessageBuffer> messageBufferList) : IGameServiceMessage
public readonly struct RouteMessageBufferList(IFrontendClient client, ushort muxId, IReadOnlyList<MessageBuffer> messageBufferList) : IGameServiceMessage
{
public readonly ITcpClient Client = client;
public readonly IFrontendClient Client = client;
public readonly ushort MuxId = muxId;
public readonly IReadOnlyList<MessageBuffer> MessageBufferList = messageBufferList;
}
public readonly struct RouteMessageBuffer(ITcpClient client, MessageBuffer messageBuffer) : IGameServiceMessage
public readonly struct RouteMessageBuffer(IFrontendClient client, MessageBuffer messageBuffer) : IGameServiceMessage
{
public readonly ITcpClient Client = client;
public readonly IFrontendClient Client = client;
public readonly MessageBuffer MessageBuffer = messageBuffer;
}
public readonly struct RouteMessage(ITcpClient client, Type protocol, MailboxMessage message) : IGameServiceMessage
public readonly struct RouteMessage(IFrontendClient client, Type protocol, MailboxMessage message) : IGameServiceMessage
{
public readonly ITcpClient Client = client;
public readonly IFrontendClient Client = client;
public readonly Type Protocol = protocol;
public readonly MailboxMessage Message = message;
}

View file

@ -0,0 +1,34 @@
using Google.ProtocolBuffers;
namespace MHServerEmu.Core.Network
{
/// <summary>
/// Represents a frontend's connection to a remote game client.
/// </summary>
public interface IFrontendClient
{
public bool IsConnected { get; }
public ulong GameId { get; set; } // REMOVEME: Replace this with a service message
/// <summary>
/// Disconnects this <see cref="IFrontendClient"/>.
/// </summary>
public void Disconnect();
/// <summary>
/// Sends the provided <see cref="MuxCommand"/> over the specified mux channel.
/// </summary>
public void SendMuxCommand(ushort muxId, MuxCommand command);
/// <summary>
/// Sends the provided <see cref="IMessage"/> over the specified mux channel.
/// </summary>
public void SendMessage(ushort muxId, IMessage message);
/// <summary>
/// Sends the provided <see cref="IList{T}"/> of <see cref="IMessage"/> instances over the specified mux channel.
/// </summary>
public void SendMessageList(ushort muxId, List<IMessage> messageList);
}
}

View file

@ -1,5 +1,4 @@
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Network.Tcp;
namespace MHServerEmu.Core.Network
{
@ -16,7 +15,7 @@ namespace MHServerEmu.Core.Network
private static readonly Logger Logger = LogManager.CreateLogger();
private Queue<(ITcpClient, MailboxMessage)> _messageQueue = new();
private Queue<(IFrontendClient, MailboxMessage)> _messageQueue = new();
/// <summary>
/// Returns <see langword="true"/> if this <see cref="MessageList{TClient}"/> instance has any queued messages.
@ -26,9 +25,9 @@ namespace MHServerEmu.Core.Network
// NOTE: Rather than exposing the underlying data structure like the client, we encapsulate it with Enqueue() / TransferFrom() / Clear() methods.
/// <summary>
/// Enqueues the provided <see cref="MailboxMessage"/> from an <see cref="ITcpClient"/>.
/// Enqueues the provided <see cref="MailboxMessage"/> from an <see cref="IFrontendClient"/>.
/// </summary>
public void Enqueue(ITcpClient client, MailboxMessage message)
public void Enqueue(IFrontendClient client, MailboxMessage message)
{
// NOTE: In the client this is done by calling FastList::InsertTailList()
_messageQueue.Enqueue((client, message));
@ -68,11 +67,11 @@ namespace MHServerEmu.Core.Network
/// <summary>
/// Retrieves the next queued <see cref="MailboxMessage"/> instance without removing it from the queue.
/// </summary>
public (ITcpClient, MailboxMessage) PeekNextMessage()
public (IFrontendClient, MailboxMessage) PeekNextMessage()
{
// Do we even need peeking considering we have the HasMessages properties?
if (_messageQueue.TryPeek(out var result) == false)
return Logger.WarnReturn<(ITcpClient, MailboxMessage)>(default, $"PeekNextMessage(): No messages to peek");
return Logger.WarnReturn<(IFrontendClient, MailboxMessage)>(default, $"PeekNextMessage(): No messages to peek");
return result;
}
@ -80,10 +79,10 @@ namespace MHServerEmu.Core.Network
/// <summary>
/// Retrieves the next queued <see cref="MailboxMessage"/> instance.
/// </summary>
public (ITcpClient, MailboxMessage) PopNextMessage()
public (IFrontendClient, MailboxMessage) PopNextMessage()
{
if (_messageQueue.TryDequeue(out var result) == false)
return Logger.WarnReturn<(ITcpClient, MailboxMessage)>(default, $"PopNextMessage(): No messages to pop");
return Logger.WarnReturn<(IFrontendClient, MailboxMessage)>(default, $"PopNextMessage(): No messages to pop");
return result;
}

View file

@ -1,5 +1,4 @@
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Network.Tcp;
namespace MHServerEmu.Core.Network
{
@ -10,7 +9,7 @@ namespace MHServerEmu.Core.Network
}
/// <summary>
/// Buffered reader for data received from <see cref="ITcpClient"/>.
/// Buffered reader for data received by an <see cref="IFrontendClient"/>.
/// </summary>
public class MuxReader
{
@ -24,7 +23,7 @@ namespace MHServerEmu.Core.Network
private readonly MemoryStream _readBufferStream = new(new byte[ReadBufferSize]);
private readonly List<MessageBuffer> _messageBufferList = new();
private readonly ITcpClient _client;
private readonly IFrontendClient _client;
private MuxReaderState _state;
private int _stateBytes;
@ -34,7 +33,7 @@ namespace MHServerEmu.Core.Network
/// <summary>
/// Constructs and initializes a new <see cref="MuxReader"/> for the provided <see cref="ITcpClient"/>.
/// </summary>
public MuxReader(ITcpClient client)
public MuxReader(IFrontendClient client)
{
_client = client;
Reset();
@ -131,7 +130,7 @@ namespace MHServerEmu.Core.Network
{
case MuxCommand.Connect:
Logger.Trace($"Client [{_client}] connected on mux channel {header.MuxId}");
_client.Connection.Send(new MuxPacket(header.MuxId, MuxCommand.ConnectAck));
_client.SendMuxCommand(header.MuxId, MuxCommand.ConnectAck);
Reset();
break;

View file

@ -1,5 +1,4 @@
using Google.ProtocolBuffers;
using MHServerEmu.Core.Network.Tcp;
namespace MHServerEmu.Core.Network
{
@ -11,23 +10,23 @@ namespace MHServerEmu.Core.Network
private readonly ushort _muxChannel;
private readonly List<IMessage> _pendingMessageList = new();
public ITcpClient TcpClient { get; }
public IFrontendClient FrontendClient { get; }
public virtual bool CanSendOrReceiveMessages { get => true; }
/// <summary>
/// Constructs a new <see cref="NetClient"/> bound to the provided <see cref="ITcpClient"/>.
/// Constructs a new <see cref="NetClient"/> bound to the provided <see cref="IFrontendClient"/>.
/// </summary>
public NetClient(ushort muxChannel, ITcpClient tcpClient)
public NetClient(ushort muxChannel, IFrontendClient frontendClient)
{
ArgumentNullException.ThrowIfNull(tcpClient);
ArgumentNullException.ThrowIfNull(frontendClient);
_muxChannel = muxChannel;
TcpClient = tcpClient;
FrontendClient = frontendClient;
}
public void Disconnect()
{
TcpClient.Disconnect();
FrontendClient.Disconnect();
}
/// <summary>
@ -46,7 +45,7 @@ namespace MHServerEmu.Core.Network
if (_pendingMessageList.Count == 0)
return;
TcpClient.SendMessageList(_muxChannel, _pendingMessageList);
FrontendClient.SendMessageList(_muxChannel, _pendingMessageList);
_pendingMessageList.Clear();
}

View file

@ -1,6 +1,5 @@
using System.Collections;
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Network.Tcp;
namespace MHServerEmu.Core.Network
{
@ -13,7 +12,7 @@ namespace MHServerEmu.Core.Network
{
private static readonly Logger Logger = LogManager.CreateLogger();
private readonly Dictionary<ITcpClient, TNetClient> _netClientDict = new();
private readonly Dictionary<IFrontendClient, TNetClient> _netClientDict = new();
// Incoming messages are asynchronously posted to a mailbox where they are deserialized and stored for later retrieval.
// When it's time to process messages, we copy all messages stored in our mailbox to a list.
@ -22,10 +21,10 @@ namespace MHServerEmu.Core.Network
private readonly MessageList _messagesToProcessList = new();
// We swap queues with a lock when handling async client connect / disconnect events
private Queue<ITcpClient> _asyncAddClientQueue = new();
private Queue<ITcpClient> _asyncRemoveClientQueue = new();
private Queue<ITcpClient> _addClientQueue = new();
private Queue<ITcpClient> _removeClientQueue = new();
private Queue<IFrontendClient> _asyncAddClientQueue = new();
private Queue<IFrontendClient> _asyncRemoveClientQueue = new();
private Queue<IFrontendClient> _addClientQueue = new();
private Queue<IFrontendClient> _removeClientQueue = new();
private SpinLock _addClientLock = new(false);
private SpinLock _removeClientLock = new(false);
@ -42,10 +41,10 @@ namespace MHServerEmu.Core.Network
/// <summary>
/// Returns the <see cref="NetClient"/> bound to the provided <see cref="ITcpClient"/>.
/// </summary>
public TNetClient GetNetClient(ITcpClient tcpClient)
public TNetClient GetNetClient(IFrontendClient frontendClient)
{
if (_netClientDict.TryGetValue(tcpClient, out TNetClient netClient) == false)
Logger.Warn($"GetNetClient(): ITcpClient {tcpClient} is not bound to a NetClient");
if (_netClientDict.TryGetValue(frontendClient, out TNetClient netClient) == false)
Logger.Warn($"GetNetClient(): IFrontendClient {frontendClient} is not bound to a NetClient");
return netClient;
}
@ -62,9 +61,9 @@ namespace MHServerEmu.Core.Network
}
/// <summary>
/// Enqueues registration of a new <see cref="NetClient"/> for the provided <see cref="ITcpClient"/> during the next update.
/// Enqueues registration of a new <see cref="NetClient"/> for the provided <see cref="IFrontendClient"/> during the next update.
/// </summary>
public void AsyncAddClient(ITcpClient client)
public void AsyncAddClient(IFrontendClient client)
{
bool lockTaken = false;
try
@ -80,9 +79,9 @@ namespace MHServerEmu.Core.Network
}
/// <summary>
/// Enqueues removal of the <see cref="NetClient"/> bound to the provided <see cref="ITcpClient"/> during the next update.
/// Enqueues removal of the <see cref="NetClient"/> bound to the provided <see cref="IFrontendClient"/> during the next update.
/// </summary>
public void AsyncRemoveClient(ITcpClient client)
public void AsyncRemoveClient(IFrontendClient client)
{
bool lockTaken = false;
try
@ -100,16 +99,16 @@ namespace MHServerEmu.Core.Network
/// <summary>
/// Handles an incoming <see cref="MessageBuffer"/> asynchronously.
/// </summary>
public void AsyncReceiveMessageBuffer(ITcpClient tcpClient, MessageBuffer messageBuffer)
public void AsyncReceiveMessageBuffer(IFrontendClient frontendClient, in MessageBuffer messageBuffer)
{
// Gazillion's implementation does this in NetworkManager::ConnectionStatus()
// If the message fails to deserialize it means either data got corrupted somehow or we have a hacker trying to mess things up.
// In both cases it's better to bail out.
if (_mailbox.Post(tcpClient, messageBuffer) == false)
if (_mailbox.Post(frontendClient, messageBuffer) == false)
{
Logger.Error($"AsyncPostMessage(): Message deserialization error for data from client, disconnecting. Client: {tcpClient}");
tcpClient.Disconnect();
Logger.Error($"AsyncPostMessage(): Message deserialization error for data from client, disconnecting. Client: {frontendClient}");
frontendClient.Disconnect();
}
}
@ -123,8 +122,8 @@ namespace MHServerEmu.Core.Network
while (_messagesToProcessList.HasMessages)
{
(ITcpClient tcpClient, MailboxMessage message) = _messagesToProcessList.PopNextMessage();
TNetClient netClient = GetNetClient(tcpClient);
(IFrontendClient frontendClient, MailboxMessage message) = _messagesToProcessList.PopNextMessage();
TNetClient netClient = GetNetClient(frontendClient);
if (netClient != null && netClient.CanSendOrReceiveMessages)
netClient.ReceiveMessage(message);
@ -145,10 +144,10 @@ namespace MHServerEmu.Core.Network
protected bool RegisterNetClient(TNetClient netClient)
{
return _netClientDict.TryAdd(netClient.TcpClient, netClient);
return _netClientDict.TryAdd(netClient.FrontendClient, netClient);
}
protected abstract bool AcceptAndRegisterNewClient(ITcpClient tcpClient);
protected abstract bool AcceptAndRegisterNewClient(IFrontendClient tcpClient);
protected abstract void OnNetClientDisconnected(TNetClient netClient);
@ -169,8 +168,8 @@ namespace MHServerEmu.Core.Network
while (_addClientQueue.Count > 0)
{
ITcpClient tcpClient = _addClientQueue.Dequeue();
AcceptAndRegisterNewClient(tcpClient);
IFrontendClient frontendClient = _addClientQueue.Dequeue();
AcceptAndRegisterNewClient(frontendClient);
}
}
@ -191,11 +190,11 @@ namespace MHServerEmu.Core.Network
while (_removeClientQueue.Count > 0)
{
ITcpClient tcpClient = _removeClientQueue.Dequeue();
IFrontendClient frontendClient = _removeClientQueue.Dequeue();
if (_netClientDict.Remove(tcpClient, out TNetClient netClient) == false)
if (_netClientDict.Remove(frontendClient, out TNetClient netClient) == false)
{
Logger.Warn($"RemoveDisconnectedClients(): ITcpClient {tcpClient} not found");
Logger.Warn($"RemoveDisconnectedClients(): IFrontendClient {frontendClient} not found");
continue;
}
@ -206,12 +205,12 @@ namespace MHServerEmu.Core.Network
/// <summary>
/// A simple wrapper around <see cref="Dictionary{TKey, TValue}.ValueCollection.Enumerator"/>
/// to iterate <typeparamref name="TNetClient"/> instances managed by this <see cref="NetworkManager{TNetClient}"/>.
/// to iterate <typeparamref name="TNetClient"/> instances managed by this <see cref="NetworkManager{TNetClient, TProtocol}"/>.
/// </summary>
public struct Enumerator : IEnumerator<TNetClient>
{
private readonly NetworkManager<TNetClient, TProtocol> _networkManager;
private Dictionary<ITcpClient, TNetClient>.ValueCollection.Enumerator _enumerator;
private Dictionary<IFrontendClient, TNetClient>.ValueCollection.Enumerator _enumerator;
public TNetClient Current { get => _enumerator.Current; }
object IEnumerator.Current { get => Current; }

View file

@ -1,6 +1,4 @@
using Google.ProtocolBuffers;
namespace MHServerEmu.Core.Network.Tcp
namespace MHServerEmu.Core.Network.Tcp
{
/// <summary>
/// Provides access to a <see cref="TcpServer"/>'s connection to a remote client.
@ -8,23 +6,5 @@ namespace MHServerEmu.Core.Network.Tcp
public interface ITcpClient
{
public TcpClientConnection Connection { get; }
public bool IsConnected { get; }
public ulong GameId { get; set; } // REMOVEME: Replace this with a service message
/// <summary>
/// Disconnects this <see cref="ITcpClient"/>.
/// </summary>
public void Disconnect();
/// <summary>
/// Sends the provided <see cref="IMessage"/> over the specified mux channel.
/// </summary>
public void SendMessage(ushort muxId, IMessage message);
/// <summary>
/// Sends the provided <see cref="IList{T}"/> of <see cref="IMessage"/> instances over the specified mux channel.
/// </summary>
public void SendMessageList(ushort muxId, List<IMessage> messageList);
}
}

View file

@ -9,9 +9,9 @@ using MHServerEmu.DatabaseAccess.Models;
namespace MHServerEmu.Frontend
{
/// <summary>
/// Represents an <see cref="ITcpClient"/> connected to the <see cref="FrontendServer"/>.
/// An implementation of <see cref="IFrontendClient"/> backed by a <see cref="TcpServer"/>.
/// </summary>
public class FrontendClient : ITcpClient, IDBAccountOwner
public class FrontendClient : IFrontendClient, ITcpClient, IDBAccountOwner
{
private static readonly Logger Logger = LogManager.CreateLogger();
@ -51,13 +51,19 @@ namespace MHServerEmu.Frontend
return $"Account={Session.Account}, SessionId=0x{Session.Id:X}";
}
#region ITcpClient Implementation
#region IFrontendClient Implementation
public void Disconnect()
{
Connection.Disconnect();
}
public void SendMuxCommand(ushort muxId, MuxCommand command)
{
MuxPacket packet = new(muxId, command);
Connection.Send(packet);
}
public void SendMessage(ushort muxId, IMessage message)
{
MuxPacket packet = new(muxId, MuxCommand.Data);

View file

@ -188,17 +188,17 @@ namespace MHServerEmu.Games
IsRunning = false;
}
public void AddClient(ITcpClient client)
public void AddClient(IFrontendClient client)
{
NetworkManager.AsyncAddClient(client);
}
public void RemoveClient(ITcpClient client)
public void RemoveClient(IFrontendClient client)
{
NetworkManager.AsyncRemoveClient(client);
}
public void ReceiveMessageBuffer(ITcpClient client, MessageBuffer messageBuffer)
public void ReceiveMessageBuffer(IFrontendClient client, in MessageBuffer messageBuffer)
{
NetworkManager.AsyncReceiveMessageBuffer(client, messageBuffer);
}

View file

@ -5,7 +5,6 @@ using MHServerEmu.Core.Extensions;
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Memory;
using MHServerEmu.Core.Network;
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.Core.Serialization;
using MHServerEmu.Core.System.Time;
using MHServerEmu.Core.VectorMath;
@ -41,7 +40,7 @@ namespace MHServerEmu.Games.Network
private static readonly Logger Logger = LogManager.CreateLogger();
private readonly ITcpClient _tcpClient;
private readonly IFrontendClient _frontendClient;
private readonly DBAccount _dbAccount;
private bool _waitingForRegionIsAvailableResponse = false;
@ -64,13 +63,13 @@ namespace MHServerEmu.Games.Network
/// <summary>
/// Constructs a new <see cref="PlayerConnection"/>.
/// </summary>
public PlayerConnection(Game game, ITcpClient tcpClient) : base(MuxChannel, tcpClient)
public PlayerConnection(Game game, IFrontendClient frontendClient) : base(MuxChannel, frontendClient)
{
Game = game;
// The ITcpClient used by PlayerConnection also needs to implement IDBAccountOwner
_tcpClient = tcpClient;
_dbAccount = ((IDBAccountOwner)tcpClient).Account;
// IFrontendClient used by PlayerConnection also needs to implement IDBAccountOwner
_frontendClient = frontendClient;
_dbAccount = ((IDBAccountOwner)frontendClient).Account;
AOI = new(this);
WorldView = new(this);
@ -278,9 +277,9 @@ namespace MHServerEmu.Games.Network
// Remove game id to let the player manager know that it is now safe to write to the database.
// TODO: Replace this with a player manager message.
_tcpClient.GameId = 0;
_frontendClient.GameId = 0;
Logger.Info($"Removed ITcpClient [{_tcpClient}] from game [{Game}]");
Logger.Info($"Removed frontend client [{_frontendClient}] from game [{Game}]");
}
#endregion
@ -496,8 +495,7 @@ namespace MHServerEmu.Games.Network
case ClientToGameServerMessage.NetMessageReportPlayer: // 66
case ClientToGameServerMessage.NetMessageChatBanVote: // 67
case ClientToGameServerMessage.NetMessageTryModifyCommunityMemberCircle: // 106, TODO: handle this in game
GameServiceProtocol.RouteMessage groupingManagerMessage = new(_tcpClient, typeof(ClientToGameServerMessage), message);
ServerManager.Instance.SendMessageToService(ServerType.GroupingManager, groupingManagerMessage);
RouteMessageToService(ServerType.GroupingManager, message);
break;
// Billing
@ -506,22 +504,26 @@ namespace MHServerEmu.Games.Network
case ClientToGameServerMessage.NetMessageBuyItemFromCatalog: // 70
case ClientToGameServerMessage.NetMessageBuyGiftForOtherPlayer: // 71
case ClientToGameServerMessage.NetMessageGetGiftHistory: // 73
GameServiceProtocol.RouteMessage billingMessage = new(_tcpClient, typeof(ClientToGameServerMessage), message);
ServerManager.Instance.SendMessageToService(ServerType.Billing, billingMessage);
RouteMessageToService(ServerType.Billing, message);
break;
// Leaderboards
case ClientToGameServerMessage.NetMessageLeaderboardRequest: // 157
case ClientToGameServerMessage.NetMessageLeaderboardArchivedInstanceListRequest: // 158
case ClientToGameServerMessage.NetMessageLeaderboardInitializeRequest: // 159
GameServiceProtocol.RouteMessage leaderboardMessage = new(_tcpClient, typeof(ClientToGameServerMessage), message);
ServerManager.Instance.SendMessageToService(ServerType.Leaderboard, leaderboardMessage);
RouteMessageToService(ServerType.Leaderboard, message);
break;
default: Logger.Warn($"ReceiveMessage(): Unhandled {(ClientToGameServerMessage)message.Id} [{message.Id}]"); break;
}
}
private void RouteMessageToService(ServerType serverType, in MailboxMessage mailboxMessage)
{
GameServiceProtocol.RouteMessage routeMessage = new(_frontendClient, typeof(ClientToGameServerMessage), mailboxMessage);
ServerManager.Instance.SendMessageToService(serverType, routeMessage);
}
private bool OnPlayerSystemMetrics(MailboxMessage message) // 1
{
var playerSystemMetrics = message.As<NetMessagePlayerSystemMetrics>();

View file

@ -3,7 +3,6 @@ using Google.ProtocolBuffers;
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Memory;
using MHServerEmu.Core.Network;
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.Core.System.Time;
using MHServerEmu.Games.Entities;
using MHServerEmu.Games.Regions;
@ -215,32 +214,32 @@ namespace MHServerEmu.Games.Network
#endregion
protected override bool AcceptAndRegisterNewClient(ITcpClient tcpClient)
protected override bool AcceptAndRegisterNewClient(IFrontendClient frontendClient)
{
// Make sure this client is still connected (it may not be if we are lagging hard)
if (tcpClient.IsConnected == false)
return Logger.WarnReturn(false, $"AcceptAndRegisterNewClient(): Client [{tcpClient}] is no longer connected");
if (frontendClient.IsConnected == false)
return Logger.WarnReturn(false, $"AcceptAndRegisterNewClient(): Client [{frontendClient}] is no longer connected");
// Construct a new PlayerConnection bound to this ITcpClient
PlayerConnection playerConnection = new(_game, tcpClient);
// Construct a new PlayerConnection bound to this IFrontendClient
PlayerConnection playerConnection = new(_game, frontendClient);
// Make sure this client's account is not being used by another client pending disconnection.
// We do this after constructing the connection to keep the ITcpClient -> PlayerDbId retrieval in one place.
// We do this after constructing the connection to keep the IFrontendClient -> PlayerDbId retrieval in one place.
ulong dbId = playerConnection.PlayerDbId;
if (_playerDbIds.Add(dbId) == false)
{
Logger.Warn($"AcceptAndRegisterNewClient(): Attempting to add client [{tcpClient}] to game [{_game}], but its account dbId 0x{dbId:X} is already in use");
tcpClient.Disconnect();
Logger.Warn($"AcceptAndRegisterNewClient(): Attempting to add client [{frontendClient}] to game [{_game}], but its account dbId 0x{dbId:X} is already in use");
frontendClient.Disconnect();
return false;
}
// Register the client to allow it to receive messages
if (RegisterNetClient(playerConnection) == false)
Logger.Error($"AcceptAndRegisterNewClient(): Failed to add client [{tcpClient}]");
Logger.Error($"AcceptAndRegisterNewClient(): Failed to add client [{frontendClient}]");
// TODO: Replace this with a message to PlayerManager
tcpClient.GameId = _game.Id;
frontendClient.GameId = _game.Id;
// Send time sync straight away for the client to be able to initialize its EventScheduler (needed for loading screens).
// This will also make the client start sending pings, so it needs to be done after we assign game id.
@ -258,7 +257,7 @@ namespace MHServerEmu.Games.Network
// This connection will be set as pending when we receive region availability query response
Logger.Info($"Accepted and registered client [{tcpClient}] to game [{_game}]");
Logger.Info($"Accepted and registered client [{frontendClient}] to game [{_game}]");
return true;
}

View file

@ -1,6 +1,6 @@
using Gazillion;
using MHServerEmu.Core.Config;
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.Core.Network;
namespace MHServerEmu.Grouping
{
@ -38,7 +38,7 @@ namespace MHServerEmu.Grouping
/// The in-game chat window does not handle well messages longer than 25-30 lines (~40 characters per line).
/// If you need to send a long message, use SendMetagameMessages() or SendMetagameMessageSplit().
/// </remarks>
public static void SendMetagameMessage(ITcpClient client, string text, bool showSender = true)
public static void SendMetagameMessage(IFrontendClient client, string text, bool showSender = true)
{
client.SendMessage(MuxChannel, ChatNormalMessage.CreateBuilder()
.SetRoomType(ChatRoomTypes.CHAT_ROOM_TYPE_METAGAME)
@ -51,7 +51,7 @@ namespace MHServerEmu.Grouping
/// <summary>
/// Sends the specified collection of texts as metagame chat messages to the provided <see cref="FrontendClient"/>.
/// </summary>
public static void SendMetagameMessages(ITcpClient client, IEnumerable<string> texts, bool showSender = true)
public static void SendMetagameMessages(IFrontendClient client, IEnumerable<string> texts, bool showSender = true)
{
foreach (string text in texts)
{
@ -63,7 +63,7 @@ namespace MHServerEmu.Grouping
/// <summary>
/// Splits the specified text at line breaks and sends it as a collection of metagame chat messages to the provided <see cref="FrontendClient"/>.
/// </summary>
public static void SendMetagameMessageSplit(ITcpClient client, string text, bool showSender = true)
public static void SendMetagameMessageSplit(IFrontendClient client, string text, bool showSender = true)
{
SendMetagameMessages(client, text.Split("\r\n", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries), showSender);
}

View file

@ -2,7 +2,6 @@
using Google.ProtocolBuffers;
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Network;
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.DatabaseAccess;
using MHServerEmu.DatabaseAccess.Models;
@ -15,7 +14,7 @@ namespace MHServerEmu.Grouping
private static readonly Logger Logger = LogManager.CreateLogger();
private readonly object _playerLock = new();
private readonly Dictionary<string, ITcpClient> _playerDict = new(); // Store players in a name-client dictionary because tell messages are sent by player name
private readonly Dictionary<string, IFrontendClient> _playerDict = new(); // Store players in a name-client dictionary because tell messages are sent by player name
private ICommandParser _commandParser;
@ -72,7 +71,7 @@ namespace MHServerEmu.Grouping
private void OnRouteMailboxMessage(in GameServiceProtocol.RouteMessage routeMailboxMessage)
{
ITcpClient client = routeMailboxMessage.Client;
IFrontendClient client = routeMailboxMessage.Client;
MailboxMessage message = routeMailboxMessage.Message;
// Handle messages routed from games
@ -90,7 +89,7 @@ namespace MHServerEmu.Grouping
#region Client Management
private bool AddClient(ITcpClient client)
private bool AddClient(IFrontendClient client)
{
lock (_playerLock)
{
@ -108,7 +107,7 @@ namespace MHServerEmu.Grouping
}
}
private bool RemoveClient(ITcpClient client)
private bool RemoveClient(IFrontendClient client)
{
lock (_playerLock)
{
@ -132,7 +131,7 @@ namespace MHServerEmu.Grouping
}
}
public bool TryGetPlayerByName(string playerName, out ITcpClient client)
public bool TryGetPlayerByName(string playerName, out IFrontendClient client)
{
return _playerDict.TryGetValue(playerName.ToLower(), out client);
}
@ -141,7 +140,7 @@ namespace MHServerEmu.Grouping
#region Message Handling
private bool OnChat(ITcpClient client, MailboxMessage message)
private bool OnChat(IFrontendClient client, MailboxMessage message)
{
var chat = message.As<NetMessageChat>();
if (chat == null) return Logger.WarnReturn(false, $"OnChat(): Failed to retrieve message");
@ -182,7 +181,7 @@ namespace MHServerEmu.Grouping
return true;
}
private bool OnTell(ITcpClient client, MailboxMessage message)
private bool OnTell(IFrontendClient client, MailboxMessage message)
{
var tell = message.As<NetMessageTell>();
if (tell == null) return Logger.WarnReturn(false, $"OnTell(): Failed to retrieve message");
@ -197,7 +196,7 @@ namespace MHServerEmu.Grouping
return true;
}
private bool OnTryModifyCommunityMemberCircle(ITcpClient client, MailboxMessage message)
private bool OnTryModifyCommunityMemberCircle(IFrontendClient client, MailboxMessage message)
{
// We are handling this in the grouping manager to avoid exposing the ChatHelper class
// TODO: Remove this and handle it in game after we implemented social functionality there.

View file

@ -1,4 +1,4 @@
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.Core.Network;
namespace MHServerEmu.Grouping
{
@ -10,6 +10,6 @@ namespace MHServerEmu.Grouping
/// <summary>
/// Attempts to parse a command from the provided <see cref="string"/> message. Returns <see langword="true"/> if successful.
/// </summary>
public bool TryParse(string message, ITcpClient client);
public bool TryParse(string message, IFrontendClient client);
}
}

View file

@ -1,7 +1,6 @@
using Gazillion;
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Network;
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.DatabaseAccess;
using MHServerEmu.Games.GameData;
@ -45,13 +44,13 @@ namespace MHServerEmu.Leaderboards
private void OnRouteMailboxMessage(in GameServiceProtocol.RouteMessage routeMailboxMessage)
{
ITcpClient tcpClient = routeMailboxMessage.Client;
IFrontendClient client = routeMailboxMessage.Client;
MailboxMessage message = routeMailboxMessage.Message;
switch ((ClientToGameServerMessage)message.Id)
{
case ClientToGameServerMessage.NetMessageLeaderboardInitializeRequest: OnInitializeRequest(tcpClient, message); break;
case ClientToGameServerMessage.NetMessageLeaderboardRequest: OnRequest(tcpClient, message); break;
case ClientToGameServerMessage.NetMessageLeaderboardInitializeRequest: OnInitializeRequest(client, message); break;
case ClientToGameServerMessage.NetMessageLeaderboardRequest: OnRequest(client, message); break;
default: Logger.Warn($"Handle(): Unhandled {(ClientToGameServerMessage)message.Id} [{message.Id}]"); break;
}
@ -59,7 +58,7 @@ namespace MHServerEmu.Leaderboards
#endregion
private bool OnInitializeRequest(ITcpClient client, MailboxMessage message)
private bool OnInitializeRequest(IFrontendClient client, MailboxMessage message)
{
var initializeRequest = message.As<NetMessageLeaderboardInitializeRequest>();
if (initializeRequest == null) return Logger.WarnReturn(false, $"OnInitializeRequest(): Failed to retrieve message");
@ -76,7 +75,7 @@ namespace MHServerEmu.Leaderboards
return true;
}
private bool OnRequest(ITcpClient client, MailboxMessage message)
private bool OnRequest(IFrontendClient client, MailboxMessage message)
{
var request = message.As<NetMessageLeaderboardRequest>();
if (request == null) return Logger.WarnReturn(false, $"OnRequest(): Failed to retrieve message");

View file

@ -3,7 +3,6 @@ using Google.ProtocolBuffers;
using MHServerEmu.Core.Config;
using MHServerEmu.Core.Logging;
using MHServerEmu.Core.Network;
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.DatabaseAccess.Models;
using MHServerEmu.Frontend;
using MHServerEmu.Games;
@ -146,12 +145,12 @@ namespace MHServerEmu.PlayerManagement
private void OnRouteMessage(in GameServiceProtocol.RouteMessage routeMessage)
{
ITcpClient tcpClient = routeMessage.Client;
IFrontendClient client = routeMessage.Client;
MailboxMessage message = routeMessage.Message;
switch ((FrontendProtocolMessage)message.Id)
{
case FrontendProtocolMessage.ClientCredentials: OnClientCredentials(tcpClient, message); break;
case FrontendProtocolMessage.ClientCredentials: OnClientCredentials(client, message); break;
default: Logger.Warn($"Handle(): Unhandled {(ClientToGameServerMessage)message.Id} [{message.Id}]"); break;
}
@ -421,11 +420,13 @@ namespace MHServerEmu.PlayerManagement
/// <summary>
/// Handles <see cref="ClientCredentials"/>.
/// </summary>
private bool OnClientCredentials(ITcpClient client, MailboxMessage message)
private bool OnClientCredentials(IFrontendClient client, MailboxMessage message)
{
var clientCredentials = message.As<ClientCredentials>();
if (clientCredentials == null) return Logger.WarnReturn(false, "OnClientCredentials(): clientCredentials == null");
FrontendClient frontendClient = (FrontendClient)client;
if (Config.SimulateQueue)
{
Logger.Debug("Responding with LoginQueueStatus message");
@ -437,9 +438,9 @@ namespace MHServerEmu.PlayerManagement
return false;
}
if (_sessionManager.VerifyClientCredentials((FrontendClient)client, clientCredentials) == false)
if (_sessionManager.VerifyClientCredentials(frontendClient, clientCredentials) == false)
{
Logger.Warn($"OnClientCredentials(): Failed to verify client credentials, disconnecting client on {client.Connection}");
Logger.Warn($"OnClientCredentials(): Failed to verify client credentials, disconnecting client on {frontendClient.Connection}");
client.Disconnect();
return false;
}
@ -457,7 +458,7 @@ namespace MHServerEmu.PlayerManagement
/// <summary>
/// Handles <see cref="NetMessageReadyForGameJoin"/>.
/// </summary>
private bool OnReadyForGameJoin(ITcpClient client, MessageBuffer messageBuffer)
private bool OnReadyForGameJoin(IFrontendClient client, MessageBuffer messageBuffer)
{
// There is a client-side bug with NetMessageReadyForGameJoin that requires special handling, see DeserializeReadyForGameJoin() for more info.
var readyForGameJoin = messageBuffer.DeserializeReadyForGameJoin();

View file

@ -1,4 +1,4 @@
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.Core.Network;
using MHServerEmu.Frontend;
using MHServerEmu.Grouping;
@ -9,7 +9,7 @@ namespace MHServerEmu.Commands
/// </summary>
public class CommandParser : ICommandParser
{
public bool TryParse(string message, ITcpClient client)
public bool TryParse(string message, IFrontendClient client)
{
return CommandManager.Instance.TryParse(message, (FrontendClient)client);
}

View file

@ -1,19 +1,18 @@
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.Frontend;
using MHServerEmu.Core.Network;
using MHServerEmu.Grouping;
namespace MHServerEmu.Commands
{
/// <summary>
/// Provides output to a chat window of a <see cref="FrontendClient"/>.
/// Provides output to a chat window of a <see cref="IFrontendClient"/>.
/// </summary>
public class FrontendClientChatOutput : IClientOutput
{
// TODO: Potentially move this to MHServerEmu.Grouping.
public void Output(string output, ITcpClient client)
public void Output(string output, IFrontendClient client)
{
ChatHelper.SendMetagameMessage((FrontendClient)client, output);
ChatHelper.SendMetagameMessage(client, output);
}
}
}

View file

@ -1,15 +1,15 @@
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.Core.Network;
namespace MHServerEmu.Commands
{
/// <summary>
/// Exposes <see cref="string"/> output for an <see cref="ITcpClient"/>.
/// Exposes <see cref="string"/> output for an <see cref="IFrontendClient"/>.
/// </summary>
public interface IClientOutput
{
/// <summary>
/// Outputs the provided <see cref="string"/> to the specified <see cref="ITcpClient"/>.
/// Outputs the provided <see cref="string"/> to the specified <see cref="IFrontendClient"/>.
/// </summary>
public void Output(string output, ITcpClient client);
public void Output(string output, IFrontendClient client);
}
}

View file

@ -3,7 +3,6 @@ using Gazillion;
using MHServerEmu.Commands.Attributes;
using MHServerEmu.Core.Config;
using MHServerEmu.Core.Network;
using MHServerEmu.Core.Network.Tcp;
using MHServerEmu.DatabaseAccess.Models;
using MHServerEmu.Frontend;
using MHServerEmu.Grouping;
@ -43,7 +42,7 @@ namespace MHServerEmu.Commands.Implementations
if (groupingManager == null)
return "Failed to connect to the grouping manager.";
if (groupingManager.TryGetPlayerByName(@params[0], out ITcpClient target) == false)
if (groupingManager.TryGetPlayerByName(@params[0], out IFrontendClient target) == false)
return $"Player {@params[0]} not found.";
target.Disconnect();