using Google.Protobuf.WellKnownTypes; using MetaAssets; using Newtonsoft.Json; using ServerControlCenter; using ServerCore; using ServerBase; using StackExchange.Redis; using System.Numerics; namespace ServerCommon { public class InstanceRoomInfo { public string roomId { get; set; } = string.Empty; public string InstanceAddress { get; set; } = string.Empty; public int InstancePort { get; set; } = 0; public int InstanceId { get; set; } = 0; public int UgcNpcCount { get; set; } = 0; public string MyhomeGuid { get; set; } = string.Empty; public EPlaceType InstancePlaceType { get; set; } = EPlaceType.NONE; public Timestamp InstanceStartTime { get; set; } = DateTimeHelper.MinTime.ToTimestamp(); } public class InstanceRoomStorage { private enum ExistInstanceRoomState { None = 0, Different = 1, Exist = 2, Exception = 3 } readonly int KEEP_INSTANCE_ROOM_TIME = (60 * 1000); //ConnectionMultiplexer _connection = default!; IDatabase _database = default!; string _roomKeyPrefix = String.Empty; string _instanceKeyPrefix = String.Empty; public void Init(IDatabase redisDB, string keyPrefix) { //_connection = await ConnectionMultiplexer.ConnectAsync(configuration); //_database = _connection.GetDatabase(); _database = redisDB; //var servers = _connection.GetEndPoints().Select(endpoint => _connection.GetServer(endpoint)); //foreach (var server in servers) //{ // Console.WriteLine(server.EndPoint); //} _roomKeyPrefix = keyPrefix; if (_roomKeyPrefix != string.Empty && !_roomKeyPrefix.EndsWith(":")) { _roomKeyPrefix += ":"; } _roomKeyPrefix += "instanceroom"; _instanceKeyPrefix = keyPrefix; if (_instanceKeyPrefix != string.Empty && !_instanceKeyPrefix.EndsWith(":")) { _instanceKeyPrefix += ":"; } _instanceKeyPrefix += "instanceroom"; } string GetRoomIdSeqKey() { return $"{_roomKeyPrefix}:roomidseq"; } public string GetRoomInfoKey(string instanceRoomId) { return $"{_roomKeyPrefix}:{instanceRoomId}:info"; } public string GetRoomMemberListKey(string instanceRoomId) { return $"{_roomKeyPrefix}:{instanceRoomId}:memberlist"; } public string GetInstanceRoomListKey(string instanceRoomIdBase) { return $"{_instanceKeyPrefix}:{instanceRoomIdBase}:roomlist"; } public async Task getRoomIdSeq() { var result = new Result(); var err_msg = string.Empty; long room_id_seq = 0; try { string room_id_seq_key = GetRoomIdSeqKey(); room_id_seq = await _database.StringIncrementAsync(room_id_seq_key); } catch (Exception e) { err_msg = $"Failed to getRoomIdSeq from Redis !!! : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().fatal(result.toBasicString()); } return room_id_seq; } public async Task<(Result, bool)> createInstanceRoom(string instanceRoomId, string instanceAddress, int instancePort, int instanceMetaId) { var result = new Result(); var err_msg = string.Empty; var is_created = false; try { string room_info_key = GetRoomInfoKey(instanceRoomId); var instance_room_info = new InstanceRoomInfo(); instance_room_info.roomId = instanceRoomId; instance_room_info.InstanceAddress = instanceAddress; instance_room_info.InstancePort = instancePort; instance_room_info.InstanceId = instanceMetaId; instance_room_info.UgcNpcCount = 0; // 중복 확인 var exist_instance_room_state = await isExistInstanceRoom(instanceRoomId, instanceMetaId); switch (exist_instance_room_state) { case ExistInstanceRoomState.None: { var hash_entries = TypeConvertHelper.toHashEntries(instance_room_info); await _database.HashSetAsync(room_info_key, hash_entries); await _database.KeyExpireAsync(room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); is_created = true; } break; case ExistInstanceRoomState.Different: { err_msg = $"InstanceRoomId is Duplicated !!! : instanceRoomId:{instanceRoomId}, instanceMetaId:{instanceMetaId}"; result.setFail(ServerErrorCode.InstanceRoomIdDuplicated, err_msg); Log.getLogger().error(err_msg); return (result, is_created); } case ExistInstanceRoomState.Exist: { // 존재하는 방이 만드려는 방과 동일하다. // 해당 방으로 입장한다. } break; case ExistInstanceRoomState.Exception: { err_msg = $"Failed to isExistInstanceRoom() !!!"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().error(result.toBasicString()); return (result, is_created); } default: { err_msg = $"ExistInstanceRoomState Invalid !!! : state:{exist_instance_room_state}"; result.setFail(ServerErrorCode.NotImplemented, err_msg); Log.getLogger().error(result.toBasicString()); return (result, is_created); } } } catch (Exception e) { err_msg = $"Failed to createInstanceRoom from Redis !!! : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().fatal(err_msg); } return (result, is_created); } async Task isExistInstanceRoom(string instanceRoomId, int instanceMetaId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_info_key = GetRoomInfoKey(instanceRoomId); // 기존 방 정보 얻기 var value = await _database.HashGetAllAsync(instance_room_info_key, CommandFlags.PreferReplica); if (value.Length <= 0) return ExistInstanceRoomState.None; // 내가 원하는 방인지 확인 var instance_room_info = TypeConvertHelper.toClassFromHashEntries(value); if (instance_room_info.roomId != instanceRoomId || instance_room_info.InstanceId != instanceMetaId) return ExistInstanceRoomState.Different; } catch (Exception e) { err_msg = $"Failed to isExistInstanceRoom from Redis !!! : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().fatal(result.toBasicString()); return ExistInstanceRoomState.Exception; } return ExistInstanceRoomState.Exist; } public async Task addInstanceRoomList(string instanceRoomIdBase, string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); await _database.SortedSetAddAsync(instance_room_list_key, instanceRoomId, 0); await _database.KeyExpireAsync(instance_room_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); } catch (Exception e) { err_msg = $"Failed to addInstanceRoomList from Redis !!! : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().fatal(err_msg); } return result; } public async Task joinInstanceRoom(string userGuid, string instanceRoomId, int limitCount) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_member_list_key = GetRoomMemberListKey(instanceRoomId); var push_after_length = await _database.ListRightPushAsync(instance_room_member_list_key, userGuid); if (push_after_length > limitCount) { await _database.ListTrimAsync(instance_room_member_list_key, 0, limitCount - 1); var trim_after_position = await _database.ListPositionAsync(instance_room_member_list_key, userGuid); if (trim_after_position == -1) { err_msg = $"roomId:{instanceRoomId} is full !!! : pushAfterLength:{push_after_length}, limitCount:{limitCount}, trimAfterPosition:{trim_after_position} - userGuid:{userGuid}"; result.setFail(ServerErrorCode.InstanceRoomIsFull, err_msg); Log.getLogger().debug(err_msg); return result; } } await _database.KeyExpireAsync(instance_room_member_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); } catch (Exception e) { err_msg = $"Failed to joinInstanceRoom from Redis !!! : message:{e} - userGuid:{userGuid}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().fatal(result.toBasicString()); } return result; } public async Task increaseInstanceRoomScore(string instanceRoomIdBase, string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); await _database.SortedSetIncrementAsync(instance_room_list_key, instanceRoomId, 1); } catch (Exception e) { err_msg = $"Failed to increaseInstanceRoomScore from Redis !!! : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().fatal(result.toBasicString()); } return result; } public async Task increaseInstanceRoomUgcNpcScore(string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_info_key = GetRoomInfoKey(instanceRoomId); await _database.HashIncrementAsync(instance_room_info_key, nameof(InstanceRoomInfo.UgcNpcCount), 1); } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to decreaseInstanceRoomUgcNpcScore from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } return result; } public async Task> GetInstanceRoomMemberList(string roomId) { List returnList = new(); string roomKey = GetRoomMemberListKey(roomId); var roomMemberList = await _database.ListRangeAsync(roomKey); if (roomMemberList == null) return returnList; returnList.AddRange(roomMemberList.Select(x => x.ToString())); return returnList; } public async Task leaveInstanceRoom(string userGuid, string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_list_key = GetRoomMemberListKey(instanceRoomId); await _database.ListRemoveAsync(instance_room_list_key, userGuid); } catch (Exception e) { err_msg = $"Failed to leaveInstanceRoom from Redis !!! : message:{e} - userGuid:{userGuid}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().fatal(err_msg); } return result; } public async Task decreaseInstanceRoomScore(string instanceRoomIdBase, string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); await _database.SortedSetDecrementAsync(instance_room_list_key, instanceRoomId, 1); } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to decreaseInstanceRoomScore from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } return result; } public async Task decreaseInstanceRoomUgcNpcScore(string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_info_key = GetRoomInfoKey(instanceRoomId); await _database.HashDecrementAsync(instance_room_info_key, nameof(InstanceRoomInfo.UgcNpcCount), 1); } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to decreaseInstanceRoomUgcNpcScore from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } return result; } public async Task deleteInstanceRoom(string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string roomInfoKey = GetRoomInfoKey(instanceRoomId); string roomMemberListKey = GetRoomMemberListKey(instanceRoomId); await _database.KeyDeleteAsync(roomInfoKey); await _database.KeyDeleteAsync(roomMemberListKey); } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to deleteInstanceRoom from Redis !!! : errorCode{error_code}, errMsg:{e.Message}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } return result; } public async Task removeInstanceRoomList(string instanceRoomIdBase, string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); await _database.SortedSetRemoveAsync(instance_room_list_key, instanceRoomId); } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to removeInstanceRoomList from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } return result; } public async Task GetInstanceRoomInfo(string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { var instance_room_info_key = GetRoomInfoKey(instanceRoomId); var value = await _database.HashGetAllAsync(instance_room_info_key, CommandFlags.PreferReplica); if (value.Length <= 0) return null; return TypeConvertHelper.toClassFromHashEntries(value); } catch (Exception e) { err_msg = $"Failed to GetInstanceRoomInfo() from Redis !!! : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().error(err_msg); return null; } } public async Task setInstanceRoomExtraInfo(string roomId, EPlaceType placeType, DateTime startTime) { var result = new Result(); try { var instance_info = await GetInstanceRoomInfo(roomId); if (null == instance_info) { result.setFail(ServerErrorCode.NotExistInstanceRoom); return result; } instance_info.InstancePlaceType = placeType; instance_info.InstanceStartTime = startTime.ToTimestamp(); var room_info_key = GetRoomInfoKey(roomId); var hash_entries = TypeConvertHelper.toHashEntries(instance_info); await _database.HashSetAsync(room_info_key, hash_entries); await _database.KeyExpireAsync(room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); return result; } catch (Exception ex) { result.setFail(ServerErrorCode.InstanceRoomException, $"{ex}"); Log.getLogger().error($"{ex.ToString()}"); return result; } } public async Task setInstanceRoomMyhomeGuid(string roomId, string myhomeGuid) { var result = new Result(); var err_msg = string.Empty; try { var instance_info = await GetInstanceRoomInfo(roomId); if (null == instance_info) { err_msg = $"Failed to GetInstanceRoomInfo() !!! : instanceRoomId:{roomId}"; result.setFail(ServerErrorCode.InstanceRoomNotExistAtRedis, err_msg); Log.getLogger().error(result.toBasicString()); return result; } instance_info.MyhomeGuid = myhomeGuid; var room_info_key = GetRoomInfoKey(roomId); var hash_entries = TypeConvertHelper.toHashEntries(instance_info); await _database.HashSetAsync(room_info_key, hash_entries); await _database.KeyExpireAsync(room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); return result; } catch (Exception e) { err_msg = $"Failed to setInstanceRoomMyhomeGuid from Redis !!! : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().fatal(err_msg); return result; } } public async Task> GetInstanceRoomList(string instanceRoomIdBase, int limitCount) { var err_msg = string.Empty; List result_room_list = new List(); try { string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); var room_list = await _database.SortedSetRangeByScoreAsync(instance_room_list_key, 0, limitCount, Exclude.Stop, Order.Descending); foreach (var roomId in room_list) { result_room_list.Add(roomId.ToString()); } } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to get InstanceRoomList from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; Log.getLogger().error(err_msg); } return result_room_list; } public async Task getInstanceRoomListTotalUserCount(string instanceRoomIdBase) { var err_msg = string.Empty; int total_user_count = 0; try { string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); var sorted_set_entities = await _database.SortedSetRangeByRankWithScoresAsync(instance_room_list_key); foreach (var entity in sorted_set_entities) { if (entity.Score < 0) continue; total_user_count += (int)entity.Score; } } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to get InstanceRoomListTotalUserCount from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; Log.getLogger().error(err_msg); } return total_user_count; } public async Task getInstanceRoomUserCount(string instanceRoomId) { var err_msg = string.Empty; int user_count = 0; try { var room_member_list_key = GetRoomMemberListKey(instanceRoomId); user_count = (int)await _database.ListLengthAsync(room_member_list_key, CommandFlags.PreferReplica); } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to get InstanceRoomUserCount from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; Log.getLogger().error(err_msg); } return user_count; } public async Task keepInstanceRoom(string instanceRoomId) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_info_key = GetRoomInfoKey(instanceRoomId); string intstance_room_member_list_key = GetRoomMemberListKey(instanceRoomId); await _database.KeyExpireAsync(instance_room_info_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); await _database.KeyExpireAsync(intstance_room_member_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME)); } catch(Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to KeepInstanceRoom from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } return result; } public async Task keepInstanceRoomList(string instanceRoomIdBase) { var result = new Result(); var err_msg = string.Empty; try { string instance_room_list_key = GetInstanceRoomListKey(instanceRoomIdBase); await _database.KeyExpireAsync(instance_room_list_key, TimeSpan.FromMilliseconds(KEEP_INSTANCE_ROOM_TIME), ExpireWhen.Always, CommandFlags.FireAndForget); } catch (Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to KeepInstanceRoomList from Redis !!! : : errorCode{error_code}, errMsg:{e.Message}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } return result; } } }