using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Amazon.DynamoDBv2.Model; using Google.Protobuf.WellKnownTypes; using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using MetaAssets; using META_ID = System.UInt32; using LAND_AUCTION_NUMBER = System.Int32; using REQUSTOR_ID = System.String; using USER_GUID = System.String; using USER_NICKNAME = System.String; namespace GameServer; /*============================================================================================= # Land Auction 이벤트별 정보 흐름도 1. 경매 예약 하기 LandAuctionRegistryDoc 생성 2. 경매 활성화 하기 LandAuctionActivityDoc 생성 or 읽기 및 갱신 LandAuctionRegistryDoc 읽기 => Redis 읽기 => 경매 시작 or 종료 or 취소 호출 3. 경매 시작 하기 LandAuctionRegistryDoc 갱신 => Redis 갱신 5. 경매 종료 하기 (낙찰 & 유찰) + 운영 기능 LandAuctionRegistryDoc 갱신 => Redis 갱신 LandAuctionRecord 생성 or 갱신 6. 경매 취소 하기 + 운영 기능 LandAuctionRegistryDoc 읽기 => Redis 갱신 4. 경매 입찰 및 재입찰 하기 Redis 읽기 => LandAuctionRegistryDoc 읽기 => LandAuctionBidPriceDoc 생성 및 갱신 => LandAuctionHighestBidUserDoc 생성 및 갱신 => LandAuctionRegistryDoc 갱신 => Redis 갱신 //===========================================================================================*/ //============================================================================================= // 랜드 경매 버전 //============================================================================================= public class LandAuctionVersion { public DateTime RegisteredVersionTime { get; set; } = DateTimeHelper.MinTime; public DateTime ProcessVersionTime { get; set; } = DateTimeHelper.MinTime; public DateTime HighestRankVersionTime { get; set; } = DateTimeHelper.MinTime; public bool IsSyncRequried { get; set; } = false; // 정보가 변경되어 동기화 필요 여부 (true : 동기화 대상, false: 동기화 필요 없음) public LandAuctionVersion(DateTime registeredVersionTime, DateTime processVersionTime, DateTime highestVersionTime) { RegisteredVersionTime = registeredVersionTime; ProcessVersionTime = processVersionTime; HighestRankVersionTime = highestVersionTime; } public LandAuctionVersion() {} public void resetSyncRequried() { IsSyncRequried = false; } public string toBasicString() => $"LandAuctionVersion(RegisteredVersion:{RegisteredVersionTime}, ProcessVersion:{ProcessVersionTime}, HighestRankVersion:{HighestRankVersionTime})"; } //============================================================================================= // 랜드 경매 체크 결과 //============================================================================================= public class LandAuctionCheckResult { public LandAuctionInfo LandAuctionInfo { get; set; } = new LandAuctionInfo(); public LandAuctionBidType LandAuctionBidType { get; set; } = LandAuctionBidType.None; public CurrencyType MyBidCurrencyType { get; set; } = CurrencyType.None; // 직전 나의 입찰한 재화 종류 (일반 입찰은 최고가인 경우 or 블라인드 입찰에 참여한 경우) public double MyBidPrice { get; set; } = 0; // 직전 나의 입찰가 (일반 입찰은 최고가인 경우 or 블라인드 입찰에 참여한 경우) public LandAuctionVersion Version { get; set; } = new LandAuctionVersion(); } //============================================================================================= // 랜드 경매 입찰 결과 //============================================================================================= public class LandAuctionBidResult { public LandAuction? TargetLandAuction { get; set; } = null; public LandAuctionBidType CurrentBidType { get; set; } = LandAuctionBidType.None; public DateTime BidTime { get; set; } = DateTimeHelper.MinTime; public List ToUpsertItemRequestQueryContexts { get; set; } = new(); public LandAuctionTopBidderCache? CurrTopBidder { get; set; } = null; public LandAuctionTopBidderCache? OldTopBidder { get; set; } = null; } //============================================================================================= // 랜드 경매 낙찰 결과 //============================================================================================= public class LandAuctionWinningResult { public USER_GUID WinningUserGuid { get; set; } = string.Empty; public USER_NICKNAME WinningUserNickname { get; set; } = string.Empty; public ReceivedMailDoc? ReceivedMailDoc { get; set; } = null; } public class LandAuctionAction : EntityActionBase { // 캐시 저장 단계 public enum CacheSaveStep { None, Waiting, // 캐시 저장 대기중 Started, // 캐시 저장 시작 Completed, // 캐시 저장 완료 Failed // 캐시 저장 실패 } // 공통 private DateTime m_land_auction_next_load_time = DateTimeHelper.MinTime; // 랜드 경매 정보 관련 private LAND_AUCTION_NUMBER m_current_land_auction_number = 0; private DateTime m_land_auction_last_loaded_time = DateTimeHelper.MinTime; private CacheSaveStep m_cache_save_step = CacheSaveStep.None; // 랜드 경매 마지막 완료 정보 (경매 & 최고 입찰) private LandAuctionRegistryDoc? m_land_auction_registry_doc_for_record = null; private LandAuctionHighestBidUserDoc? m_land_auction_highest_bid_user_doc_for_record = null; private LandAuctionVersion? m_prev_version; public LandAuctionAction(LandAuction owner) : base(owner) { } public override Task onInit() { var result = new Result(); return Task.FromResult(result); } public override void onClear() { } public async Task<(Result, LandAuctionBidResult?)> tryBidLandAuction( LandAuctionCheckResult checkResult , Player bidRequestor , LandAuctionBidType reqBidType , CurrencyType currencyType, double bidPrice ) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var db_connector = server_logic.getDynamoDbClient(); var result = new Result(); var err_msg = string.Empty; var current_bid_type = toLandAuctionBidType(); if (current_bid_type != reqBidType) { err_msg = $"BidType has been changed !!! : reqBidType:{reqBidType} => currBidType:{current_bid_type} - {land_auction.toBasicString()}, {bidRequestor.toBasicString()}"; Log.getLogger().warn(err_msg); reqBidType = current_bid_type; } ( var is_success , var curr_top_bidder, var old_top_bidder , var is_db_upsert) = await land_auction.tryRankLandAuctionTopBidderCache(bidRequestor.getUserGuid(), bidPrice); if (false == is_success) { err_msg = $"Failed to tryRankLandAuctionTopBidderCache() !!! - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.RedisSortedSetsReadFailed, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } NullReferenceCheckHelper.throwIfNull(curr_top_bidder, () => $"curr_top_bidder is null !!! - {bidRequestor.toBasicString()}"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!! - {bidRequestor.toBasicString()}"); var land_meta_id = checkResult.LandAuctionInfo.LandMetaId; var auction_number = checkResult.LandAuctionInfo.AuctionNumber; var bid_currency_type = checkResult.LandAuctionInfo.CurrencyType; var bid_result = new LandAuctionBidResult(); bid_result.CurrentBidType = reqBidType; bid_result.BidTime = DateTimeHelper.Current; // 최고가 입찰자의 변경이 발생한 경우 if (true == is_db_upsert) { var highest_bid_user_nickname = string.Empty; (result, var nickname_attrib) = await NicknameDoc.findNicknameFromGuid(curr_top_bidder.HighestBidUserGuid); if (result.isSuccess()) { NullReferenceCheckHelper.throwIfNull(nickname_attrib, () => $"nickname_attrib is null !!!"); highest_bid_user_nickname = nickname_attrib.Nickname; } var to_update_highest_bid_users = new Dictionary(); if (LandAuctionBidType.Normal == reqBidType) { to_update_highest_bid_users.Add(nameof(LandAuctionHighestBidUserAttrib.NormalHighestBidPrice) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { N = curr_top_bidder.HighestBidPrice.ToString() } }); to_update_highest_bid_users.Add(nameof(LandAuctionHighestBidUserAttrib.NormalHighestBidUserGuid) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { S = curr_top_bidder.HighestBidUserGuid } }); to_update_highest_bid_users.Add(nameof(LandAuctionHighestBidUserAttrib.NormalHighestBidUserNickname) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { S = highest_bid_user_nickname } }); } to_update_highest_bid_users.Add(nameof(LandAuctionHighestBidUserAttrib.HighestBidPrice) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { N = curr_top_bidder.HighestBidPrice.ToString() } }); to_update_highest_bid_users.Add(nameof(LandAuctionHighestBidUserAttrib.HighestBidUserGuid) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { S = curr_top_bidder.HighestBidUserGuid } }); to_update_highest_bid_users.Add(nameof(LandAuctionHighestBidUserAttrib.HighestBidUserNickname) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { S = highest_bid_user_nickname } }); to_update_highest_bid_users.Add(nameof(LandAuctionHighestBidUserAttrib.HighestRankVersionTime) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { S = DateTimeHelper.Current.toStringWithUtcIso8601() } }); var highest_bid_user_doc = new LandAuctionHighestBidUserDoc((META_ID)land_meta_id, auction_number); var highest_bid_exception_handler = new DynamoDbQueryExceptionNotifier.ExceptionHandler(DynamoDbQueryExceptionNotifier.ConditionalCheckFailed, ServerErrorCode.LandAuctionHighestBidUserAttribError); (result, var highest_bid_user_item_query) = DynamoDbItemRequestHelper.makeUpdateItemRequestWithDoc( highest_bid_user_doc , to_update_highest_bid_users , highest_bid_exception_handler ); if (result.isFail()) { return (result, null); } NullReferenceCheckHelper.throwIfNull(highest_bid_user_item_query, () => $"highest_bid_user_item_query is null !!! - {land_auction.toBasicString()}, {bidRequestor.toBasicString()}"); bid_result.ToUpsertItemRequestQueryContexts.Add(highest_bid_user_item_query); if (null != old_top_bidder) { var old_top_bid_user_guid = old_top_bidder.HighestBidUserGuid; var old_top_bid_price = old_top_bidder.HighestBidPrice; // 차순위로 변경된 최고 입찰자가 Db에 최고 입찰자로 되어 있는지 체크 (UserGuid, BidPrice) 한다. (result, var is_exist_old_top_user) = await land_auction.isExistHighestBidderFromDb(old_top_bid_user_guid, old_top_bid_price); if(result.isFail()) { return (result, null); } if(true == is_exist_old_top_user) { bid_result.OldTopBidder = old_top_bidder; } } } bid_result.CurrTopBidder = curr_top_bidder; // 입찰자의 입찰 정보를 업데이트 한다 !!! { var to_change_attrib_actions = new Dictionary(); // 마지막 입찰금 정보를 저장 한다. to_change_attrib_actions.Add( nameof(LandAuctionRefundBidPriceAttrib.LastBidType) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { S = reqBidType.ToString() } }); to_change_attrib_actions.Add( nameof(LandAuctionRefundBidPriceAttrib.LastBidPrice) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Update , Value = new AttributeValue { N = bidPrice.ToString() } }); // 입찰금을 환급금에 추가 한다. if (LandAuctionBidType.Normal == reqBidType) { to_change_attrib_actions.Add( nameof(LandAuctionRefundBidPriceAttrib.RefundableNormalBidPrice) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Increase , Value = new AttributeValue { N = bidPrice.ToString() } }); } else if (LandAuctionBidType.Blind == reqBidType) { to_change_attrib_actions.Add(nameof(LandAuctionRefundBidPriceAttrib.RefundableBlindBidPrice) , new DynamoDbItemRequestHelper.AttribAction { ValueAction = DynamoDbItemRequestHelper.AttribValueAction.Increase , Value = new AttributeValue { N = bidPrice.ToString() } }); } else { ConditionValidCheckHelper.throwIfFalseWithCondition( () => false , () => $"Invalid BidType !!! : reqBidType:{reqBidType}" + $" - {land_auction.toBasicString()}"); } var refund_bid_price_doc = new LandAuctionRefundBidPriceDoc(bidRequestor.getUserGuid(), (META_ID)land_meta_id, auction_number); var refund_bid_exception_handler = new DynamoDbQueryExceptionNotifier.ExceptionHandler(DynamoDbQueryExceptionNotifier.ConditionalCheckFailed, ServerErrorCode.LandAuctionBidderRefundBidPriceAttribError); (result, var refund_bid_price_query_context) = DynamoDbItemRequestHelper.makeUpdateItemRequestWithDoc( refund_bid_price_doc , to_change_attrib_actions , refund_bid_exception_handler ); if (result.isFail()) { return (result, null); } NullReferenceCheckHelper.throwIfNull(refund_bid_price_query_context, () => $"refund_bid_price_query_context is null !!! - {land_auction.toBasicString()}, {bidRequestor.toBasicString()}"); bid_result.ToUpsertItemRequestQueryContexts.Add(refund_bid_price_query_context); } bid_result.TargetLandAuction = land_auction; return (result, bid_result); } public async Task tryCheckLandAuction(META_ID landMetaId, REQUSTOR_ID requestorId, bool isLoadRequiredMeta) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!! - landMetaId:{landMetaId}, requestorId:{requestorId}"); var result = new Result(); var err_msg = string.Empty; if (false == MetaData.Instance._LandTable.TryGetValue((Int32)landMetaId, out var land_meta_data)) { err_msg = $"Failed to TryGetValue() !!! : landMetaId:{landMetaId} - requestorId:{requestorId}"; result.setFail(ServerErrorCode.LandMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } if (true == isLoadRequiredMeta) { result = await tryLoadLandAuctionMetaWithLock(landMetaId, ServerCommon.MetaHelper.GameConfigMeta.LandAuctionReloadIntervalSec); if (result.isFail()) { return result; } } result = await tryRunLandAuction(requestorId); if (result.isFail()) { return result; } return result; } public async Task<(Result, LandAuctionCheckResult?)> tryCheckActivitingLandAuctionWithTransactionRunner(META_ID landMetaId, REQUSTOR_ID requestorId, bool isLoadRequiredMeta) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!! - landMetaId:{landMetaId}, requestorId:{requestorId}"); var result = new Result(); var err_msg = string.Empty; if (true == isLoadRequiredMeta) { result = await tryLoadLandAuctionMetaWithLock(landMetaId, ServerCommon.MetaHelper.GameConfigMeta.LandAuctionReloadIntervalSec); if (result.isFail()) { return (result, null); } } (result, var auction_check_result) = await tryRunLandAuctionWithRequestorTransactionRunner(requestorId); if (result.isFail()) { return (result, null); } return (result, auction_check_result); } public async Task completedCheckLandAuction( LandAuctionWinningResult? winningResult, OwnedLandHelper.OwnedLandOwnerChangedInfo? changedInfo , REQUSTOR_ID requestorId , QueryExecutorBase queryExecutorBase) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var query_batch = queryExecutorBase.getQueryBatch(); NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!! - {land_auction.toBasicString()}"); var trans_id = query_batch.getTransId(); var server_logic = GameServerApp.getServerLogic(); var db_client = server_logic.getDynamoDbClient(); var redis_connector = server_logic.getRedisConnector(); var result = new Result(); var err_msg = string.Empty; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!! - {land_auction.toBasicString()}"); updateRegistryVersion(registry_attribute.RegisteredVersionTime, registry_attribute.ProcessVersionTime); if (false == isSavableStepCache()) { var is_success = await land_auction.tryReleaseWriteLockWithLandAuction(requestorId); if (false == is_success) { err_msg = $"Failed to tryReleaseWriteLockWithLandAuction() !!! - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); } return result; } var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; var auction_state = registry_attribute.LandAuctionState; if (LandAuctionState.Waiting == auction_state) { // 랜드 경매 대기시 랭킹을 초기화 한다. var bid_price_order_cache = new LandAuctionBidPriceOrderCacheRequest(land_meta_id, redis_connector); await bid_price_order_cache.deleteBidPriceOrder(); var normal_bid_highest_user_cache_request = new LandAuctionNormalBidHighestUserCacheRequest(land_meta_id, redis_connector); await normal_bid_highest_user_cache_request.deleteNormalBidHighestUser(); } result = await land_auction.trySaveToCache(requestorId); if (result.isFail()) { return result; } var auction_result = registry_attribute.LandAuctionResult; err_msg = $"LandAuction completed change LandAuctionState !!! : state:{registry_attribute.LandAuctionState}, result:{auction_result} - {land_auction.toBasicString()}"; Log.getLogger().debug(err_msg); if (LandAuctionResult.Successed == auction_result) { var registry_doc = registry_attribute.getOriginDocBase() as LandAuctionRegistryDoc; NullReferenceCheckHelper.throwIfNull(registry_doc, () => $"registry_doc is null !!! - {land_auction.toBasicString()}"); setLandAuctionRegistryDocForRecord(registry_doc); var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!! - {land_auction.toBasicString()}"); var highest_bid_user_doc = highest_bid_user_attribute.getOriginDocBase() as LandAuctionHighestBidUserDoc; NullReferenceCheckHelper.throwIfNull(highest_bid_user_doc, () => $"highest_bid_user_doc is null !!! - {land_auction.toBasicString()}"); setLandAuctionHighestBidUserDocForRecord(highest_bid_user_doc); if ( null != winningResult && null != winningResult.ReceivedMailDoc ) { var calium_storage_entity = server_logic.findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(calium_storage_entity, () => $"calium_storage_entity is null !!! : {land_auction.toBasicString()}"); var calium_event_action = calium_storage_entity.getEntityAction(); NullReferenceCheckHelper.throwIfNull(calium_event_action, () => $"calium_event_action is null !!! : {land_auction.toBasicString()}"); var highest_bid_price = 0.0; (result, var refund_bid_user_doc) = await LandAuctionDbHelper.readLandAuctionRefundBidPriceDocFromDb(winningResult.WinningUserGuid, land_meta_id, auction_number); if(result.isFail()) { err_msg = $"Failed to readLandAuctionRefundBidPriceDocFromDb() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().fatal(err_msg); } if(null == refund_bid_user_doc) { err_msg = $"refund_bid_user_doc is null !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); } else { var refund_bid_price_attrib = refund_bid_user_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(refund_bid_price_attrib, () => $"refund_bid_price_attrib is null !!! : {land_auction.toBasicString()}"); highest_bid_price = refund_bid_price_attrib.LastBidPrice; result = await calium_event_action.sendCaliumBurnEvent( server_logic , trans_id, winningResult.WinningUserNickname , "LandAuctionSuccess", highest_bid_price ); if (result.isFail()) { err_msg = $"Failed to sendCaliumBurnEvent() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().fatal(err_msg); } } NullReferenceCheckHelper.throwIfNull(changedInfo, () => $"changedInfo is null !!! - {land_auction.toBasicString()}"); (result, var land, var building) = changedInfo.applyChangedToOwnedLand(); if (result.isFail()) { Log.getLogger().error($"Failed to applyChangedToOwnedLand() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"); return result; } NullReferenceCheckHelper.throwIfNull(land, () => $"land is null !!! - {land_auction.toBasicString()}"); NullReferenceCheckHelper.throwIfNull(building, () => $"building is null !!! - {land_auction.toBasicString()}"); await LandNotifyHelper.sendNtfModifyLandInfo(land); await BuildingNotifyHelper.sendNtfModifyBuildingInfo(building); var building_info = await building.toBuildingInfo(); var building_meta_ids = new List() { (META_ID)building_info.BuildingMetaId }; err_msg = $"LandAuction Completed : state:{registry_attribute.LandAuctionState}, result:{auction_result}" + $", winningUserGuid:{winningResult.WinningUserGuid}, winningUserNickname:{winningResult.WinningUserNickname}, highestBidPrice:{highest_bid_price} - {land_auction.toBasicString()}"; Log.getLogger().debug(err_msg); LandAuctionNotifyHelper.broadcast_GS2GS_NTF_LAND_AUCTION_WINNING_BID( winningResult.WinningUserGuid, winningResult.WinningUserNickname , land_meta_id, building_meta_ids , BoolType.True ); var land_log_info = land.toLandLogInfo(); var land_business_log = new LandBusinessLog(land_log_info); var building_log_info = building.toBuildingLogInfo(); var building_business_log = new BuildingBusinessLog(building_log_info); query_batch.appendBusinessLog(land_business_log); query_batch.appendBusinessLog(building_business_log); } } if (LandAuctionResult.None != auction_result) { LandAuctionBusinessLogHelper.writeBusinessLogByLandAuctionResult(land_auction, winningResult, queryExecutorBase); // 관련 모든 캐시를 제거 한다. var land_auction_cache = new LandAuctionCacheRequest(land_meta_id, redis_connector); await land_auction_cache.deleteLandAuction(); var bid_price_order_cache = new LandAuctionBidPriceOrderCacheRequest(land_meta_id, redis_connector); await bid_price_order_cache.deleteBidPriceOrder(); var normal_bid_highest_user_cache_request = new LandAuctionNormalBidHighestUserCacheRequest(land_meta_id, redis_connector); await normal_bid_highest_user_cache_request.deleteNormalBidHighestUser(); // 랭킹 블록키도 제거 한다. await land_auction.tryRemoveLandAuctionTopBidderBlockFromCache(); } return result; } public async Task completedBidLandAuction( Player player, double reqBidPrice , LandAuctionCheckResult checkResult, LandAuctionBidResult bidResult , QueryExecutorBase queryExecutorBase ) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var redis_connector = server_logic.getRedisConnector(); var land_auction_info = checkResult.LandAuctionInfo; NullReferenceCheckHelper.throwIfNull(land_auction_info, () => $"land_auction_info is null !!!"); var land_auction_action = land_auction.getEntityAction(); NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null"); var land_meta_id = land_auction_info.LandMetaId; var auction_number = land_auction_info.AuctionNumber; var result = new Result(); var err_msg = string.Empty; var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null"); // 2. 일반 입찰 최고가 정보를 DB 에서 읽는다. (result, var found_highest_bid_user_doc) = await land_auction.tryGetLandAuctionHighestBidUserDoc((META_ID)land_meta_id, auction_number); if (result.isSuccess()) { if (null != found_highest_bid_user_doc) { result = await land_auction.copyDocToEntityAttributeForLandAuction(highest_bid_user_attribute, found_highest_bid_user_doc, true); if (result.isFail()) { err_msg = $"Failed to copyDocToEntityAttributeForLandAuction() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); } } } // LandAuctionBidType.Normal 상태일 경우 Cache 한다 !!! if (LandAuctionBidType.Normal == checkResult.LandAuctionBidType) { var normal_bid_highest_user_cache = new NormalBidHighestUserCache(); normal_bid_highest_user_cache.LandMetaId = (META_ID)land_meta_id; normal_bid_highest_user_cache.AuctionNumber = auction_number; normal_bid_highest_user_cache.NormalHighestBidPrice = highest_bid_user_attribute.NormalHighestBidPrice; normal_bid_highest_user_cache.NormalHighestBidUserGuid = highest_bid_user_attribute.NormalHighestBidUserGuid; normal_bid_highest_user_cache.NormalHighestBidUserNickname = highest_bid_user_attribute.NormalHighestBidUserNickname; normal_bid_highest_user_cache.HighestRankVersionTime = highest_bid_user_attribute.HighestRankVersionTime; var normal_bid_highest_user_cache_request = new LandAuctionNormalBidHighestUserCacheRequest(normal_bid_highest_user_cache, redis_connector); result = await normal_bid_highest_user_cache_request.upsertNormalBidHighestUser(DateTimeHelper.toRemainingTimeMin(land_auction_info.AuctionEndTime.ToDateTime())); if (result.isFail()) { err_msg = $"Failed to upsertNormalBidHighestUser() !!! : {result.toBasicString()} - landMetaId:{land_meta_id}, auctionNumber:{auction_number}"; Log.getLogger().error(err_msg); } } err_msg = $"LandAuction Bid Completed : bidType:{bidResult.CurrentBidType}, reqBidPrice:{reqBidPrice}" + $", {highest_bid_user_attribute.toBasicString()}, {land_auction.toBasicString()} - {player.toBasicString()}"; Log.getLogger().debug(err_msg); LandAuctionBusinessLogHelper.writeBusinessLogByLandAuctionBid(land_auction, bidResult, queryExecutorBase); land_auction_action.syncLandAuctionToClients(land_auction_info); } private void sendNoticeChat(USER_NICKNAME senderNickname, string message) { var server_logic = GameServerApp.getServerLogic(); var rabbit_mq_4_game = server_logic.getRabbitMqConnector() as RabbitMQ4Game; NullReferenceCheckHelper.throwIfNull(rabbit_mq_4_game, () => $"rabbit_mq_4_game is null !!!"); rabbit_mq_4_game.sendChat(message, senderNickname, ChatType.Notice); } public async Task tryLoadLandAuctionMetaWithLock(META_ID landMetaId, double reloadIntervalSec = 0) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!! - landMetaId:{landMetaId}"); var server_logic = GameServerApp.getServerLogic(); var db_client = server_logic.getDynamoDbClient(); var result = new Result(); var err_msg = string.Empty; // 1. 최신의 경매 정보를 얻어 온다. var activity_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(activity_attribute, () => $"activity_attribute is null !!! - landMetaId:{landMetaId}"); var doc = activity_attribute.getOriginDocBase(); if (null == doc) { // 1.1. Db 에서 활성화 경매 정보를 읽는다. result = await land_auction.tryLoadLandAuctionActivityDoc(landMetaId); if (result.isFail()) { return result; } // 1.2. Db 에서 등록된 경매 정보를 읽는다. result = await land_auction.tryLoadLandAuctionRegistryDoc(landMetaId, activity_attribute.AuctionNumber); if (result.isFail()) { return result; } // 1.3. Db 에서 최고가 입찰자 정보를 읽는다. (result, _) = await land_auction.tryLoadLandAuctionHighestBidUserDoc(landMetaId, activity_attribute.AuctionNumber); if (result.isFail()) { return result; } } else { using (var releaser = await land_auction.getAsyncLock()) { err_msg = $"AsyncLock() Start - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); // 1.2. Db 에서 등록된 경매 정보를 재로딩 시간 설정 만큼 경과 했다면 읽는다. if (getLandAuctionLastLoadedTime() < DateTimeHelper.Current.AddSeconds(reloadIntervalSec)) { // 1.2.1. Db 에서 활성화 경매 정보를 읽는다. result = await land_auction.tryLoadLandAuctionActivityDoc(landMetaId); if (result.isSuccess()) { // 1.2.2. Db 에서 등록된 경매 정보를 읽는다. result = await land_auction.tryLoadLandAuctionRegistryDoc(landMetaId, activity_attribute.AuctionNumber); if (result.isSuccess()) { setLandAuctionLastLoadedTime(DateTimeHelper.Current); // 1.2.2. Db 에서 최고가 입찰자 정보를 읽는다. await land_auction.tryLoadLandAuctionHighestBidUserDoc(landMetaId, activity_attribute.AuctionNumber); } } } err_msg = $"AsyncLock() End - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); } } return result; } public async Task tryLoadLandAuctionMeta(META_ID landMetaId, double reloadIntervalSec = 0) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!! - landMetaId:{landMetaId}"); var server_logic = GameServerApp.getServerLogic(); var db_client = server_logic.getDynamoDbClient(); var result = new Result(); var err_msg = string.Empty; // 1. 최신의 경매 정보를 얻어 온다. var activity_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(activity_attribute, () => $"activity_attribute is null !!! - landMetaId:{landMetaId}"); // 1.2. Db 에서 등록된 경매 정보를 재로딩 시간 설정 만큼 경과 했다면 읽는다. if (getLandAuctionLastLoadedTime() < DateTimeHelper.Current.AddSeconds(reloadIntervalSec)) { // 1.2.1. Db 에서 활성화 경매 정보를 읽는다. result = await land_auction.tryLoadLandAuctionActivityDoc(landMetaId); if (result.isFail()) { return result; } // 1.2.2. Db 에서 등록된 경매 정보를 읽는다. result = await land_auction.tryLoadLandAuctionRegistryDoc(landMetaId, activity_attribute.AuctionNumber); if (result.isFail()) { return result; } setLandAuctionLastLoadedTime(DateTimeHelper.Current); // 1.2.2. Db 에서 최고가 입찰자 정보를 읽는다. (result, _) = await land_auction.tryLoadLandAuctionHighestBidUserDoc(landMetaId, activity_attribute.AuctionNumber); if (result.isFail()) { return result; } } return result; } private async Task tryRunLandAuction(REQUSTOR_ID requestorId) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var redis_connector = server_logic.getRedisDb(); var result = new Result(); var err_msg = string.Empty; var land_meta_id = getLandMetaId(); var auction_number = getAuctionNumber(); var auction_check_result = new LandAuctionCheckResult(); //===================================================================================== // 랜드 경매 Write 권한 얻은 유저는 경매 진행 상태 및 버전 정보를 업데이트 할 수 있다 !!! - kangms //===================================================================================== if (false == isSyncRequired() && true == isLandAuctionState(LandAuctionState.Ended)) { err_msg = $"Already LandAuctionState.Ended of LandAuction !!! : {getLandAuctionVersion()?.toBasicString()} - requestorId:{requestorId}, {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); return result; } // 1. 랜드 경매 Write 권한 얻는다. var is_success = await land_auction.tryAcquireWriteLockWithLandAuction(requestorId, 120); if (true == is_success) { using (var runner_with_scope = new EntityTransactionRunnerWithScopLock(land_auction)) { result = await runner_with_scope.tryInvokeWithAsyncLock(() => checkAndUpdate(requestorId)); if (result.isFail()) { return result; } auction_check_result.LandAuctionInfo = toLandAuctionInfo(); auction_check_result.LandAuctionBidType = toLandAuctionBidType(); Log.getLogger().debug($"Successed tryAcquireWriteLockWithLandAuction() - {land_auction.toBasicString()}"); } } // 2. 랜드 경매 정보를 캐시 or DB 에서 읽기만 하고 읽은 버전 정보가 직전에 읽은 버전보다 최신일 경우 복사 한다. - kangms else { (result, var check_result) = await tryGetLandAuctionCheckResult(true); if (result.isFail()) { return result; } auction_check_result = check_result; err_msg = $"Failed to acquire WriteLock in LandAuction !!! : requestorId:{requestorId} - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); } NullReferenceCheckHelper.throwIfNull(auction_check_result, () => $"auction_check_result is null !!! - requestorId:{requestorId}"); if (isSyncRequired()) { if (true == isLandAuctionState(LandAuctionState.Ended)) { LandAuctionManager.It.activitingToRecord(land_meta_id, land_auction); } syncLandAuctionToClients(auction_check_result.LandAuctionInfo); resetSyncRequired(); } return result; } private async Task<(Result, LandAuctionCheckResult?)> tryRunLandAuctionWithRequestorTransactionRunner(REQUSTOR_ID requestorId) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var redis_connector = server_logic.getRedisDb(); var result = new Result(); var err_msg = string.Empty; var land_meta_id = getLandMetaId(); var auction_number = getAuctionNumber(); var auction_check_result = new LandAuctionCheckResult(); //===================================================================================== // 랜드 경매 Write 권한 얻은 유저만 랜드 경매 처리 로직을 수행할 수 있다 !!! - kangms //===================================================================================== // 1. 랜드 경매 WriteLock 권한 얻는다. var is_success = await land_auction.tryAcquireWriteLockWithLandAuction(requestorId, 3); // 최대 3초 동안 WriteLock 권한을 획득 한다. !!! if (true == is_success) { using (var runner_with_scope = new EntityTransactionRunnerWithScopLock(land_auction, requestorId)) { result = await runner_with_scope.tryInvokeWithAsyncLock(() => checkAndUpdate(requestorId)); if (result.isFail()) { return (result, null); } auction_check_result.LandAuctionInfo = toLandAuctionInfo(); auction_check_result.LandAuctionBidType = toLandAuctionBidType(); } } // 2. 랜드 경매 정보를 Cache or DB 에서 읽고, RegisteredVersion 과 ProcessVersion 을 읽고 더 최신일 경우 로컬 메모리를 업데이트 한다 !!! else { err_msg = $"Failed to acquire WriteLock in LandAuction !!! : requestorId:{requestorId} - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); (result, var check_result) = await tryGetLandAuctionCheckResult(true); if (result.isFail()) { return (result, null); } auction_check_result = check_result; } NullReferenceCheckHelper.throwIfNull(auction_check_result, () => $"auction_check_result is null !!! - requestorId:{requestorId}"); if (isSyncRequired()) { if (true == isLandAuctionState(LandAuctionState.Ended)) { LandAuctionManager.It.activitingToRecord(land_meta_id, land_auction); } syncLandAuctionToClients(auction_check_result.LandAuctionInfo); resetSyncRequired(); } err_msg = $"{auction_check_result.toBasicString()} - requestorId:{requestorId}"; Log.getLogger().debug(err_msg); return (result, auction_check_result); } private async Task checkAndUpdate(REQUSTOR_ID requestorId) { // 본 함수내부에서 LandAuction.AsyncLock 관련 Lock 시도를 하면 DeadLock 이 발생 한다 !!! - kangms var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var redis_connector = server_logic.getRedisConnector(); var land_auction_action = land_auction.getEntityAction(); NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {land_auction.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!! - {land_auction.toBasicString()}"); LandAuctionWinningResult? auction_winning_result = null; OwnedLandHelper.OwnedLandOwnerChangedInfo? owner_changed_info = null; var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; LandAuctionCache? land_auction_cache = null; var land_auction_cache_request = new LandAuctionCacheRequest(land_meta_id, redis_connector); var fetch_result = await land_auction_cache_request.fetchLandAuction(); if (fetch_result.isSuccess()) { land_auction_cache = land_auction_cache_request.getLandAuctionCache(); } else { err_msg = $"Failed to fetchLandAuction() !!! : {fetch_result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().warn(err_msg); } if (null == land_auction_cache) { result = await land_auction_action.tryLoadLandAuctionMeta(land_meta_id); if (result.isFail()) { err_msg = $"Failed to tryLoadLandAuctionMeta() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); return result; } (result, var winning_result, var changed_info) = await land_auction_action.tryUpdateLandAuctionResult(); if (result.isFail()) { return result; } auction_winning_result = winning_result; owner_changed_info = changed_info; } else { if (registry_attribute.AuctionNumber > land_auction_cache.AuctionNumber) { // 로컬 정보의 AuctionNumber 증가된 경우 캐시 정보를 신뢰할 수 없으므로, 즉시 제거 한다. await land_auction_cache_request.deleteLandAuction(); } else { if (registry_attribute.ProcessVersionTime < land_auction_cache.ProcessVersionTime) { registry_attribute.copyProcessInfoFromCache(land_auction_cache); } } (result, var winning_result, var changed_info) = await land_auction_action.tryUpdateLandAuctionResult(); if (result.isFail()) { return result; } auction_winning_result = winning_result; owner_changed_info = changed_info; } var batch = new QueryBatchEx( land_auction, LogActionType.LandAuctionCheck , server_logic.getDynamoDbClient() , true); { if (null != auction_winning_result && null != auction_winning_result.ReceivedMailDoc) { batch.addQuery(new DBQEntityWrite(auction_winning_result.ReceivedMailDoc)); } if (null != owner_changed_info) { NullReferenceCheckHelper.throwIfNull(owner_changed_info.LandDoc, () => $"owner_changed_info.LandDoc is null !!!"); NullReferenceCheckHelper.throwIfNull(owner_changed_info.OwnedLandDoc, () => $"owner_changed_info.OwnedLandDoc is null !!!"); NullReferenceCheckHelper.throwIfNull(owner_changed_info.BuildingDoc, () => $"owner_changed_info.BuildingDoc is null !!!"); NullReferenceCheckHelper.throwIfNull(owner_changed_info.OwnedBuildingDoc, () => $"owner_changed_info.OwnedBuildingDoc is null !!!"); batch.addQuery(new DBQEntityWrite(owner_changed_info.LandDoc)); batch.addQuery(new DBQEntityWrite(owner_changed_info.OwnedLandDoc)); batch.addQuery(new DBQEntityWrite(owner_changed_info.BuildingDoc)); batch.addQuery(new DBQEntityWrite(owner_changed_info.OwnedBuildingDoc)); } batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); batch.addQuery(new QueryFinal(), async (_query) => { var completed_result = await land_auction_action.completedCheckLandAuction( auction_winning_result, owner_changed_info , requestorId , _query); if (completed_result.isFail()) { var err_msg = $"Failed to completedCheckLandAuction() !!! : {completed_result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); } return QueryBatchBase.QueryResultType.Success; }); } result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) { return result; } return result; } public async Task recoverTopBidderCacheFromDb() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var redis_connector = server_logic.getRedisDb(); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!! - {land_auction.toBasicString()}"); var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; var result = new Result(); var err_msg = string.Empty; (result, var top_bidder_cache) = await land_auction.tryGetLandAuctionTopBidderCache(); if (result.isFail()) { err_msg = $"Failed to tryGetLandAuctionTopBidderCache() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); return result; } else { if (null == top_bidder_cache) { (result, var is_loaded) = await land_auction.tryLoadLandAuctionHighestBidUserDoc(land_meta_id, auction_number); if (result.isFail()) { err_msg = $"Failed to tryLoadLandAuctionHighestBidUserDoc() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); return result; } if (true == is_loaded) { var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!! - {land_auction.toBasicString()}"); await land_auction.tryRemoveLandAuctionTopBidderBlockFromCache(); (var is_success , var curr_top_bidder , _, _) = await land_auction.tryRankLandAuctionTopBidderCache(highest_bid_user_attribute.HighestBidUserGuid , highest_bid_user_attribute.HighestBidPrice); if (false == is_success) { err_msg = $"Failed to tryRankLandAuctionTopBidderCache() !!! - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.RedisSortedSetsReadFailed, err_msg); Log.getLogger().error(result.toBasicString()); return result; } NullReferenceCheckHelper.throwIfNull(curr_top_bidder, () => $"curr_top_bidder is null !!! - {land_auction.toBasicString()}"); } } } return result; } public async Task<(Result, LandAuctionCheckResult?)> tryGetLandAuctionCheckResult(bool isWithLock) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var redis_connector = server_logic.getRedisDb(); var result = new Result(); var err_msg = string.Empty; var land_meta_id = getLandMetaId(); var auction_number = getAuctionNumber(); var auction_check_result = new LandAuctionCheckResult(); // 1. 랜드 경매 정보를 캐시에서 읽는다. result = await tryFillupLandAuctionCheckResultFromCache(land_meta_id, auction_check_result); if (result.isFail()) { err_msg = $"Failed to tryFillupLandAuctionCheckResultFromCache() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); // 2. 랜드 경매 정보를 DB에서 읽는다. result = await tryFillupLandAuctionCheckResultFromDb(land_meta_id, auction_check_result , isWithLock); if (result.isFail()) { err_msg = $"Failed to tryFillupLandAuctionCheckResultFromDb() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null); } } auction_check_result.LandAuctionInfo = toLandAuctionInfo(); auction_check_result.LandAuctionBidType = toLandAuctionBidType(); return (result, auction_check_result); } private async Task tryFillupLandAuctionCheckResultFromCache(META_ID landMetaId, LandAuctionCheckResult landAuctionCheckResult) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var redis_connector = server_logic.getRedisConnector(); var result = new Result(); var err_msg = string.Empty; var auction_number = getAuctionNumber(); // 1. 랜드 경매 정보를 캐시에서 읽는다. var land_auction_cache_request = new LandAuctionCacheRequest(landMetaId, redis_connector); result = await land_auction_cache_request.fetchLandAuction(); if (result.isFail()) { err_msg = $"Failed to fetchLandAuction() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return result; } var land_auction_cache = land_auction_cache_request.getLandAuctionCache(); if (null == land_auction_cache) { err_msg = $"Empty cache !!!, return getLandAuctionCache() !!! - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionEmptyCacheInRedis, err_msg); return result; } else { landAuctionCheckResult.LandAuctionInfo.LandAuctionState = land_auction_cache.LandAuctionState; landAuctionCheckResult.LandAuctionInfo.LandAuctionResult = land_auction_cache.LandAuctionResult; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!! - {land_auction.toBasicString()}"); result = land_auction.copyCacheToEntityAttributeForLandAuction(registry_attribute, land_auction_cache); if (result.isFail()) { err_msg = $"Failed to copyCacheToEntityAttributeForLandAuction() !!! : {result.toBasicString()}, cacheType:{land_auction_cache.getTypeName()} - landMetaId:{landMetaId}"; Log.getLogger().error(err_msg); return result; } } // 2. 일반 입찰 최고가 유저 정보를 캐시에서 읽는다. var normal_bid_highest_cache_request = new LandAuctionNormalBidHighestUserCacheRequest(landMetaId, redis_connector); result = await normal_bid_highest_cache_request.fetchNormalBidHighestUser(); if (result.isFail()) { err_msg = $"Failed to fetchNormalBidHighestUser() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return result; } var normal_bid_highest_cache = normal_bid_highest_cache_request.getNormalBidHighestUserCache(); if (null == normal_bid_highest_cache) { err_msg = $"Empty cache !!!, return getNormalBidHighestUserCache() !!! - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionEmptyCacheInRedis, err_msg); return result; } else { landAuctionCheckResult.LandAuctionInfo.HighestBid = normal_bid_highest_cache.NormalHighestBidPrice; landAuctionCheckResult.LandAuctionInfo.WinningUserGuid = normal_bid_highest_cache.NormalHighestBidUserGuid; landAuctionCheckResult.LandAuctionInfo.WinningUserNickname = normal_bid_highest_cache.NormalHighestBidUserNickname; var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!! - {land_auction.toBasicString()}"); result = land_auction.copyCacheToEntityAttributeForLandAuction(highest_bid_user_attribute, normal_bid_highest_cache); if (result.isFail()) { err_msg = $"Failed to copyCacheToEntityAttributeForLandAuction() !!! : {result.toBasicString()}, cacheType:{normal_bid_highest_cache.getTypeName()} - landMetaId:{landMetaId}"; Log.getLogger().error(err_msg); return result; } } return result; } public async Task tryFillupLandAuctionCheckResultFromDb(META_ID landMetaId, LandAuctionCheckResult landAuctionCheckResult , bool isWithLock) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var result = new Result(); var err_msg = string.Empty; (result, var found_activity_doc) = await LandAuctionDbHelper.readLandAuctionActivityDocFromDb(landMetaId); if (result.isFail()) { err_msg = $"Failed to readLandAuctionActivityDocFromDb() !!! : {result.toBasicString()} - landMetaId:{landMetaId}"; Log.getLogger().error(err_msg); return result; } NullReferenceCheckHelper.throwIfNull(found_activity_doc, () => $"found_activity_doc is null !!!"); var ativity_attib = found_activity_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(ativity_attib, () => $"ativity_attib is null !!!"); var land_meta_id = ativity_attib.LandMetaId; var auction_number = ativity_attib.AuctionNumber; (result, var found_registry_doc) = await LandAuctionDbHelper.readLandAuctionRegistryDocFromDb(landMetaId, auction_number); if (result.isFail()) { err_msg = $"Failed to readLandAuctionRegistryDocFromDb() !!! : {result.toBasicString()} - landMetaId:{landMetaId}, auctionNumber:{auction_number}"; Log.getLogger().error(err_msg); return result; } else { NullReferenceCheckHelper.throwIfNull(found_registry_doc, () => $"found_registry_doc is null !!!"); var registry_attrib = found_registry_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(registry_attrib, () => $"registry_attrib is null !!!"); landAuctionCheckResult.LandAuctionInfo = new(); landAuctionCheckResult.LandAuctionInfo.LandMetaId = (Int32)land_meta_id; landAuctionCheckResult.LandAuctionInfo.AuctionNumber = auction_number; landAuctionCheckResult.LandAuctionInfo.AuctionReservationNoticeStartTime = registry_attrib.AuctionReservationNoticeStartTime.ToTimestamp(); landAuctionCheckResult.LandAuctionInfo.CurrencyType = registry_attrib.BidCurrencyType; landAuctionCheckResult.LandAuctionInfo.StartingBid = registry_attrib.BidStartPrice; landAuctionCheckResult.LandAuctionInfo.AuctionStartTime = registry_attrib.AuctionStartTime.ToTimestamp(); landAuctionCheckResult.LandAuctionInfo.AuctionEndTime = registry_attrib.AuctionEndTime.ToTimestamp(); landAuctionCheckResult.LandAuctionInfo.LandAuctionState = registry_attrib.LandAuctionState; landAuctionCheckResult.LandAuctionInfo.LandAuctionResult = registry_attrib.LandAuctionResult; landAuctionCheckResult.LandAuctionInfo.IsLandOwnerChanged = LandAuctionResult.Successed == registry_attrib.LandAuctionResult ? BoolType.True : BoolType.False; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!! - {land_auction.toBasicString()}"); result = await land_auction.copyDocToEntityAttributeForLandAuction(registry_attribute, found_registry_doc , isWithLock); if (result.isFail()) { return result; } } (result, var found_highest_bid_user_doc) = await LandAuctionDbHelper.readLandAuctionHighestBidUserDocFromDb(landMetaId, auction_number); if (result.isFail()) { err_msg = $"Failed to readLandAuctionHighestBidUserDocFromDb() !!! : {result.toBasicString()} - landMetaId:{landMetaId}, auctionNumber:{auction_number}"; Log.getLogger().error(err_msg); return result; } if (null != found_highest_bid_user_doc) { var highest_bid_user_attrib = found_highest_bid_user_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attrib, () => $"highest_bid_user_attrib is null !!!"); landAuctionCheckResult.LandAuctionInfo.HighestBid = highest_bid_user_attrib.NormalHighestBidPrice; landAuctionCheckResult.LandAuctionInfo.WinningUserGuid = highest_bid_user_attrib.NormalHighestBidUserGuid; landAuctionCheckResult.LandAuctionInfo.WinningUserNickname = highest_bid_user_attrib.NormalHighestBidUserNickname; var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!! - {land_auction.toBasicString()}"); result = await land_auction.copyDocToEntityAttributeForLandAuction(highest_bid_user_attribute, found_highest_bid_user_doc , isWithLock); if (result.isFail()) { return result; } } return result; } public async Task<(Result, LandAuctionWinningResult?, OwnedLandHelper.OwnedLandOwnerChangedInfo?)> tryUpdateLandAuctionResult() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var result = new Result(); var err_msg = string.Empty; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); var land_meta_id = registry_attribute.LandMetaId; var check_land_owner_result = OwnedLandHelper.checkLandWithoutOwner(land_meta_id); if (check_land_owner_result.isFail()) { Log.getLogger().warn($"Failed to checkLandWithoutOwner() !!! : {check_land_owner_result.toBasicString()} - {land_auction.toBasicString()}"); return (result, null, null); } if (true == registry_attribute.IsCancelAuction) { return (await cancelLandAuctionResult(), null, null); } else if (true == isLandAuctionStateByTime(LandAuctionState.Waiting)) { return (await waitingLandAuctionResult(), null, null); } else if (true == isLandAuctionStateByTime(LandAuctionState.Scheduled)) { return (await scheduleLandAuctionResult(), null, null); } else if (true == isLandAuctionStateByTime(LandAuctionState.Started)) { return (await startLandAuctionResult(), null, null); } else if (true == isLandAuctionStateByTime(LandAuctionState.Ended)) { return await stopLandAuctionResult(); } return (result, null, null); } private async Task cancelLandAuctionResult() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); var result = new Result(); var err_msg = string.Empty; var highest_bid_user_nickname = string.Empty; var land_auction_state = registry_attribute.LandAuctionState; if (LandAuctionState.Ended != land_auction_state) { var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; var bid_currency_type = registry_attribute.BidCurrencyType; registry_attribute.LandAuctionState = LandAuctionState.Ended; registry_attribute.LandAuctionResult = LandAuctionResult.Canceled; Log.getLogger().info($"try cancelLandAuctionResult() !!! - {toBasicString()}"); // 최고 입찰자 정보를 읽고, 랭킹 정보를 블록 처리 한다. !!! (var is_success, var curr_top_bidder) = await land_auction.tryGetLandAuctionTopBidderCacheWithBlock(); if (null == curr_top_bidder) { // DB 정보를 읽는다. (result, var is_loaded) = await land_auction.tryLoadLandAuctionHighestBidUserDoc(land_meta_id, auction_number); if (result.isFail()) { err_msg = $"Failed to tryLoadLandAuctionHighestBidUserDoc() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); return result; } if (true == is_loaded) { var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!!"); curr_top_bidder = new LandAuctionTopBidderCache(); curr_top_bidder.LandMetaId = highest_bid_user_attribute.LandMetaId; curr_top_bidder.HighestBidUserGuid = highest_bid_user_attribute.HighestBidUserGuid; curr_top_bidder.HighestBidPrice = highest_bid_user_attribute.HighestBidPrice; highest_bid_user_nickname = highest_bid_user_attribute.HighestBidUserNickname; } } if (null != curr_top_bidder) { // 최고 입찰자가 존재 하지만 랜드 소유권 변경은 하고, 최고 입찰자 저장만 시킨다. var highest_bid_user_guid = curr_top_bidder.HighestBidUserGuid; // 최고 입찰자의 닉네임이 없다면 if (highest_bid_user_nickname.isNullOrWhiteSpace()) { var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!!"); if (highest_bid_user_attribute.HighestBidUserGuid == highest_bid_user_guid) { highest_bid_user_nickname = highest_bid_user_attribute.HighestBidUserNickname; } else { (result, var nickname_attrib) = await NicknameDoc.findNicknameFromGuid(highest_bid_user_guid); NullReferenceCheckHelper.throwIfNull(nickname_attrib, () => $"nickname_attrib is null !!!"); if (result.isFail()) { err_msg = $"Not found Highest Bid UserNickname !!! : userGuid:{highest_bid_user_guid} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); } else { // 랭킹에 한하여 Cache => DB 에 저장 한다. highest_bid_user_attribute.LandMetaId = curr_top_bidder.LandMetaId; highest_bid_user_attribute.AuctionNumber = auction_number; highest_bid_user_attribute.BidCurrencyType = bid_currency_type; highest_bid_user_attribute.HighestBidPrice = curr_top_bidder.HighestBidPrice; highest_bid_user_attribute.HighestBidUserGuid = curr_top_bidder.HighestBidUserGuid; highest_bid_user_attribute.HighestBidUserNickname = nickname_attrib.Nickname; highest_bid_user_attribute.modifiedEntityAttribute(true); highest_bid_user_nickname = nickname_attrib.Nickname; } } } } registry_attribute.ProcessVersionTime = DateTimeHelper.Current; registry_attribute.modifiedEntityAttribute(); // 랜드 경매 종료 기록 정보를 등록 한다. var land_auction_record_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_record_attribute, () => $"land_auction_record_attribute is null !!!"); land_auction_record_attribute.LandMetaId = registry_attribute.LandMetaId; land_auction_record_attribute.AuctionNumber = registry_attribute.AuctionNumber; land_auction_record_attribute.modifiedEntityAttribute(true); m_cache_save_step = CacheSaveStep.Waiting; } return result; } private async Task waitingLandAuctionResult() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; var result = new Result(); var err_msg = string.Empty; var land_auction_state = registry_attribute.LandAuctionState; if (LandAuctionState.Waiting != land_auction_state) { registry_attribute.LandAuctionState = LandAuctionState.Waiting; registry_attribute.LandAuctionResult = LandAuctionResult.None; registry_attribute.ProcessVersionTime = DateTimeHelper.Current; registry_attribute.modifiedEntityAttribute(); m_cache_save_step = CacheSaveStep.Waiting; err_msg = $"try change state of LandAuction : to:{registry_attribute.LandAuctionState} <= from:{land_auction_state} - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); } return await Task.FromResult(result); } private async Task scheduleLandAuctionResult() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var db_connector = server_logic.getDynamoDbClient(); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; var result = new Result(); var err_msg = string.Empty; var land_auction_state = registry_attribute.LandAuctionState; if (LandAuctionState.Scheduled != land_auction_state) { registry_attribute.LandAuctionState = LandAuctionState.Scheduled; registry_attribute.LandAuctionResult = LandAuctionResult.None; registry_attribute.ProcessVersionTime = DateTimeHelper.Current; registry_attribute.modifiedEntityAttribute(); var highest_bid_user_doc = new LandAuctionHighestBidUserDoc((META_ID)land_meta_id, auction_number); var highest_bid_user_attrib = highest_bid_user_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attrib, () => $"highest_bid_user_attrib is null !!! - {land_auction.toBasicString()}"); highest_bid_user_attrib.BidCurrencyType = registry_attribute.BidCurrencyType; highest_bid_user_attrib.NormalHighestBidPrice = 0; highest_bid_user_attrib.NormalHighestBidUserGuid = string.Empty; highest_bid_user_attrib.NormalHighestBidUserNickname = string.Empty; highest_bid_user_attrib.HighestBidPrice = 0; highest_bid_user_attrib.HighestBidUserGuid = string.Empty; highest_bid_user_attrib.HighestBidUserNickname = string.Empty; result = await db_connector.simpleUpsertDocumentWithDocType(highest_bid_user_doc); if (result.isFail()) { err_msg = $"Failed to simpleUpsertDocumentWithDocType !!! : {highest_bid_user_doc.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); return result; } m_cache_save_step = CacheSaveStep.Waiting; err_msg = $"try change state of LandAuction : to:{registry_attribute.LandAuctionState} <= from:{land_auction_state} - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); } return result; } private async Task startLandAuctionResult() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var result = new Result(); var err_msg = string.Empty; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); var land_auction_state = registry_attribute.LandAuctionState; if (LandAuctionState.Started != land_auction_state) { registry_attribute.LandAuctionState = LandAuctionState.Started; registry_attribute.ProcessVersionTime = DateTimeHelper.Current; registry_attribute.modifiedEntityAttribute(); m_cache_save_step = CacheSaveStep.Waiting; err_msg = $"try change state of LandAuction : to:{registry_attribute.LandAuctionState} <= from:{land_auction_state} - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); } result = await recoverTopBidderCacheFromDb(); if (result.isFail()) { err_msg = $"Failed to recoverTopBidderCacheFromDb() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); return result; } return await Task.FromResult(result); } private async Task<(Result, LandAuctionWinningResult?, OwnedLandHelper.OwnedLandOwnerChangedInfo?)> stopLandAuctionResult() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); var result = new Result(); var err_msg = string.Empty; var land_auction_result = registry_attribute.LandAuctionResult; var land_auction_state = registry_attribute.LandAuctionState; if (LandAuctionState.Ended == land_auction_state) { return (result, null, null); } var auction_winning_result = new LandAuctionWinningResult(); OwnedLandHelper.OwnedLandOwnerChangedInfo? owner_changed_info = null; var highest_bid_user_nickname = string.Empty; var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; var bid_currency_type = registry_attribute.BidCurrencyType; // 랜드 경매 종료 상태를 설정 한다. registry_attribute.LandAuctionState = LandAuctionState.Ended; // 최고 입찰자 정보를 읽고, 랭킹 정보를 블록 처리 한다. !!! (var is_success, var curr_top_bidder) = await land_auction.tryGetLandAuctionTopBidderCacheWithBlock(); if (null == curr_top_bidder) { // DB 정보를 읽는다. (result, var is_loaded) = await land_auction.tryLoadLandAuctionHighestBidUserDoc(land_meta_id, auction_number); if (result.isFail()) { err_msg = $"Failed to tryLoadLandAuctionHighestBidUserDoc() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null, null); } if ( false == is_loaded || false == isExistLandAuctionHighestBidUser() ) { // 최고 입찰자는 없으므로, 랜드 경매 결과를 실패 처리 한다. registry_attribute.LandAuctionResult = LandAuctionResult.Failed; } else { var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!!"); curr_top_bidder = new LandAuctionTopBidderCache(); curr_top_bidder.LandMetaId = highest_bid_user_attribute.LandMetaId; curr_top_bidder.HighestBidUserGuid = highest_bid_user_attribute.HighestBidUserGuid; curr_top_bidder.HighestBidPrice = highest_bid_user_attribute.HighestBidPrice; highest_bid_user_nickname = highest_bid_user_attribute.HighestBidUserNickname; } } if (null != curr_top_bidder) { // 최고 입찰자가 존재하므로 랜드 경매 결과는 성공 이다 !!! registry_attribute.LandAuctionResult = LandAuctionResult.Successed; if (false == GameServerApp.getServerLogic().getLandManager().tryGetLand((Int32)land_meta_id, out var found_land)) { err_msg = $"Not found Land !!! : landMetaId:{land_meta_id} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandNotFound, err_msg); Log.getLogger().error(err_msg); return (result, null, null); } var highest_bid_user_guid = curr_top_bidder.HighestBidUserGuid; // 랜드 소유를 변경하기 기본 조건들을 체크하고 Doc 정보를 구성 한다. (result, var changed_info) = await OwnedLandHelper.makeOwnedLandOwnerChangedInfo(land_meta_id, OwnedType.Own, highest_bid_user_guid); if (result.isFail()) { err_msg = $"Failed to makeOwnedLandOwnerChangedInfo() !!! : {result.toBasicString()}, landMetaId:{land_meta_id} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null, null); } NullReferenceCheckHelper.throwIfNull(changed_info, () => $"changed_info is null !!! - {land_auction.toBasicString()}"); owner_changed_info = changed_info; // 최고 입찰자의 닉네임이 없다면 if (highest_bid_user_nickname.isNullOrWhiteSpace()) { var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!!"); if (highest_bid_user_attribute.HighestBidUserGuid == highest_bid_user_guid) { highest_bid_user_nickname = highest_bid_user_attribute.HighestBidUserNickname; } else { (result, var nickname_attrib) = await NicknameDoc.findNicknameFromGuid(highest_bid_user_guid); NullReferenceCheckHelper.throwIfNull(nickname_attrib, () => $"nickname_attrib is null !!!"); if (result.isFail()) { err_msg = $"Not found Highest Bid UserNickname !!! : userGuid:{highest_bid_user_guid} - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); } else { // 랭킹에 한하여 Cache => DB 에 저장 한다. highest_bid_user_attribute.LandMetaId = curr_top_bidder.LandMetaId; highest_bid_user_attribute.AuctionNumber = auction_number; highest_bid_user_attribute.BidCurrencyType = bid_currency_type; highest_bid_user_attribute.HighestBidPrice = curr_top_bidder.HighestBidPrice; highest_bid_user_attribute.HighestBidUserGuid = curr_top_bidder.HighestBidUserGuid; highest_bid_user_attribute.HighestBidUserNickname = nickname_attrib.Nickname; highest_bid_user_attribute.modifiedEntityAttribute(true); highest_bid_user_nickname = nickname_attrib.Nickname; } } } (result, var new_mail_doc) = await createMailWithLandItem(highest_bid_user_guid, highest_bid_user_nickname); if (result.isFail()) { return (result, null, null); } auction_winning_result.WinningUserGuid = highest_bid_user_guid; auction_winning_result.WinningUserNickname = highest_bid_user_nickname; auction_winning_result.ReceivedMailDoc = new_mail_doc; } registry_attribute.ProcessVersionTime = DateTimeHelper.Current; registry_attribute.modifiedEntityAttribute(); // 랜드 경매 종료 기록 정보를 등록 한다. var land_auction_record_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_record_attribute, () => $"land_auction_record_attribute is null !!!"); land_auction_record_attribute.LandMetaId = registry_attribute.LandMetaId; land_auction_record_attribute.AuctionNumber = registry_attribute.AuctionNumber; land_auction_record_attribute.modifiedEntityAttribute(true); m_cache_save_step = CacheSaveStep.Waiting; err_msg = $"try change result and state of LandAuction :" + $" Result(to:{registry_attribute.LandAuctionResult} <= from:{land_auction_result})" + $" State(to:{registry_attribute.LandAuctionState} <= from:{land_auction_state})" + $" - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); return (result, auction_winning_result, owner_changed_info); } public Result isBidableLandAuction( LandAuctionCheckResult checkResult , CurrencyType currencyType, double bidPrice ) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_meta_id = checkResult.LandAuctionInfo.LandMetaId; var auction_number = checkResult.LandAuctionInfo.AuctionNumber; var result = isValid(auction_number); if (result.isFail()) { return result; } // 1. 랜드 소유권 설정 상태를 체크 한다. result = OwnedLandHelper.checkLandWithoutOwner((META_ID)land_meta_id); if (result.isFail()) { return result; } // 2. 경매 시작 상태를 체크 한다. if (false == checkResult.isLandAuctionState(LandAuctionState.Started)) { var remain_sec = DateTimeHelper.toRemainingTimeSec(checkResult.LandAuctionInfo.AuctionStartTime.ToDateTime()); var err_msg = $"Not Started state LandAuction !!! : remainSecFromStartTime:{remain_sec} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionNotStarted, err_msg); return result; } // 3. 경매 입찰 종류를 체크 한다. var curr_bid_currency_type = checkResult.LandAuctionInfo.CurrencyType; if (curr_bid_currency_type != currencyType) { var err_msg = $"Invalid Bid CurrencyType !!! : currBidCurrencyType:{curr_bid_currency_type} == reqCurrencyType:{currencyType} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionBidCurrencyTypeInvalid, err_msg); return result; } // 4. 입찰금을 체크 한다. var start_bid_price = checkResult.LandAuctionInfo.StartingBid; if (start_bid_price > bidPrice) { var err_msg = $"Not enough bid price !!!, less than Starting Bid Price : startBidPrice:{start_bid_price} <= reqCurrencyType:{bidPrice} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionBidPriceNotEnough, err_msg); return result; } return result; } public bool isHistory() { if (true == isLandAuctionState(LandAuctionState.Ended) && true == isLandAuctionResult(LandAuctionResult.Successed)) { return true; } return false; } public bool isScheduling() { if ((true == isLandAuctionStateByTime(LandAuctionState.Scheduled) || true == isLandAuctionStateByTime(LandAuctionState.Started)) && true != isLandAuctionState(LandAuctionState.Ended)) { return true; } return false; } public bool isLandAuctionResult(LandAuctionResult auctionResult) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); return auctionResult == registry_attribute.LandAuctionResult; } public bool isLandAuctionStateByTime(LandAuctionState state) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); return registry_attribute.isLandAuctionStateByTime(state); } public bool isLandAuctionState(LandAuctionState state) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); return registry_attribute.LandAuctionState == state; } public async Task<(Result, ReceivedMailDoc?)> createMailWithLandItem(USER_GUID highestBidUserGuid, USER_NICKNAME highestBidUserNickname) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var redis_connector = server_logic.getRedisDb(); var result = new Result(); var err_msg = string.Empty; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; var system_mail_key = "LandAuctionResult"; if (MetaData.Instance.SystemMailMetaData.TryGetValue(system_mail_key, out var systemMailMetaData) == false) { err_msg = $"Not found SystemMailMeta !!! : mailKey:{system_mail_key} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.SystemMailMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } if (false == MetaData.Instance._LandTable.TryGetValue((Int32)land_meta_id, out var land_meta_data)) { err_msg = $"Not found LandMeta !!! : landMetaId:{land_meta_id} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } if (0 >= land_meta_data.LinkedItem) { err_msg = $"Not set LinkedItem of LandItem : landMetaId:{land_meta_id} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionLandItemNotSet, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } if (false == server_logic.getLandManager().tryGetLand((Int32)land_meta_id, out var found_land)) { err_msg = $"Not found Land !!! : landMetaId:{land_meta_id} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } var found_land_auction = LandAuctionManager.It.findActivitingLandAuction(land_meta_id); if (null == found_land_auction) { err_msg = $"Not found activiting LandAuction !!! : landMetaId:{land_meta_id} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } var contens_arguments = new List(); contens_arguments.Add(land_meta_data.getStringKeyOfLandName()); contens_arguments.Add(highestBidUserNickname); contens_arguments.Add(found_land_auction.getWinningBidValue()); var mail_items = new List(); var mail_item = new ServerCommon.MailItem() { ItemId = (META_ID)land_meta_data.LinkedItem, Count = 1 }; mail_items.Add(mail_item); (result, var new_mail_doc) = await Mail.createSystemMailWithMeta( highestBidUserGuid, highestBidUserNickname, systemMailMetaData , contens_arguments , mail_items, ServerCommon.MetaHelper.GameConfigMeta.LandAuctionSuccessedMailPeriodMinutes); if (result.isFail()) { err_msg = $"Failed to createSystemMailWithMeta() !!! : {result.toBasicString()} - {land_auction.toBasicString()}"; Log.getLogger().info(err_msg); return (result, null); } return (result, new_mail_doc); } public async Task sendTopBidderInfo( LandAuctionBidType bidType , LandAuctionTopBidderCache currTopBidder, LandAuctionTopBidderCache? oldTopBidder) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var result = new Result(); var err_msg = string.Empty; if(null == oldTopBidder) { var curr_top_user_guid = currTopBidder.HighestBidUserGuid; await sendTopBidderInfoByUSER_GUID(curr_top_user_guid, BoolType.False); } else { var has_received_refund_mail = LandAuctionBidType.Normal == bidType ? BoolType.True : BoolType.False; // 현재 최고가 입찰자가 직전 최고가 입찰자와 동일한 경우 if (currTopBidder.HighestBidUserGuid == oldTopBidder.HighestBidUserGuid) { var curr_top_user_guid = currTopBidder.HighestBidUserGuid; await sendTopBidderInfoByUSER_GUID(curr_top_user_guid, has_received_refund_mail); } else { var curr_top_user_guid = currTopBidder.HighestBidUserGuid; await sendTopBidderInfoByUSER_GUID(curr_top_user_guid, BoolType.False); var old_top_user_guid = oldTopBidder.HighestBidUserGuid; await sendTopBidderInfoByUSER_GUID(old_top_user_guid, has_received_refund_mail); } } } private async Task sendTopBidderInfoByUSER_GUID(USER_GUID targetUserGuid, BoolType hasReceivedRefundMail = BoolType.True) { await Task.CompletedTask; var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var server_logic = GameServerApp.getServerLogic(); var result = new Result(); var err_msg = string.Empty; var curr_top_user_guid = targetUserGuid; var bid_currency_type = getBidCurrencyType(); // LandAuctionBidType.Normal과 LandAuctionBidType.Blind 는 모두 Normal 기준 값으로 동기화 한다. - kangms var highest_bid_price = getNormalHighestBidPrice(); var highest_bid_user_guid = getNormalHighestBidUserGuid(); var highest_bid_user_nickaname = getNormalHighestBidUserNickname(); if (false == LandAuctionNotifyHelper.broadcast_GS2GS_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE( curr_top_user_guid, hasReceivedRefundMail , getLandMetaId() , bid_currency_type, highest_bid_price , highest_bid_user_guid, highest_bid_user_nickaname)) { err_msg = $"Failed to send_GS2GS_NTF_LAND_AUCTION_HIGHEST_BIDDER_CHANGE() !!! - {land_auction.toBasicString()}"; Log.getLogger().error(err_msg); } } public async Task tryLoadLandAuctionForRecord(LAND_AUCTION_NUMBER landAuctionNumber) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var result = new Result(); var err_msg = string.Empty; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); if (registry_attribute.AuctionNumber == landAuctionNumber) { var registry_doc = registry_attribute.getOriginDocBase() as LandAuctionRegistryDoc; NullReferenceCheckHelper.throwIfNull(registry_doc, () => $"registry_doc is null !!!"); setLandAuctionRegistryDocForRecord(registry_doc); } else { (result, var record_doc) = await LandAuctionDbHelper.readLandAuctionRegistryDocFromDb(registry_attribute.LandMetaId, landAuctionNumber); if (result.isFail()) { err_msg = $"Failed to readLandAuctionRegistryDocFromDb() !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); return result; } NullReferenceCheckHelper.throwIfNull(record_doc, () => $"record_doc is null !!!"); setLandAuctionRegistryDocForRecord(record_doc); } var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!!"); if (highest_bid_user_attribute.AuctionNumber == landAuctionNumber) { var highest_bid_user_doc = highest_bid_user_attribute.getOriginDocBase() as LandAuctionHighestBidUserDoc; NullReferenceCheckHelper.throwIfNull(highest_bid_user_doc, () => $"highest_bid_user_doc is null !!!"); setLandAuctionHighestBidUserDocForRecord(highest_bid_user_doc); } else { (result, var highest_bid_user_doc) = await LandAuctionDbHelper.readLandAuctionHighestBidUserDocFromDb(registry_attribute.LandMetaId, landAuctionNumber); if (result.isFail()) { err_msg = $"Failed to readLandAuctionHighestBidUserDocFromDb() !!! : {result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); return result; } if (null != highest_bid_user_doc) { setLandAuctionHighestBidUserDocForRecord(highest_bid_user_doc); } } return result; } private bool isExistLandAuctionHighestBidUser() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attribute, () => $"highest_bid_user_attribute is null !!!"); if( 0 >= highest_bid_user_attribute.LandMetaId || true == highest_bid_user_attribute.HighestBidUserGuid.isNullOrWhiteSpace() || true == highest_bid_user_attribute.HighestBidUserNickname.isNullOrWhiteSpace() ) { return false; } return true; } public Result isValid(LAND_AUCTION_NUMBER auctionNumber) { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var result = new Result(); var err_msg = string.Empty; var curr_auction_number = getAuctionNumber(); if(curr_auction_number != auctionNumber) { err_msg = $"Invalid LandAuction auctionNumber !!! : currAuctionNumber:{curr_auction_number} != reqAuctionNumber:{auctionNumber} - {land_auction.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionInvalidAuctionNumber, err_msg); return result; } return result; } public LandAuctionBidType toLandAuctionBidType() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); // 랜드 경매 종료 상태를 설정 한다. var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); // 현재 입찰 종류를 반환 받는다. return LandAuctionMetaHelper.toCurrentBidType(registry_attribute.AuctionEndTime); } public LandAuctionState getLandAuctionState() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); return registry_attribute.LandAuctionState; } public async Task toLandInfo() { var server_logic = GameServerApp.getServerLogic(); var land_manager = server_logic.getLandManager(); var result = new Result(); var err_msg = string.Empty; var land_meta_id = getLandMetaId(); if (false == land_manager.tryGetLand((Int32)land_meta_id, out var found_land)) { err_msg = $"Failed to tryGetLand() !!! : landMetaId:{land_meta_id}"; Log.getLogger().error(err_msg); return new LandInfo(); } return await found_land.toLandInfo(); } public LandAuctionInfo toLandAuctionInfo() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null !!!"); var land_auction_info = new LandAuctionInfo(); land_auction_info.LandMetaId = (Int32)registry_attribute.LandMetaId; land_auction_info.AuctionNumber = registry_attribute.AuctionNumber; land_auction_info.AuctionReservationNoticeStartTime = registry_attribute.AuctionReservationNoticeStartTime.ToTimestamp(); land_auction_info.CurrencyType = registry_attribute.BidCurrencyType; land_auction_info.StartingBid = getStartBidPrice(); land_auction_info.AuctionStartTime = registry_attribute.AuctionStartTime.ToTimestamp(); land_auction_info.AuctionEndTime = registry_attribute.AuctionEndTime.ToTimestamp(); var bid_type = toLandAuctionBidType(); if(LandAuctionBidType.Normal == bid_type) { land_auction_info.HighestBid = getNormalHighestBidPrice(); land_auction_info.WinningUserGuid = getNormalHighestBidUserGuid(); land_auction_info.WinningUserNickname = getNormalHighestBidUserNickname(); } else { land_auction_info.HighestBid = getNormalHighestBidPrice(); land_auction_info.WinningUserGuid = getNormalHighestBidUserGuid(); land_auction_info.WinningUserNickname = getNormalHighestBidUserNickname(); } land_auction_info.LandAuctionState = registry_attribute.LandAuctionState; land_auction_info.LandAuctionResult = registry_attribute.LandAuctionResult; land_auction_info.IsLandOwnerChanged = toLandOwnerChanged(); return land_auction_info; } public async Task toLandAuctionCompact() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var registry_doc_4_record= getLandAuctionRegistryDocForRecord(); if(null == registry_doc_4_record) { var err_msg = string.Empty; var auction_number = getAuctionNumber(); var record_result = await tryLoadLandAuctionForRecord(auction_number); if (record_result.isFail()) { err_msg = $"Failed to tryLoadLandAuctionForRecord() !!! : {record_result.toBasicString()} - {toBasicString()}"; Log.getLogger().error(err_msg); return null; } registry_doc_4_record = getLandAuctionRegistryDocForRecord(); NullReferenceCheckHelper.throwIfNull(registry_doc_4_record, () => $"registry_doc_4_record is null !!!"); } var registry_attrib = registry_doc_4_record.getAttrib(); NullReferenceCheckHelper.throwIfNull(registry_attrib, () => $"registry_attrib is null !!!"); var land_auction_compact = new LandAuctionCompact(); land_auction_compact.LandMetaId = (Int32)registry_attrib.LandMetaId; land_auction_compact.AuctionNumber = registry_attrib.AuctionNumber; land_auction_compact.AuctionReservationNoticeStartTime = registry_attrib.AuctionReservationNoticeStartTime.ToTimestamp(); land_auction_compact.CurrencyType = registry_attrib.BidCurrencyType; land_auction_compact.AuctionStartTime = registry_attrib.AuctionStartTime.ToTimestamp(); land_auction_compact.AuctionEndTime = registry_attrib.AuctionEndTime.ToTimestamp(); var highest_bid_user_doc_4_record = getLandAuctionHighestBidUserDocForRecord(); if(null != highest_bid_user_doc_4_record) { var highest_bid_user_attrib = highest_bid_user_doc_4_record.getAttrib(); NullReferenceCheckHelper.throwIfNull(highest_bid_user_attrib, () => $"highest_bid_user_attrib is null !!!"); land_auction_compact.HighestBid = highest_bid_user_attrib.HighestBidPrice; land_auction_compact.WinningUserGuid = highest_bid_user_attrib.HighestBidUserGuid; land_auction_compact.WinningUserNickname = highest_bid_user_attrib.HighestBidUserNickname; } land_auction_compact.LandAuctionState = registry_attrib.LandAuctionState; land_auction_compact.LandAuctionResult = registry_attrib.LandAuctionResult; return land_auction_compact; } private LandAuctionHighestBidUserDoc? getLandAuctionHighestBidUserDocForRecord() => m_land_auction_highest_bid_user_doc_for_record; private void setLandAuctionHighestBidUserDocForRecord(LandAuctionHighestBidUserDoc landAuctionHighestBidUserDoc) => m_land_auction_highest_bid_user_doc_for_record = landAuctionHighestBidUserDoc; private LandAuctionRegistryDoc? getLandAuctionRegistryDocForRecord() => m_land_auction_registry_doc_for_record; private void setLandAuctionRegistryDocForRecord(LandAuctionRegistryDoc landAuctionRegistryDoc) => m_land_auction_registry_doc_for_record = landAuctionRegistryDoc; public LandAuctionSummary toLandAuctionSummary() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_summary = new LandAuctionSummary(); land_auction_summary.LandAuctionInfo = toLandAuctionInfo(); return land_auction_summary; } public double getHighestBidPrice() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_highest_bid_user_attribute, () => $"land_auction_highest_bid_user_attribute is null !!!"); return land_auction_highest_bid_user_attribute.HighestBidPrice; } public USER_GUID getHighestBidUserGuid() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_highest_bid_user_attribute, () => $"land_auction_highest_bid_user_attribute is null !!!"); return land_auction_highest_bid_user_attribute.HighestBidUserGuid; } public USER_NICKNAME getHighestBidUserNickname() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_highest_bid_user_attribute, () => $"land_auction_highest_bid_user_attribute is null !!!"); return land_auction_highest_bid_user_attribute.HighestBidUserNickname; } public double getNormalHighestBidPrice() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_highest_bid_user_attribute, () => $"land_auction_highest_bid_user_attribute is null !!!"); return land_auction_highest_bid_user_attribute.NormalHighestBidPrice; } public USER_GUID getNormalHighestBidUserGuid() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_highest_bid_user_attribute, () => $"land_auction_highest_bid_user_attribute is null !!!"); return land_auction_highest_bid_user_attribute.NormalHighestBidUserGuid; } public USER_NICKNAME getNormalHighestBidUserNickname() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_highest_bid_user_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_highest_bid_user_attribute, () => $"land_auction_highest_bid_user_attribute is null !!!"); return land_auction_highest_bid_user_attribute.NormalHighestBidUserNickname; } public double getStartBidPrice() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_registry_attribute, () => $"land_auction_registry_attribute is null !!!"); return land_auction_registry_attribute.BidStartPrice; } public CurrencyType getBidCurrencyType() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_registry_attribute, () => $"land_auction_registry_attribute is null !!!"); return land_auction_registry_attribute.BidCurrencyType; } public BoolType toLandOwnerChanged() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_registry_attribute, () => $"land_auction_registry_attribute is null !!!"); return LandAuctionResult.Successed == land_auction_registry_attribute.LandAuctionResult ? BoolType.True : BoolType.False; } public LAND_AUCTION_NUMBER getAuctionNumber() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_registry_attribute, () => $"land_auction_registry_attribute is null !!!"); return land_auction_registry_attribute.AuctionNumber; } public META_ID getLandMetaId() { var land_auction = getOwner() as LandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null !!!"); var land_auction_registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(land_auction_registry_attribute, () => $"land_auction_registry_attribute is null !!!"); return land_auction_registry_attribute.LandMetaId; } public void syncLandAuctionToClients(LandAuctionInfo landAuctionInfo) { var land_auction_summary = new LandAuctionSummary(); land_auction_summary.LandAuctionInfo = landAuctionInfo; getLandAuctionVersion()?.toBasicString(); LandAuctionNotifyHelper.broadcast_GS2C_NTF_LAND_AUCTION_SUMMARY(land_auction_summary); } private void resetSyncRequired() { if (null == m_prev_version) return; m_prev_version.resetSyncRequried(); } public bool isSyncRequired() { return null == m_prev_version ? false : m_prev_version.IsSyncRequried; } public void updateHighestRankVersion(DateTime highestRankVersionTime) { if (null == m_prev_version) { m_prev_version = new LandAuctionVersion(DateTimeHelper.MinTime, DateTimeHelper.MinTime, highestRankVersionTime); m_prev_version.IsSyncRequried = true; } else { (var is_success, var new_version) = m_prev_version.checkChangedVersion(DateTimeHelper.MinTime, DateTimeHelper.MinTime, highestRankVersionTime); if (true == is_success) { m_prev_version = new_version; } } } public void updateProcessVersion(DateTime processVersionTime) { if (null == m_prev_version) { m_prev_version = new LandAuctionVersion(DateTimeHelper.MinTime, processVersionTime, DateTimeHelper.MinTime); m_prev_version.IsSyncRequried = true; } else { (var is_success, var new_version) = m_prev_version.checkChangedVersion(DateTimeHelper.MinTime, processVersionTime, DateTimeHelper.MinTime); if (true == is_success) { m_prev_version = new_version; } } } public void updateRegistryVersion(DateTime registeredVersionTime, DateTime processVersionTime) { if(null == m_prev_version) { m_prev_version = new LandAuctionVersion(registeredVersionTime, processVersionTime, DateTimeHelper.MinTime); m_prev_version.IsSyncRequried = true; } else { (var is_success, var new_version) = m_prev_version.checkChangedVersion(registeredVersionTime, processVersionTime, DateTimeHelper.MinTime); if (true == is_success) { m_prev_version = new_version; } } } public LandAuctionVersion? getLandAuctionVersion() => m_prev_version; public void resetCacheSaveStep() => m_cache_save_step = CacheSaveStep.None; public bool isSavableStepCache() => CacheSaveStep.Waiting == m_cache_save_step; public CacheSaveStep getCaheSaveStep() => m_cache_save_step; public DateTime getLandAuctionLastLoadedTime() => m_land_auction_last_loaded_time; public void setLandAuctionLastLoadedTime(DateTime loadedTime) => m_land_auction_last_loaded_time = loadedTime; public LAND_AUCTION_NUMBER getCurrentLanAuctionNumber() => m_current_land_auction_number; public void setCurrentLandAuctionNumber(LAND_AUCTION_NUMBER currentNumber) => m_current_land_auction_number = currentNumber; }