using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Google.Protobuf.WellKnownTypes; using Microsoft.AspNetCore.DataProtection.KeyManagement; using StackExchange.Redis; using NLog.Layouts; using Amazon.CloudWatchLogs.Model; using Microsoft.AspNetCore.Identity; using ServerCore; using ServerBase; using SESSION_ID = System.Int32; using WORLD_ID = System.UInt32; using META_ID = System.UInt32; using ENTITY_GUID = System.String; using ACCOUNT_ID_STRING = System.String; using ACCOUNT_ID = System.String; using OWNER_GUID = System.String; using USER_GUID = System.String; using CHARACTER_GUID = System.String; using ITEM_GUID = System.String; using PARTY_GUID = System.String; namespace ServerCommon; public class LoginCache : CacheBase { public USER_GUID UserGuid = string.Empty; public ACCOUNT_ID_STRING AccountIdString = string.Empty; public ACCOUNT_ID AccountId = string.Empty; public LanguageType LanguageType = LanguageType.None; public DateTime LoginDateTime; public PlayerStateType State = PlayerStateType.None; //클라이언트 주도로 저장되고 있다, 어떤 상태가 관리되는지 확인해 본다. - kangms // 접속중인 서버 정보 public string CurrentServer = String.Empty; // 가입된 파티 식별키 public string PartyGuid = string.Empty; // 접속중인 인스턴트 룸 식별키 public string InstanceRoomId = string.Empty; public Int32 ServerSwitchCount = 0; public class ReservationToSwitchServer { public string OneTimeKey = string.Empty; public string DestServer = string.Empty; } public ReservationToSwitchServer? ReservedToSwitchServer { get; set; } = null; public LoginCache() : base() { } private void copyCacheFromAccountAttribute(AccountAttribute accountAttribute) { NullReferenceCheckHelper.throwIfNull(accountAttribute, () => $"accountAttribute is null !!!"); AccountIdString = accountAttribute.AccountIdString; AccountId = accountAttribute.AccountId; UserGuid = accountAttribute.UserGuid; LanguageType = accountAttribute.LanguageType; LoginDateTime = accountAttribute.LoginDateTime; } public void copyCacheFromAccountAttributeWithServerName(AccountAttribute accountAttribute, string currServer) { copyCacheFromAccountAttribute(accountAttribute); CurrentServer = currServer; } public string toBasicString() { return $"LoginCacheInfo: AccountIdString:{AccountIdString}, AccountId:{AccountId}, UserGuid:{UserGuid}, LanguageType:{LanguageType}, LoginDateTime:{LoginDateTime}"; } } public partial class LoginCacheRequest : RedisRequestPrivateBase { public static readonly double LOGIN_CACHE_EXPIRY_TIME = ConstValue.default_1_hour_to_sec * ConstValue.default_1_sec_to_milisec; public static readonly double SERVER_SWITCH_CACHE_EXPIRY_TIME = 5 * (ConstValue.default_1_min_to_sec * ConstValue.default_1_sec_to_milisec); // Requester User private USER_GUID m_user_guid = string.Empty; private ACCOUNT_ID_STRING m_account_id_string = string.Empty; private ACCOUNT_ID m_account_id = string.Empty; // Request Target UserGuid private USER_GUID m_target_user_guid = string.Empty; // Out private LoginCache? m_login_cache_nullable; public LoginCacheRequest(UserBase owner, RedisConnector redisConnector) : base(owner, redisConnector) { var account_attribute = owner.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"account_attribute is null !!! - {owner.toBasicString()}"); m_account_id_string = account_attribute.AccountIdString; m_account_id = account_attribute.AccountId; m_user_guid = account_attribute.UserGuid; m_target_user_guid = m_user_guid; } public LoginCacheRequest(IServerLogic owner, RedisConnector redisConnector) : base(owner, redisConnector) { } private static string getPrefixOfKey() { return $"login:"; } protected override string onMakeKey() { return $"{getPrefixOfKey()}{m_target_user_guid}"; } protected string onMakeKeyWith(string combinationKey) { return $"login:{combinationKey}"; } public async Task fetchLogin() { var result = new Result(); var err_msg = string.Empty; try { result = await onPrepareRequest(); if (result.isFail()) { return result; } var database = getDatabase(); var redis_value = await database.StringGetAsync(getKey(), CommandFlags.PreferReplica); if (true == redis_value.HasValue) { var curr_login_cache = JsonConvert.DeserializeObject(redis_value.ToString()); if (null == curr_login_cache) { err_msg = $"Failed to convert DeserializeObject of Json !!! : {toBasicString()} - {getOwner().toBasicString()}"; result.setFail(ServerErrorCode.JsonConvertDeserializeFailed, err_msg); Log.getLogger().error(err_msg); return result; } m_login_cache_nullable = curr_login_cache; return result; } else { err_msg = $"Failed to get LoginCache in Redis !!! : Key:{getKey()} - {getOwner().toBasicString()}"; result.setFail(ServerErrorCode.RedisLoginCacheGetFailed, err_msg); Log.getLogger().error(err_msg); return result; } } catch(Exception e) { var error_code = ServerErrorCode.TryCatchException; err_msg = $"Failed to get LoginCache from Redis !!! : : errorCode{error_code}, errMsg:{e.Message} - {getOwner().toBasicString()}"; result.setFail(error_code, err_msg); Log.getLogger().error(err_msg); } return result; } public async Task isLoginableToUserAuth() { var owner = getOwner(); var result = new Result(); var err_msg = string.Empty; try { result = await onPrepareRequest(); if (result.isFail()) { return result; } var database = getDatabase(); var redis_value = await database.StringGetAsync(getKey(), CommandFlags.PreferReplica); if (true == redis_value.HasValue) { var curr_login_cache = JsonConvert.DeserializeObject(redis_value.ToString()); if (null == curr_login_cache) { err_msg = $"Failed to convert DeserializeObject of Json !!! : {redis_value.ToString()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.JsonConvertDeserializeFailed, err_msg); Log.getLogger().error(err_msg); return result; } m_login_cache_nullable = curr_login_cache; if(curr_login_cache.ReservedToSwitchServer != null) { err_msg = $"User is switching Server !!! : {curr_login_cache.toBasicString()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UserIsSwitchingServer, err_msg); return result; } err_msg = $"User Login Duplicated !!! : {curr_login_cache.toBasicString()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UserDuplicatedLogin, err_msg); return result; } return result; } catch (Exception e) { err_msg = $"Failed to get LoginCache from Redis !!! : Exception:{e} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().error(err_msg); return result; } } public async Task isLoginableToGame(string currConnectedServer, string toCheckOtp) { var owner = getOwner(); var result = new Result(); var err_msg = string.Empty; try { result = await fetchLogin(); if (result.isFail()) { return result; } var curr_login_cache = getLoginCache(); NullReferenceCheckHelper.throwIfNull(curr_login_cache, () => "Login Cache is null !!"); if (curr_login_cache.ReservedToSwitchServer == null) { err_msg = $"User is not switching Server !!! : {curr_login_cache.toBasicString()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UserIsNotSwitchingServer, err_msg); return result; } var dest_server = curr_login_cache.ReservedToSwitchServer.DestServer; if (currConnectedServer != dest_server) { err_msg = $"Not matched Dest Server !!! : currServer:{currConnectedServer} == destServer:{dest_server} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.ConnectedServerIsNotDestServer, err_msg); Log.getLogger().error(result.toBasicString()); return result; } var reserved_otp = curr_login_cache.ReservedToSwitchServer.OneTimeKey; if (reserved_otp != toCheckOtp) { err_msg = $"Server switching Otp not match !!! : reservedOtp:{reserved_otp} == toCheckOtp:{toCheckOtp} - {curr_login_cache.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.ServerSwitchingOtpNotMatch, err_msg); return result; } return result; } catch (Exception e) { err_msg = $"Failed to get LoginCache from Redis !!! : Exception:{e} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().error(err_msg); return result; } } public async Task upsertLogin() { var result = new Result(); var err_msg = string.Empty; try { result = await onPrepareRequest(); if (result.isFail()) { return result; } if (getTargetUserGuid() != m_user_guid) { err_msg = $"Failed to set LoginCacheInfo !!!, OwnerUserGuid and TargetUserGuid not match !!! : OwnerUserGuid:{m_user_guid} != TargetUserGuid:{getTargetUserGuid()} - {getOwner().toBasicString()}"; result.setFail(ServerErrorCode.RedisLoginCacheOwnerUserGuidNotMatch, err_msg); Log.getLogger().error(err_msg); return result; } var login_cache = getLoginCache(); NullReferenceCheckHelper.throwIfNull(login_cache, () => "login_cache is null !!!"); var login_cache_json_string = login_cache.toJsonString(); var database = getDatabase(); if (false == await database.StringSetAsync(getKey(), login_cache_json_string, TimeSpan.FromMilliseconds(LOGIN_CACHE_EXPIRY_TIME))) { err_msg = $"Failed to set LoginCacheInfo to Redis !!! : Key:{getKey()} - {getOwner().toBasicString()}"; result.setFail(ServerErrorCode.RedisLoginCacheSetFailed, err_msg); Log.getLogger().error(err_msg); return result; } return result; } catch (Exception e) { err_msg = $"Failed to set LoginCache in Redis !!! : : Exception:{e} - {getOwner().toBasicString()}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().error(err_msg); return result; } } public async Task logout(USER_GUID userGuid) { var key = onMakeKeyWith(userGuid); var database = getDatabase(); await database.KeyDeleteAsync(key); var err_msg = $"Logout in LoginCache - userGuid:{userGuid}"; Log.getLogger().debug(err_msg); } public LoginCache? getLoginCache() => m_login_cache_nullable; public LoginCache newLoginCache() => m_login_cache_nullable = new LoginCache(); public USER_GUID getTargetUserGuid() => m_target_user_guid; protected void setTargetUserGuid(USER_GUID targetUserGuid) => m_target_user_guid = targetUserGuid; public override string toBasicString() { return $"LoginCache: In:UserGuid:{m_user_guid}, AccountId:{m_account_id}, Out:{m_login_cache_nullable?.toBasicString()}"; } } public partial class LoginCacheOtherUserRequest : LoginCacheRequest { public LoginCacheOtherUserRequest(UserBase requester, RedisConnector redisConnector, USER_GUID otherUserGuid) : base(requester, redisConnector) { setTargetUserGuid(otherUserGuid); } public LoginCacheOtherUserRequest(IServerLogic logic, RedisConnector redisConnector, USER_GUID otherUserGuid) : base(logic, redisConnector) { setTargetUserGuid(otherUserGuid); } }