public void Execute( ItemType itemType, string guid, int itemCount, int inventoryCount, int expectedCount, bool fromPreviousAction, bool legacy ) { var avatarState = _initialState.GetAvatarState(_avatarAddress); ITradableItem tradableItem; var itemId = new Guid(guid); var orderId = Guid.NewGuid(); var updateSellOrderId = Guid.NewGuid(); ItemSubType itemSubType; const long requiredBlockIndex = Order.ExpirationInterval; switch (itemType) { case ItemType.Equipment: { var itemUsable = ItemFactory.CreateItemUsable( _tableSheets.EquipmentItemSheet.First, itemId, requiredBlockIndex); tradableItem = itemUsable; itemSubType = itemUsable.ItemSubType; break; } case ItemType.Costume: { var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); costume.Update(requiredBlockIndex); tradableItem = costume; itemSubType = costume.ItemSubType; break; } default: { var material = ItemFactory.CreateTradableMaterial( _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); itemSubType = material.ItemSubType; material.RequiredBlockIndex = requiredBlockIndex; tradableItem = material; break; } } var shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); var shopState = new ShardedShopStateV2(shardedShopAddress); var order = OrderFactory.Create( _agentAddress, _avatarAddress, orderId, new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), tradableItem.TradableId, requiredBlockIndex, itemSubType, itemCount ); var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); var prevState = _initialState; if (inventoryCount > 1) { for (int i = 0; i < inventoryCount; i++) { // Different RequiredBlockIndex for divide inventory slot. if (tradableItem is ITradableFungibleItem tradableFungibleItem) { var tradable = (TradableMaterial)tradableFungibleItem.Clone(); tradable.RequiredBlockIndex = tradableItem.RequiredBlockIndex - i; avatarState.inventory.AddItem2(tradable, 2 - i); } } } else { avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount); } var sellItem = legacy ? order.Sell2(avatarState) : order.Sell3(avatarState); var orderDigest = legacy ? order.Digest2(avatarState, _tableSheets.CostumeStatSheet) : order.Digest(avatarState, _tableSheets.CostumeStatSheet); shopState.Add(orderDigest, requiredBlockIndex); orderDigestList.Add(orderDigest); if (legacy) { Assert.True(avatarState.inventory.TryGetTradableItems(itemId, requiredBlockIndex * 2, itemCount, out _)); } else { Assert.True(avatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); } Assert.Equal(inventoryCount, avatarState.inventory.Items.Count); Assert.Equal(expectedCount, avatarState.inventory.Items.Sum(i => i.count)); Assert.Single(shopState.OrderDigestList); Assert.Single(orderDigestList.OrderDigestList); Assert.Equal(requiredBlockIndex * 2, sellItem.RequiredBlockIndex); if (fromPreviousAction) { prevState = prevState.SetState(_avatarAddress, avatarState.Serialize()); } else { prevState = prevState .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) .SetState(_avatarAddress, avatarState.SerializeV2()); } prevState = prevState .SetState(Addresses.GetItemAddress(itemId), sellItem.Serialize()) .SetState(Order.DeriveAddress(order.OrderId), order.Serialize()) .SetState(orderDigestList.Address, orderDigestList.Serialize()) .SetState(shardedShopAddress, shopState.Serialize()); var currencyState = prevState.GetGoldCurrency(); var price = new FungibleAssetValue(currencyState, ProductPrice, 0); var action = new UpdateSell0 { orderId = orderId, updateSellOrderId = updateSellOrderId, tradableId = itemId, sellerAvatarAddress = _avatarAddress, itemSubType = itemSubType, price = price, count = itemCount, }; var nextState = action.Execute(new ActionContext { BlockIndex = 101, PreviousStates = prevState, Random = new TestRandom(), Rehearsal = false, Signer = _agentAddress, }); var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId); var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(updateSellShopAddress)); Assert.Equal(1, nextShopState.OrderDigestList.Count); Assert.NotEqual(orderId, nextShopState.OrderDigestList.First().OrderId); Assert.Equal(updateSellOrderId, nextShopState.OrderDigestList.First().OrderId); Assert.Equal(itemId, nextShopState.OrderDigestList.First().TradableId); Assert.Equal(requiredBlockIndex + 101, nextShopState.OrderDigestList.First().ExpiredBlockIndex); }
public override IAccountStateDelta Execute(IActionContext context) { var states = context.PreviousStates; var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); var shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId); var updateSellOrderAddress = Order.DeriveAddress(updateSellOrderId); var itemAddress = Addresses.GetItemAddress(tradableId); var digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); if (context.Rehearsal) { return(states .SetState(context.Signer, MarkChanged) .SetState(itemAddress, MarkChanged) .SetState(digestListAddress, MarkChanged) .SetState(shopAddress, MarkChanged) .SetState(updateSellShopAddress, MarkChanged) .SetState(updateSellOrderAddress, MarkChanged) .SetState(inventoryAddress, MarkChanged) .SetState(worldInformationAddress, MarkChanged) .SetState(questListAddress, MarkChanged) .SetState(sellerAvatarAddress, MarkChanged)); } // common var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; Log.Verbose("{AddressesHex} updateSell exec started", addressesHex); if (price.Sign < 0) { throw new InvalidPriceException( $"{addressesHex} Aborted as the price is less than zero: {price}."); } if (!states.TryGetAvatarStateV2(context.Signer, sellerAvatarAddress, out var avatarState, out _)) { throw new FailedLoadStateException( $"{addressesHex} Aborted as the avatar state of the signer was failed to load."); } sw.Stop(); Log.Verbose("{AddressesHex} Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); sw.Restart(); if (!avatarState.worldInformation.IsStageCleared( GameConfig.RequireClearedStageLevel.ActionsInShop)) { avatarState.worldInformation.TryGetLastClearedStageId(out var current); throw new NotEnoughClearedStageLevelException( addressesHex, GameConfig.RequireClearedStageLevel.ActionsInShop, current); } sw.Stop(); avatarState.updatedAt = context.BlockIndex; avatarState.blockIndex = context.BlockIndex; if (!states.TryGetState(digestListAddress, out Dictionary rawList)) { throw new FailedLoadStateException($"{addressesHex} failed to load {nameof(OrderDigest)}({digestListAddress})."); } var digestList = new OrderDigestListState(rawList); // migration method avatarState.inventory.UnlockInvalidSlot(digestList, context.Signer, sellerAvatarAddress); avatarState.inventory.ReconfigureFungibleItem(digestList, tradableId); avatarState.inventory.LockByReferringToDigestList(digestList, tradableId, context.BlockIndex); // // for sell cancel Log.Verbose("{AddressesHex} UpdateSell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); sw.Restart(); if (!states.TryGetState(shopAddress, out BxDictionary shopStateDict)) { throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(ShardedShopStateV2)}({shopAddress})."); } sw.Stop(); Log.Verbose("{AddressesHex} UpdateSell Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); sw.Restart(); if (!states.TryGetState(Order.DeriveAddress(orderId), out Dictionary orderDict)) { throw new FailedLoadStateException($"{addressesHex} failed to load {nameof(Order)}({Order.DeriveAddress(orderId)})."); } var orderOnSale = OrderFactory.Deserialize(orderDict); orderOnSale.ValidateCancelOrder(avatarState, tradableId); var itemOnSale = orderOnSale.Cancel(avatarState, context.BlockIndex); if (context.BlockIndex < orderOnSale.ExpiredBlockIndex) { var shardedShopState = new ShardedShopStateV2(shopStateDict); shardedShopState.Remove(orderOnSale, context.BlockIndex); states = states.SetState(shopAddress, shardedShopState.Serialize()); } digestList.Remove(orderOnSale.OrderId); states = states.SetState(itemAddress, itemOnSale.Serialize()) .SetState(digestListAddress, digestList.Serialize()); sw.Stop(); var expirationMail = avatarState.mailBox.OfType <OrderExpirationMail>() .FirstOrDefault(m => m.OrderId.Equals(orderId)); if (!(expirationMail is null)) { avatarState.mailBox.Remove(expirationMail); } // for updateSell var updateSellShopState = states.TryGetState(updateSellShopAddress, out Dictionary serializedState) ? new ShardedShopStateV2(serializedState) : new ShardedShopStateV2(updateSellShopAddress); Log.Verbose("{AddressesHex} UpdateSell Get ShardedShopState: {Elapsed}", addressesHex, sw.Elapsed); sw.Restart(); var newOrder = OrderFactory.Create(context.Signer, sellerAvatarAddress, updateSellOrderId, price, tradableId, context.BlockIndex, itemSubType, count); newOrder.Validate(avatarState, count); var tradableItem = newOrder.Sell(avatarState); var costumeStatSheet = states.GetSheet <CostumeStatSheet>(); var orderDigest = newOrder.Digest(avatarState, costumeStatSheet); updateSellShopState.Add(orderDigest, context.BlockIndex); digestList.Add(orderDigest); states = states.SetState(digestListAddress, digestList.Serialize()); states = states.SetState(inventoryAddress, avatarState.inventory.Serialize()) .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) .SetState(questListAddress, avatarState.questList.Serialize()) .SetState(sellerAvatarAddress, avatarState.SerializeV2()); sw.Stop(); Log.Verbose("{AddressesHex} UpdateSell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); sw.Restart(); states = states .SetState(itemAddress, tradableItem.Serialize()) .SetState(updateSellOrderAddress, newOrder.Serialize()) .SetState(updateSellShopAddress, updateSellShopState.Serialize()); sw.Stop(); var ended = DateTimeOffset.UtcNow; Log.Verbose("{AddressesHex} UpdateSell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); Log.Verbose("{AddressesHex} UpdateSell Total Executed Time: {Elapsed}", addressesHex, ended - started); return(states); }