using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections.Concurrent; using Amazon.S3.Model; using Amazon.DynamoDBv2.Model; using Amazon.DynamoDBv2; using Renci.SshNet.Security; using ServerCore; using ServerBase; using ServerCommon; using META_ID = System.UInt32; using USER_GUID = System.String; using LAND_AUCTION_NUMBER = System.Int32; namespace GameServer { public class UserLandAuctionAction : EntityActionBase { private readonly ConcurrentDictionary m_refund_bid_prices = new(); public UserLandAuctionAction(Player owner) : base(owner) { } public override void onClear() { return; } public override async Task onInit() { await Task.CompletedTask; var result = new Result(); return result; } public async Task tryLoadLandAuctionBidPriceAllFromDb() { var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null"); var server_logic = GameServerApp.getServerLogic(); var db_connector = server_logic.getDynamoDbClient(); var result = new Result(); var err_msg = string.Empty; (result, var read_docs) = await LandAuctionDbHelper.readLandAuctionRefundBidPriceDocsFromDb(owner); if(result.isFail()) { err_msg = $"Failed to readLandAuctionRefundBidPriceDocsFromDb() !!! : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } NullReferenceCheckHelper.throwIfNull(read_docs, () => $"read_docs is null !!! - {owner.toBasicString()}"); foreach (var read_doc in read_docs) { var refund_bid_price = new LandAuctionRefundBidPrice(owner); var init_result = await refund_bid_price.onInit(); if (init_result.isFail()) { Log.getLogger().error(init_result.toBasicString()); continue; } var primary_key = read_doc.getPrimaryKey(); if (false == m_refund_bid_prices.TryAdd(primary_key.SK, refund_bid_price)) { err_msg = $"Failed to TryAdd() !!!, duplicated SK !!! : {primary_key.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); continue; } var refund_bid_price_action = refund_bid_price.getEntityAction(); NullReferenceCheckHelper.throwIfNull(refund_bid_price_action, () => $"refund_bid_price_action is null !!!"); var load_result = refund_bid_price_action.tryLoadRefundBidPriceFromDoc(read_doc); if (load_result.isFail()) { Log.getLogger().error(load_result.toBasicString()); continue; } } return result; } public async Task updateRefundBidPriceAll() { var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null"); var server_logic = GameServerApp.getServerLogic(); var db_connector = server_logic.getDynamoDbClient(); var redis_connector = server_logic.getRedisDb(); var fn_land_auction_bid_price_refund = async delegate () { var result = new Result(); var err_msg = string.Empty; var to_delete_refund_bid_prices = new List(); var to_send_new_mail_docs = new List(); var refund_bid_prices = m_refund_bid_prices.Values.ToList(); foreach (var refund_bid_price in refund_bid_prices) { var refund_bid_price_action = refund_bid_price.getEntityAction(); NullReferenceCheckHelper.throwIfNull(refund_bid_price_action, () => $"refund_bid_price_action is null !!! - {player.toBasicString()}"); var update_result = await refund_bid_price_action.tryUpdateRefundBidPrice(); if (update_result.isFail()) { err_msg = $"Failed to tryUpdateRefundBidPrice() !!! : {update_result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); continue; } if(true == refund_bid_price_action.isDeletable()) { var key = LandAuctionRefundBidPriceDoc.makeCombinationKeyForSK(refund_bid_price_action.getLandMetaId(), refund_bid_price_action.getAuctionNumber()); m_refund_bid_prices.Remove(key, out _); } } return result; }; var result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "RefundBidPrice", fn_land_auction_bid_price_refund); if (result.isFail()) { var err_msg = $"Failed to runTransactionRunnerSafely()!!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); } } public async Task<(Result, LandAuctionBidResult?)> tryBidLandAuction( LandAuctionBidType reqBidType , CurrencyType currencyType, double bidPrice , LandAuctionCheckResult checkResult ) { var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null"); var server_logic = GameServerApp.getServerLogic(); var db_connector = server_logic.getDynamoDbClient(); var redis_connector = server_logic.getRedisDb(); var result = new Result(); var err_msg = string.Empty; var land_meta_id = (META_ID)checkResult.LandAuctionInfo.LandMetaId; var auction_number = checkResult.LandAuctionInfo.AuctionNumber; 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} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.LandAuctionNotFound, err_msg); Log.getLogger().error(err_msg); return (result, null); } var land_auction_action = found_land_auction.getEntityAction(); NullReferenceCheckHelper.throwIfNull(land_auction_action, () => $"land_auction_action is null !!! - {owner.toBasicString()}"); // 1. 입찰 가능 여부를 체크 한다. result = land_auction_action.isBidableLandAuction(checkResult, currencyType, bidPrice); if (result.isFail()) { return (result, null); } // 2. 금전 소모 가능 여부를 체크 한다. var money_action = owner.getEntityAction(); NullReferenceCheckHelper.throwIfNull(money_action, () => $"money_action is null !!! - {owner.toBasicString()}"); result = await money_action.spendMoney(currencyType, bidPrice, useCaliumEvent:false); if(result.isFail()) { return (result, null); } // 3. LandAuctionBidPriceDoc를 존재하는 지 체크하고, 없으면 생성 한다 !!! var bid_price_doc = new LandAuctionRefundBidPriceDoc(owner.getUserGuid(), land_meta_id, auction_number); result = await bid_price_doc.newDoc4Query(); if (result.isFail()) { return (result, null); } result = await db_connector.createIfNotExist(bid_price_doc , (bid_price_doc) => { var bid_price_attrib = bid_price_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(bid_price_attrib, () => $"bid_price_attrib is null !!! - {owner.toBasicString()}"); bid_price_attrib.LandMetaId = land_meta_id; bid_price_attrib.AuctionNumber = auction_number; bid_price_attrib.BidUserGuid = owner.getUserGuid(); bid_price_attrib.BidCurrencyType = currencyType; } ); if (result.isFail()) { return (result, null); } // 4. 입찰을 시도 한다. ( result, var bid_result ) = await land_auction_action.tryBidLandAuction( checkResult , owner , reqBidType , currencyType, bidPrice ); if(result.isFail()) { return (result, null); } NullReferenceCheckHelper.throwIfNull(bid_result, () => $"bid_result is null !!! - {owner.toBasicString()}"); return (result, bid_result); } public async Task completedRefundBidPrice(LandAuctionBidResult bidResult, QueryExecutorBase queryExecutorBase) { await Task.CompletedTask; var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null"); var land_auction = bidResult.TargetLandAuction; NullReferenceCheckHelper.throwIfNull(land_auction, () => $"land_auction is null - {owner.toBasicString()}"); var curr_top_bidder = bidResult.CurrTopBidder; NullReferenceCheckHelper.throwIfNull(curr_top_bidder, () => $"curr_top_bidder is null - {owner.toBasicString()}"); var server_logic = GameServerApp.getServerLogic(); var db_connector = server_logic.getDynamoDbClient(); var result = new Result(); var err_msg = string.Empty; var registry_attribute = land_auction.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(registry_attribute, () => $"registry_attribute is null - {owner.toBasicString()}"); var land_meta_id = registry_attribute.LandMetaId; var auction_number = registry_attribute.AuctionNumber; // 입찰자의 입찰 환급 처리 LandAuctionRefundBidPrice 객체를 생성 및 등록 한다. { (result, var refund_bid_price_doc) = await LandAuctionDbHelper.readLandAuctionRefundBidPriceDocFromDb(owner, land_meta_id, auction_number); if (result.isFail()) { err_msg = $"Failed to readLandAuctionRefundBidPriceDocFromDb() !!! : {result.toBasicString()}, {land_auction.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); } else { NullReferenceCheckHelper.throwIfNull(refund_bid_price_doc, () => $"refund_bid_price_doc is null - {owner.toBasicString()}"); var refund_bid_price_attrib = refund_bid_price_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(refund_bid_price_attrib, () => $"refund_bid_price_attrib is null - {owner.toBasicString()}"); var key = LandAuctionRefundBidPriceDoc.makeCombinationKeyForSK(land_meta_id, auction_number); if (false == m_refund_bid_prices.TryGetValue(key, out var found_bid_price)) { found_bid_price = new LandAuctionRefundBidPrice(owner); result = await found_bid_price.onInit(); if (result.isFail()) { err_msg = $"Failed to onInit() !!! : {result.toBasicString()}, {land_auction.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().fatal(err_msg); } else { m_refund_bid_prices[key] = found_bid_price; } } var refund_bid_price_attribute = found_bid_price.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(refund_bid_price_attribute, () => $"refund_bid_price_attribute is null - {owner.toBasicString()}"); result = found_bid_price.copyDocToEntityAttributeForRefundBidPrice(refund_bid_price_attribute, refund_bid_price_doc); if (result.isFail()) { err_msg = $"Failed to copyDocToEntityAttributeForRefundBidPrice() !!! : {result.toBasicString()}, docType:{refund_bid_price_doc.getTypeName()}, {result.toBasicString()}, {land_auction.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().fatal(err_msg); } } } // 최고 입찰자와 차순위 입찰자의 비즈니스 로그를 작성 한다. !!! var bidder_caches = new List(); bidder_caches.Add(curr_top_bidder); if (null != bidResult.OldTopBidder) { bidder_caches.Add(bidResult.OldTopBidder); } foreach (var bidder_cache in bidder_caches) { var target_user_guid = bidder_cache.HighestBidUserGuid; (result, var refund_bid_price_doc) = await LandAuctionDbHelper.readLandAuctionRefundBidPriceDocFromDb(target_user_guid, land_meta_id, auction_number); if (result.isFail()) { err_msg = $"Failed to readLandAuctionRefundBidPriceDocFromDb() !!! : {result.toBasicString()}, landMetaId:{land_meta_id}, auctionNumber:{auction_number}, bidUserGuid:{target_user_guid} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); continue; } if (null == refund_bid_price_doc) { err_msg = $"Not found LandAuctionRefundBidPriceDoc !!! : landMetaId:{land_meta_id}, auctionNumber:{auction_number} - {owner.toBasicString()}"; Log.getLogger().warn(err_msg); continue; } LandAuctionBusinessLogHelper.writeBusinessLogByLandAuctionBidPriceRefund(refund_bid_price_doc, registry_attribute.LandAuctionResult, queryExecutorBase); } } public async Task<(Result, LandAuctionRefundBidPriceDoc?)> findRefundBidPriceDoc(META_ID landMetaId, LAND_AUCTION_NUMBER auctionNumber) { var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var server_logic = GameServerApp.getServerLogic(); var db_client = server_logic.getDynamoDbClient(); var result = new Result(); var err_msg = string.Empty; (result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey( owner.getUserGuid() , LandAuctionRefundBidPriceDoc.makeCombinationKeyForSK(landMetaId, auctionNumber) ); if (result.isFail()) { err_msg = $"Failed to makePrimaryKey() !!! : {result.toBasicString()} - landMetaId:{landMetaId}"; Log.getLogger().error(err_msg); return (result, null); } NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!!"); var query_config = db_client.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK); (result, var found_refund_bid_price_doc) = await db_client.simpleQueryDocTypeWithQueryOperationConfig(query_config, false); if (result.isFail()) { err_msg = $"Failed to parse simpleQueryDocTypeWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()} - landMetaId:{landMetaId}, auctionNumber:{auctionNumber}"; Log.getLogger().error(err_msg); return (result, null); } return (result, found_refund_bid_price_doc); } } }