Exemple #1
0
        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);
        }
Exemple #2
0
        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);
        }