Я работаю на 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();
}
Это хороший подход, или я должен обращаться с этим по-другому?