Я использую .Net 5, и я делаю игровой сервер с использованием ядра SignalR.
Есть 2 способа внедрить сервис в мои классы.
1- Внедрение конструктора: где я должен разделять функции и данные, даже если они хранятся в памяти, а не в базе данных.
Например: вместо класса Room, содержащего данные и функции, я буду RoomManager
сервис и Room
для данных.
Здесь я ввожу _masterRepo
, _masterHub
, _sessionRepo
, _logger
на службе RoomManager
которые имеют срок службы.
Но мне нужно передать классы данных для работы.
public async Task AddUser(Room room, string userId, string connId)
{
var rUser = _sessionRepo.AddRoomUser(userId, connId, room);
if (room.Capacity == room.RoomUsers.Count)
{
_logger.LogInformation("a room is ready and will start");
await Start(room);
}
else
{
_sessionRepo.KeepRoom(room);
await _masterHub.Clients.User(rUser.UserId).SendAsync("RoomIsFilling");
}
}
private async Task Start(Room room)
{
var dUsers = _masterRepo.GetRoomDisplayUsersAsync(room);
var rUsers = room.RoomUsers;
var tasks = new List<Task>();
for (int i = 0; i < room.Capacity; i++)
{
tasks.Add(_masterHub.Groups.AddToGroupAsync(rUsers[i].ConnectionId, "room" + room.Id));
rUsers[i].TurnId = i;
tasks.Add(_masterHub.Clients.User(rUsers[i].UserId).SendAsync("StartRoom", i, dUsers));
}
await Task.WhenAll(tasks);
StartTurn(rUsers[0]);
}
2- Внедрение функций: где я могу комбинировать данные и функции в одном классе (нормальный дизайн ООП).
Это внутри класса Room, чтобы назвать его: someRoom.AddUser(....)
public async Task AddUser(SessionRepo _sessionRepo, ILogger _logger, IHubContext<MasterHub> _masterHub, string userId, string connId)
{
var rUser = _sessionRepo.AddRoomUser(userId, connId, room);
if (roomCapacity == RoomUsers.Count)
{
_logger.LogInformation("a room is ready and will start");
await Start(room);
}
else
{
_sessionRepo.KeepRoom(room);
await _masterHub.Clients.User(rUser.UserId).SendAsync("RoomIsFilling");
}
}
private async Task Start(MasterRepo _masterRepo)
{
var dUsers = _masterRepo.GetRoomDisplayUsersAsync(room);
var tasks = new List<Task>();
for (int i = 0; i < room.Capacity; i++)
{
tasks.Add(_masterHub.Groups.AddToGroupAsync(RoomUsers[i].ConnectionId, "room" + room.Id))
RoomUsers[i].TurnId = i
tasks.Add(_masterHub.Clients.User(RoomUsers[i].UserId).SendAsync("StartRoom", i, dUsers));
}
await Task.WhenAll(tasks)
StartTurn(RoomUsers[0]);
}
SessionRepo — это singleton, содержащий все данные с момента загрузки сервера.
public class SessionRepo : ISessionRepo
{
private ConcurrentDictionary<int, Room> Rooms;
private ConcurrentDictionary<string, RoomUser> RoomUsers;
private int LastRoomId;
private int LastRoomUserId;
private ConcurrentDictionary<(int, int), ConcurrentBag<Room>> PendingRooms;
private readonly int[] GenrePosses = {0, 1, 2, 3};
private readonly int[] UserCountPosses = {2, 3, 4};
public SessionRepo()
{
PendingRooms = new ConcurrentDictionary<(int, int), ConcurrentBag<Room>>();
for (int i = 0; i < GenrePosses.Length; i++)
{
for (int j = 0; j < UserCountPosses.Length; j++)
{
PendingRooms.TryAdd((i, j), new ConcurrentBag<Room>());
}
}
}
#region room
public Room DeleteRoom(int roomId)
{
Rooms.TryRemove(roomId, out Room room);
return room;
}
#endregion
#region pending room
public Room GetPendingRoom(int genre, int userCount)
{
PendingRooms[(genre, userCount)].TryTake(out Room room);
return room;
}
/// <summary>
/// if the room is still pending
/// </summary>
public void KeepRoom(Room room)
{
PendingRooms[(room.Genre, room.Capacity)].Add(room);
}
public RoomUser AddRoomUser(string id, string connId, Room room)
{
var rUser = new RoomUser {UserId = id, ConnectionId = connId, Room = room};
room.RoomUsers.Add(rUser);
RoomUsers.TryAdd(id, rUser);
return rUser;
}
public Room MakeRoom(int genre, int userCount)
{
var room = new Room {Genre = genre, Capacity = userCount};
Rooms.Append(ref LastRoomId, room);
return room;
}
#endregion
#region room user
public RoomUser GetRoomUserWithId(string id)
{
RoomUsers.TryGetValue(id, out RoomUser roomUser);
return roomUser;
}
#endregion
}
Я использовал внедрение функции, затем обнаружил, что сервер не должен иметь статуса, поэтому я сохранил данные сеанса в БД, но я подумал об этом, данные сеанса действительно малы для каждого пользователя и часто используются, потому что (на самом деле это только внутри игрового процесса). И если сервер выходит из строя, нет ничего, что нужно делать, чтобы сеансы продолжали работать, потому что есть рассчитанные по времени действия (например, тайм-аут), которых не произойдет, когда сервер умирает на несколько секунд, поэтому точка без статистики в сеансах не имеет значения, я думаю .
Я много узнал / искал, но не могу решить, я не могу найти достаточно сложный репозиторий SignalR, чтобы учиться. Мне некому просматривать мой код, извините, если это много, пожалуйста, поделитесь своими мыслями.