public void TryGet() { var shopState = new ShopState(); var agentAddress = new PrivateKey().ToAddress(); var avatarAddress = new PrivateKey().ToAddress(); var productId = Guid.NewGuid(); var weaponRow = new EquipmentItemSheet.Row(); weaponRow.Set(new[] { "10100000", "Weapon", "0", "Normal", "0", "ATK", "1", "2", "10100000", }); var itemUsable = new Weapon( weaponRow, Guid.NewGuid(), 0); var price = new FungibleAssetValue(new Currency("NCG", 2, minter: null)); var shopItem = new ShopItem( agentAddress, avatarAddress, productId, price, itemUsable); shopState.Register(shopItem); Assert.True(shopState.TryGet(agentAddress, shopItem.ProductId, out var outShopItem)); Assert.Equal(shopItem, outShopItem); shopState.Unregister(shopItem); Assert.False(shopState.TryGet(agentAddress, shopItem.ProductId, out _)); }
public override IAccountStateDelta Execute(IActionContext context) { IActionContext ctx = context; var states = ctx.PreviousStates; if (ctx.Rehearsal) { states = states .SetState(buyerAvatarAddress, MarkChanged) .SetState(ctx.Signer, MarkChanged) .SetState(sellerAvatarAddress, MarkChanged) .MarkBalanceChanged(GoldCurrencyMock, ctx.Signer, sellerAgentAddress); return(states.SetState(ShopState.Address, MarkChanged)); } if (ctx.Signer.Equals(sellerAgentAddress)) { return(LogError(context, "Aborted as the signer is the seller.")); } var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; Log.Debug("Buy exec started."); if (!states.TryGetAgentAvatarStates(ctx.Signer, buyerAvatarAddress, out var buyerAgentState, out var buyerAvatarState)) { return(LogError(context, "Aborted as the avatar state of the buyer was failed to load.")); } sw.Stop(); Log.Debug("Buy Get Buyer AgentAvatarStates: {Elapsed}", sw.Elapsed); sw.Restart(); if (!buyerAvatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world)) { return(LogError(context, "Aborted as the WorldInformation was failed to load.")); } if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop) { // 스테이지 클리어 부족 에러. return(LogError( context, "Aborted as the signer is not cleared the minimum stage level required to use shop yet: {ClearedLevel} < {RequiredLevel}.", world.StageClearedId, GameConfig.RequireClearedStageLevel.ActionsInShop )); } if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary d)) { return(LogError(context, "Aborted as the shop state was failed to load.")); } var shopState = new ShopState(d); sw.Stop(); Log.Debug("Buy Get ShopState: {Elapsed}", sw.Elapsed); sw.Restart(); Log.Debug("Execute Buy; buyer: {Buyer} seller: {Seller}", buyerAvatarAddress, sellerAvatarAddress); // 상점에서 구매할 아이템을 찾는다. if (!shopState.TryGet(sellerAgentAddress, productId, out var outPair)) { return(LogError( context, "Aborted as the shop item ({ProductId}) was failed to get from the seller agent ({SellerAgent}).", productId, sellerAgentAddress )); } sw.Stop(); Log.Debug($"Buy Get Item: {sw.Elapsed}"); sw.Restart(); if (!states.TryGetAgentAvatarStates(sellerAgentAddress, sellerAvatarAddress, out var sellerAgentState, out var sellerAvatarState)) { return(LogError( context, "Aborted as the seller agent/avatar was filed to load from {SellerAgent}/{SellerAvatar}.", sellerAgentAddress, sellerAvatarAddress )); } sw.Stop(); Log.Debug($"Buy Get Seller AgentAvatarStates: {sw.Elapsed}"); sw.Restart(); // 돈은 있냐? BigInteger buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); if (buyerBalance < outPair.Value.Price) { return(LogError( context, "Aborted as the buyer ({Buyer}) has no sufficient gold: {BuyerBalance} < {ItemPrice}", ctx.Signer, buyerBalance, outPair.Value.Price )); } var taxedPrice = (BigInteger)decimal.Round((decimal)outPair.Value.Price * 0.92m); // 구매자의 돈을 판매자에게 송금한다. states = states.TransferAsset(context.Signer, sellerAgentAddress, states.GetGoldCurrency(), outPair.Value.Price); // 상점에서 구매할 아이템을 제거한다. if (!shopState.Unregister(sellerAgentAddress, outPair.Value)) { return(LogError( context, "Aborted as the item ({@ProductId}) was failed to unregister from seller ({Seller})'s inventory.", outPair.Value.ProductId, sellerAgentAddress )); } // 구매자, 판매자에게 결과 메일 전송 buyerResult = new BuyerResult { shopItem = outPair.Value, itemUsable = outPair.Value.ItemUsable }; var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); buyerResult.id = buyerMail.id; sellerResult = new SellerResult { shopItem = outPair.Value, itemUsable = outPair.Value.ItemUsable, gold = taxedPrice }; var sellerMail = new SellerMail(sellerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); sellerResult.id = sellerMail.id; buyerAvatarState.Update(buyerMail); buyerAvatarState.UpdateFromAddItem(buyerResult.itemUsable, false); sellerAvatarState.Update(sellerMail); // 퀘스트 업데이트 buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, outPair.Value.Price); sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, outPair.Value.Price); var timestamp = DateTimeOffset.UtcNow; buyerAvatarState.updatedAt = timestamp; buyerAvatarState.blockIndex = ctx.BlockIndex; sellerAvatarState.updatedAt = timestamp; sellerAvatarState.blockIndex = ctx.BlockIndex; buyerAvatarState.UpdateQuestRewards(ctx); sellerAvatarState.UpdateQuestRewards(ctx); states = states.SetState(sellerAvatarAddress, sellerAvatarState.Serialize()); sw.Stop(); Log.Debug("Buy Set Seller AvatarState: {Elapsed}", sw.Elapsed); sw.Restart(); states = states.SetState(buyerAvatarAddress, buyerAvatarState.Serialize()); sw.Stop(); Log.Debug("Buy Set Buyer AvatarState: {Elapsed}", sw.Elapsed); sw.Restart(); states = states.SetState(ShopState.Address, shopState.Serialize()); sw.Stop(); var ended = DateTimeOffset.UtcNow; Log.Debug("Buy Set ShopState: {Elapsed}", sw.Elapsed); Log.Debug("Buy Total Executed Time: {Elapsed}", ended - started); return(states.SetState(ctx.Signer, buyerAgentState.Serialize())); }