0
\$\begingroup\$

I am using .Net 5, and I am making game server using SignalR core.

Ther're 2 ways to inject service to my classes.

1- Constructor injection: where I must separate functions and data, even if it's stored in memory not database.

For example: Instead of Room class that contains data and functions, I will be RoomManager service and Room for data.

Here I inject _masterRepo, _masterHub, _sessionRepo, _logger in the service RoomManager that have scoped lifetime.

But I have to pass data classes to work on.

        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- Function injection: where I can combine data and functions in the same class(normal OOP design).

This inside Room class, to call it: 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 is singleton holds all data from server boot up.

    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
    }

I was using function injection, then I found that server should be stataless, so I stored session data in DB, but I thought of this matter, session data is really small per user and it's frequenlty used bescause(it's actually inside gameplay only). And if the server goes down, there's nothing to do to keep sessions working because there're timed actions(like turn timout) that won't happen when the server die for seconds, so the statless point in sessions doens't matter I think.

I learned/searched a lot but I can't decide, I can't find complex enougth SignalR Repository to learn from. I have no one to review my code, I am sorry if this is a lot, please give me you your thoughts.

\$\endgroup\$
2
  • \$\begingroup\$ Welcome to the Code Review Community where we review code that is working as expected and provide suggestions on how to improve that code. While we sometimes provide design reviews as part of a code review that is not the main focus of the code review site. For a design review, which is what you seem to be asking about you might want to try Software Engineering but make sure you follow their guidelines when asking a question. \$\endgroup\$ – pacmaninbw Feb 27 at 12:23
  • \$\begingroup\$ Thanks, I will look at it. \$\endgroup\$ – Adam Q Feb 27 at 19:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.