Архитектура многопользовательской игровой платформы SignalR
Я работаю на multiplayer game
Платформа, которая будет иметь несколько (в основном turn-based
) multiplayer games
, Основная идея заключается в том, что каждый player
подключается к конкретному game
и в этом game
у них будет несколько lobbies
, Пользователь может ввести каждый lobby
где они будут видеть других игроков в lobby
так же как game instances
(открытые игры) в этом lobby
, Пользователи также смогут создавать новые game instance
в пределах lobby
и ждите, пока другие игроки присоединятся, чтобы начать игру.
У меня проблемы с архитектурой текущего состояния игрока. Разве это нормально - иметь какую-то модель игрока (которая на самом деле была бы просто расширенной моделью пользователя), в которой сохраняется все состояние игрока? Что-то вроде этого:
public interface IPlayer
{
Guid UserId { get; set; }
string Name { get; set; }
Guid GameDefinitionId { get; set; }
Guid? LobbyId { get; set; }
Guid? GameId { get; set; }
}
GameDefinitionId
- Определяет, какое определение игры (покер, блэкджек и т. Д.) Выбрал пользователь. Каждая из игр имеет своюhub
поэтому, когда пользователь подключен кhub
(конкретныйgame
),Player
экземпляр создан с указаннымGameDefinitionId
который является идентификатором этогоgame
(поэтому это не является обязательным в модели).LobbyId
- Необязательный идентификаторlobby
показывая, что пользователь находится в указанном лобби.GameId
- Необязательныйgame
идентификатор экземпляра, показывающий, что пользователь находится в указанномgame
,
Изменяя эти свойства (в настоящее время список IPlayer
является in-memory
, но при необходимости я бы переместил его в database
позже) я могу точно сказать, где находится пользователь и контролировать его состояние. Обратите внимание, что пользователь не может быть в нескольких лобби или играх одновременно. Конечно, каждое изменение подтверждается service
и если изменение действительно, все заинтересованные clients
уведомлены.
Это базовый центр, который каждая игра должна реализовывать
public abstract class MultiplayerGameHub : HubBase
{
protected IPlayerHandler PlayerHandler { get; private set; }
public MultiplayerGameHub(
IOnlineClientManager onlineClientManager,
ISessionProvider sessionProvider,
IPlayerHandler playerHandler)
: base(onlineClientManager, sessionProvider)
{
PlayerHandler = playerHandler;
}
public virtual async Task Connect()
{
var gameDefinition = await GetGameDefinitionAsync();
if (gameDefinition == null)
{
throw new HubException("Unknown game definition");
}
await PlayerHandler.ConnectAsync(Context.ConnectionId, gameDefinition.Id);
}
public virtual Task Disconnect()
{
return PlayerHandler.DisconnectAsync(Context.ConnectionId);
}
public Task JoinLobby(JoinLobbyParams model)
{
return PlayerHandler.JoinLobbyAsync(Context.ConnectionId, model.LobbyId);
}
public Task LeaveLobby()
{
return PlayerHandler.LeaveLobbyAsync(Context.ConnectionId);
}
public abstract Task<IGameDefinition> GetGameDefinitionAsync();
}
Это хороший подход, или я должен обращаться с этим по-другому?