Esempio n. 1
0
        public void UpdateTradableItem_Material_Multiple_Slot()
        {
            var row = TableSheets.MaterialItemSheet.First;

            Assert.NotNull(row);
            var tradableId = TradableMaterial.DeriveTradableId(row.ItemId);
            var inventory  = new Inventory();

            for (var i = 0; i < 2; i++)
            {
                ITradableItem tradableItem = ItemFactory.CreateTradableMaterial(row);
                tradableItem.RequiredBlockIndex = i;
                inventory.AddItem((ItemBase)tradableItem, 2);
            }

            Assert.Equal(2, inventory.Items.Count);
            var result = inventory.UpdateTradableItem(tradableId, 0, 1, 1);

            Assert.Equal(2, inventory.Items.Count);
            Assert.Equal(1, result.RequiredBlockIndex);
            Assert.True(inventory.TryGetTradableItems(tradableId, 1, 4, out var items));
            Assert.Equal(2, items.Count);
            for (var i = 0; i < items.Count; i++)
            {
                var item = items[i];
                Assert.Equal(2 * i + 1, item.count);
                Assert.Equal(i, ((ITradableItem)item.item).RequiredBlockIndex);
            }
        }
Esempio n. 2
0
        public static int GetCP(ITradableItem tradableItem, CostumeStatSheet sheet)
        {
            if (tradableItem is ItemUsable itemUsable)
            {
                return(GetCP(itemUsable));
            }

            if (tradableItem is Costume costume)
            {
                return(GetCP(costume, sheet));
            }

            return(0);
        }
Esempio n. 3
0
        public void HasFungibleItem()
        {
            var row = TableSheets.MaterialItemSheet.First;

            Assert.NotNull(row);
            var inventory = new Inventory();
            var material  = ItemFactory.CreateMaterial(row);

            inventory.AddItem(material, 2);
            for (var i = 0; i < 2; i++)
            {
                ITradableItem tradableItem = ItemFactory.CreateTradableMaterial(row);
                tradableItem.RequiredBlockIndex = i;
                inventory.AddItem((ItemBase)tradableItem, 2);
            }

            Assert.False(inventory.HasFungibleItem(row.ItemId, 0, 6));
            Assert.True(inventory.HasFungibleItem(row.ItemId, 0, 4));
            Assert.True(inventory.HasFungibleItem(row.ItemId, 1, 6));
        }
Esempio n. 4
0
        public void Execute(
            ItemType itemType,
            bool orderExist,
            int itemCount,
            bool backward
            )
        {
            var avatarState = _initialState.GetAvatarState(_avatarAddress);

            ITradableItem tradableItem;

            switch (itemType)
            {
            case ItemType.Consumable:
                tradableItem = ItemFactory.CreateItemUsable(
                    _tableSheets.ConsumableItemSheet.First,
                    Guid.NewGuid(),
                    0);
                break;

            case ItemType.Costume:
                tradableItem = ItemFactory.CreateCostume(
                    _tableSheets.CostumeItemSheet.First,
                    Guid.NewGuid());
                break;

            case ItemType.Equipment:
                tradableItem = ItemFactory.CreateItemUsable(
                    _tableSheets.EquipmentItemSheet.First,
                    Guid.NewGuid(),
                    0);
                break;

            case ItemType.Material:
                var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList
                                          .First(row => row.ItemSubType == ItemSubType.Hourglass);
                tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow);
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null);
            }

            Assert.Equal(0, tradableItem.RequiredBlockIndex);
            avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount);

            var previousStates = _initialState;

            if (backward)
            {
                previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize());
            }
            else
            {
                previousStates = previousStates
                                 .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());
            }

            var currencyState      = previousStates.GetGoldCurrency();
            var price              = new FungibleAssetValue(currencyState, ProductPrice, 0);
            var orderId            = new Guid("6f460c1a755d48e4ad6765d5f519dbc8");
            var existOrderId       = new Guid("229e5f8c-fabe-4c04-bab9-45325cfa69a4");
            var orderAddress       = Order.DeriveAddress(orderId);
            var shardedShopAddress = ShardedShopStateV2.DeriveAddress(
                tradableItem.ItemSubType,
                orderId);
            long blockIndex = 1;

            if (orderExist)
            {
                var shardedShopState = new ShardedShopStateV2(shardedShopAddress);
                var existOrder       = OrderFactory.Create(
                    _agentAddress,
                    _avatarAddress,
                    existOrderId,
                    price,
                    tradableItem.TradableId,
                    0,
                    tradableItem.ItemSubType,
                    1
                    );
                existOrder.Sell2(avatarState);
                blockIndex = existOrder.ExpiredBlockIndex;
                shardedShopState.Add(existOrder.Digest2(avatarState, _tableSheets.CostumeStatSheet), blockIndex);
                Assert.Single(shardedShopState.OrderDigestList);
                previousStates = previousStates.SetState(
                    shardedShopAddress,
                    shardedShopState.Serialize());
            }
            else
            {
                Assert.Null(previousStates.GetState(shardedShopAddress));
            }

            var sellAction = new Sell7
            {
                sellerAvatarAddress = _avatarAddress,
                tradableId          = tradableItem.TradableId,
                count       = itemCount,
                price       = price,
                itemSubType = tradableItem.ItemSubType,
                orderId     = orderId,
            };
            var nextState = sellAction.Execute(new ActionContext
            {
                BlockIndex     = blockIndex,
                PreviousStates = previousStates,
                Rehearsal      = false,
                Signer         = _agentAddress,
                Random         = new TestRandom(),
            });

            long expiredBlockIndex = Order.ExpirationInterval + blockIndex;

            // Check AvatarState and Inventory
            var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress);

            Assert.Single(nextAvatarState.inventory.Items);
            Assert.True(nextAvatarState.inventory.TryGetTradableItems(
                            tradableItem.TradableId,
                            expiredBlockIndex,
                            1,
                            out var inventoryItems));
            Assert.Single(inventoryItems);
            ITradableItem nextTradableItem = (ITradableItem)inventoryItems.First().item;

            Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex);

            // Check ShardedShopState
            var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress);

            Assert.NotNull(nextSerializedShardedShopState);
            var nextShardedShopState =
                new ShardedShopStateV2((Dictionary)nextSerializedShardedShopState);
            var expectedCount = orderExist ? 2 : 1;

            Assert.Equal(expectedCount, nextShardedShopState.OrderDigestList.Count);
            var orderDigest = nextShardedShopState.OrderDigestList.First(o => o.OrderId.Equals(orderId));

            Assert.Equal(price, orderDigest.Price);
            Assert.Equal(blockIndex, orderDigest.StartedBlockIndex);
            Assert.Equal(expiredBlockIndex, orderDigest.ExpiredBlockIndex);
            Assert.Equal(((ItemBase)tradableItem).Id, orderDigest.ItemId);
            Assert.Equal(tradableItem.TradableId, orderDigest.TradableId);

            var serializedOrder = nextState.GetState(orderAddress);

            Assert.NotNull(serializedOrder);
            var serializedItem = nextState.GetState(Addresses.GetItemAddress(tradableItem.TradableId));

            Assert.NotNull(serializedItem);

            var           order     = OrderFactory.Deserialize((Dictionary)serializedOrder);
            ITradableItem orderItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)serializedItem);

            Assert.Equal(price, order.Price);
            Assert.Equal(orderId, order.OrderId);
            Assert.Equal(tradableItem.TradableId, order.TradableId);
            Assert.Equal(blockIndex, order.StartedBlockIndex);
            Assert.Equal(expiredBlockIndex, order.ExpiredBlockIndex);
            Assert.Equal(_agentAddress, order.SellerAgentAddress);
            Assert.Equal(_avatarAddress, order.SellerAvatarAddress);
            Assert.Equal(expiredBlockIndex, orderItem.RequiredBlockIndex);

            var mailList = nextAvatarState.mailBox.OfType <OrderExpirationMail>().ToList();

            Assert.Single(mailList);
            var mail = mailList.First();

            Assert.NotNull(mail);
            Assert.Equal(expiredBlockIndex, mail.requiredBlockIndex);
            Assert.Equal(orderId, mail.OrderId);

            var receiptDict = nextState.GetState(OrderDigestListState.DeriveAddress(_avatarAddress));

            Assert.NotNull(receiptDict);
            var orderDigestList = new OrderDigestListState((Dictionary)receiptDict);

            Assert.Single(orderDigestList.OrderDigestList);
            OrderDigest orderDigest2 = orderDigestList.OrderDigestList.First();

            Assert.Equal(orderDigest, orderDigest2);
        }
Esempio n. 5
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            var states                  = context.PreviousStates;
            var shardedShopAddress      = ShardedShopStateV2.DeriveAddress(itemSubType, orderId);
            var inventoryAddress        = sellerAvatarAddress.Derive(LegacyInventoryKey);
            var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey);
            var questListAddress        = sellerAvatarAddress.Derive(LegacyQuestListKey);
            var orderDigestListAddress  = OrderDigestListState.DeriveAddress(sellerAvatarAddress);
            var itemAddress             = Addresses.GetItemAddress(tradableId);

            if (context.Rehearsal)
            {
                states = states.SetState(shardedShopAddress, MarkChanged);
                return(states
                       .SetState(inventoryAddress, MarkChanged)
                       .SetState(worldInformationAddress, MarkChanged)
                       .SetState(questListAddress, MarkChanged)
                       .SetState(orderDigestListAddress, MarkChanged)
                       .SetState(itemAddress, MarkChanged)
                       .SetState(sellerAvatarAddress, MarkChanged));
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);

            var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress);
            var sw           = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Sell Cancel exec started", addressesHex);

            if (!states.TryGetAvatarStateV2(context.Signer, sellerAvatarAddress, out var avatarState, out _))
            {
                throw new FailedLoadStateException(
                          $"{addressesHex}Aborted as the avatar state of the seller failed to load.");
            }

            sw.Stop();
            Log.Verbose("{AddressesHex}Sell Cancel 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);
            }

            if (!states.TryGetState(shardedShopAddress, out BxDictionary shopStateDict))
            {
                throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(ShardedShopStateV2)}({shardedShopAddress}).");
            }

            sw.Stop();
            Log.Verbose("{AddressesHex}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)}).");
            }

            Order order = OrderFactory.Deserialize(orderDict);

            order.ValidateCancelOrder2(avatarState, tradableId);
            ITradableItem sellItem         = order.Cancel2(avatarState, context.BlockIndex);
            var           shardedShopState = new ShardedShopStateV2(shopStateDict);

            shardedShopState.Remove(order, context.BlockIndex);
            states = states.SetState(shardedShopAddress, shardedShopState.Serialize());
            if (!states.TryGetState(orderDigestListAddress, out Dictionary rawList))
            {
                throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(OrderDigest)}({orderDigestListAddress}).");
            }
            var digestList = new OrderDigestListState(rawList);

            digestList.Remove(order.OrderId);

            var expirationMail = avatarState.mailBox.OfType <OrderExpirationMail>()
                                 .FirstOrDefault(m => m.OrderId.Equals(orderId));

            if (!(expirationMail is null))
            {
                avatarState.mailBox.Remove(expirationMail);
            }

            var mail = new CancelOrderMail(
                context.BlockIndex,
                orderId,
                context.BlockIndex,
                orderId
                );

            avatarState.Update(mail);

            avatarState.updatedAt  = context.BlockIndex;
            avatarState.blockIndex = context.BlockIndex;

            sw.Stop();
            Log.Verbose("{AddressesHex}Sell Cancel Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            states = states
                     .SetState(itemAddress, sellItem.Serialize())
                     .SetState(orderDigestListAddress, digestList.Serialize())
                     .SetState(inventoryAddress, avatarState.inventory.Serialize())
                     .SetState(worldInformationAddress, avatarState.worldInformation.Serialize())
                     .SetState(questListAddress, avatarState.questList.Serialize())
                     .SetState(sellerAvatarAddress, avatarState.SerializeV2());
            sw.Stop();
            Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            sw.Stop();
            var ended = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Sell Cancel Set ShopState: {Elapsed}", addressesHex, sw.Elapsed);
            Log.Verbose("{AddressesHex}Sell Cancel Total Executed Time: {Elapsed}", addressesHex, ended - started);
            return(states);
        }
Esempio n. 6
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            var states = context.PreviousStates;

            if (context.Rehearsal)
            {
                states = states.SetState(sellerAvatarAddress, MarkChanged);
                states = ShardedShopState.AddressKeys.Aggregate(
                    states,
                    (current, addressKey) => current.SetState(
                        ShardedShopState.DeriveAddress(itemSubType, addressKey),
                        MarkChanged));
                return(states.SetState(context.Signer, MarkChanged));
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);

            var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress);

            var sw = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Sell exec started", addressesHex);

            if (price.Sign < 0)
            {
                throw new InvalidPriceException(
                          $"{addressesHex}Aborted as the price is less than zero: {price}.");
            }

            if (!states.TryGetAgentAvatarStates(
                    context.Signer,
                    sellerAvatarAddress,
                    out _,
                    out var avatarState))
            {
                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();
            Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            switch (itemSubType)
            {
            case ItemSubType.EquipmentMaterial:
            case ItemSubType.FoodMaterial:
            case ItemSubType.MonsterPart:
            case ItemSubType.NormalMaterial:
                throw new InvalidShopItemException(
                          $"{addressesHex}Aborted because {nameof(itemSubType)}({itemSubType}) does not support.");
            }

            if (count < 1)
            {
                throw new InvalidShopItemException(
                          $"{addressesHex}Aborted because {nameof(count)}({count}) should be greater than or equal to 1.");
            }

            if (!avatarState.inventory.TryGetTradableItems(tradableId, context.BlockIndex, count, out List <Inventory.Item> inventoryItems))
            {
                throw new ItemDoesNotExistException(
                          $"{addressesHex}Aborted because the tradable item({tradableId}) was failed to load from avatar's inventory.");
            }

            IEnumerable <ITradableItem> tradableItems = inventoryItems.Select(i => (ITradableItem)i.item).ToList();
            var expiredBlockIndex = context.BlockIndex + ExpiredBlockIndex;

            foreach (var ti in tradableItems)
            {
                if (!ti.ItemSubType.Equals(itemSubType))
                {
                    throw new InvalidItemTypeException(
                              $"{addressesHex}Expected ItemSubType: {ti.ItemSubType}. Actual ItemSubType: {itemSubType}");
                }

                if (ti is INonFungibleItem)
                {
                    if (count != 1)
                    {
                        throw new ArgumentOutOfRangeException(
                                  $"{addressesHex}Aborted because {nameof(count)}({count}) should be 1 because {nameof(tradableId)}({tradableId}) is non-fungible item.");
                    }
                }
            }

            ITradableItem tradableItem = avatarState.inventory.SellItem(tradableId, context.BlockIndex, count);

            var productId          = context.Random.GenerateRandomGuid();
            var shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId);

            if (!states.TryGetState(shardedShopAddress, out BxDictionary serializedSharedShopState))
            {
                var shardedShopState = new ShardedShopState(shardedShopAddress);
                serializedSharedShopState = (BxDictionary)shardedShopState.Serialize();
            }

            sw.Stop();
            Log.Verbose(
                "{AddressesHex}Sell Get ShardedShopState: {Elapsed}",
                addressesHex,
                sw.Elapsed);
            sw.Restart();

            var    serializedProductList = (BxList)serializedSharedShopState[ProductsKey];
            string productKey;
            string itemIdKey;
            string requiredBlockIndexKey;

            switch (tradableItem.ItemType)
            {
            case ItemType.Consumable:
            case ItemType.Equipment:
                productKey            = LegacyItemUsableKey;
                itemIdKey             = LegacyItemIdKey;
                requiredBlockIndexKey = LegacyRequiredBlockIndexKey;
                break;

            case ItemType.Costume:
                productKey            = LegacyCostumeKey;
                itemIdKey             = LegacyCostumeItemIdKey;
                requiredBlockIndexKey = RequiredBlockIndexKey;
                break;

            case ItemType.Material:
                productKey            = TradableFungibleItemKey;
                itemIdKey             = LegacyCostumeItemIdKey;
                requiredBlockIndexKey = RequiredBlockIndexKey;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            BxDictionary serializedProductDictionary;

            if (tradableItem.ItemType == ItemType.Material)
            {
                // Find expired TradableMaterial
                serializedProductDictionary = serializedProductList
                                              .Select(p => (BxDictionary)p)
                                              .FirstOrDefault(p =>
                {
                    var materialItemId =
                        ((BxDictionary)p[productKey])[itemIdKey].ToItemId();
                    var requiredBlockIndex = p[ExpiredBlockIndexKey].ToLong();
                    return(TradableMaterial.DeriveTradableId(materialItemId)
                           .Equals(tradableItem.TradableId) && requiredBlockIndex <= context.BlockIndex);
                });
            }
            else
            {
                var serializedTradeId = tradableItem.TradableId.Serialize();
                serializedProductDictionary = serializedProductList
                                              .Select(p => (BxDictionary)p)
                                              .FirstOrDefault(p =>
                                                              ((BxDictionary)p[productKey])[itemIdKey].Equals(serializedTradeId));
            }

            // Since Bencodex 0.4, Dictionary/List are reference types; so their default values
            // are not a empty container, but a null reference:
            serializedProductDictionary = serializedProductDictionary ?? Dictionary.Empty;

            ShopItem shopItem;

            // Register new ShopItem
            if (serializedProductDictionary.Equals(BxDictionary.Empty))
            {
                shopItem = new ShopItem(
                    context.Signer,
                    sellerAvatarAddress,
                    productId,
                    price,
                    expiredBlockIndex,
                    tradableItem,
                    count);
                var serializedShopItem = shopItem.Serialize();
                serializedProductList = serializedProductList.Add(serializedShopItem);
            }
            // Update Registered ShopItem
            else
            {
                // Delete current ShopItem
                serializedProductList =
                    (BxList)serializedProductList.Remove(serializedProductDictionary);

                // Update ITradableItem.RequiredBlockIndex
                var inChainShopItem = (BxDictionary)serializedProductDictionary[productKey];
                inChainShopItem = inChainShopItem
                                  .SetItem(requiredBlockIndexKey, expiredBlockIndex.Serialize());

                // Update ShopItem.ExpiredBlockIndex
                serializedProductDictionary = serializedProductDictionary
                                              .SetItem(ExpiredBlockIndexKey, expiredBlockIndex.Serialize())
                                              .SetItem(productKey, inChainShopItem);

                // Update only Material for backwardCompatible.
                if (tradableItem.ItemType == ItemType.Material)
                {
                    serializedProductDictionary = serializedProductDictionary
                                                  .SetItem(TradableFungibleItemCountKey, count.Serialize());
                }

                serializedProductList = serializedProductList.Add(serializedProductDictionary);
                shopItem = new ShopItem(serializedProductDictionary);
            }

            serializedSharedShopState = serializedSharedShopState.SetItem(
                ProductsKey,
                new List <IValue>(serializedProductList));

            sw.Stop();
            Log.Verbose("{AddressesHex}Sell Get Register Item: {Elapsed}", addressesHex,
                        sw.Elapsed);
            sw.Restart();

            avatarState.updatedAt  = context.BlockIndex;
            avatarState.blockIndex = context.BlockIndex;

            var result = new SellCancellation.Result
            {
                shopItem                  = shopItem,
                itemUsable                = shopItem.ItemUsable,
                costume                   = shopItem.Costume,
                tradableFungibleItem      = shopItem.TradableFungibleItem,
                tradableFungibleItemCount = shopItem.TradableFungibleItemCount,
            };
            var mail = new SellCancelMail(
                result,
                context.BlockIndex,
                context.Random.GenerateRandomGuid(),
                expiredBlockIndex);

            result.id = mail.id;
            avatarState.Update(mail);

            states = states.SetState(sellerAvatarAddress, avatarState.Serialize());
            sw.Stop();
            Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            states = states.SetState(shardedShopAddress, serializedSharedShopState);
            sw.Stop();
            var ended = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed);
            Log.Verbose(
                "{AddressesHex}Sell Total Executed Time: {Elapsed}",
                addressesHex,
                ended - started);

            return(states);
        }
Esempio n. 7
0
        public void Execute(
            ItemType itemType,
            bool shopItemExist,
            long blockIndex,
            int itemCount,
            int prevCount,
            int expectedProductsCount
            )
        {
            var avatarState = _initialState.GetAvatarState(_avatarAddress);

            ITradableItem tradableItem;

            switch (itemType)
            {
            case ItemType.Consumable:
                tradableItem = ItemFactory.CreateItemUsable(
                    _tableSheets.ConsumableItemSheet.First,
                    Guid.NewGuid(),
                    0);
                break;

            case ItemType.Costume:
                tradableItem = ItemFactory.CreateCostume(
                    _tableSheets.CostumeItemSheet.First,
                    Guid.NewGuid());
                break;

            case ItemType.Equipment:
                tradableItem = ItemFactory.CreateItemUsable(
                    _tableSheets.EquipmentItemSheet.First,
                    Guid.NewGuid(),
                    0);
                break;

            case ItemType.Material:
                var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList
                                          .First(row => row.ItemSubType == ItemSubType.Hourglass);
                tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow);
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null);
            }

            Assert.Equal(0, tradableItem.RequiredBlockIndex);
            avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount);

            var previousStates = _initialState;

            previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize());
            var currencyState      = previousStates.GetGoldCurrency();
            var price              = new FungibleAssetValue(currencyState, ProductPrice, 0);
            var expectedProductId  = new Guid("6f460c1a755d48e4ad6765d5f519dbc8");
            var productId          = new Guid("229e5f8c-fabe-4c04-bab9-45325cfa69a4");
            var shardedShopAddress = ShardedShopState.DeriveAddress(
                tradableItem.ItemSubType,
                expectedProductId);

            if (shopItemExist)
            {
                tradableItem.RequiredBlockIndex = blockIndex;
                Assert.Equal(blockIndex, tradableItem.RequiredBlockIndex);
                var shopItem = new ShopItem(
                    _agentAddress,
                    _avatarAddress,
                    productId,
                    new FungibleAssetValue(currencyState, 1, 0),
                    blockIndex,
                    tradableItem,
                    prevCount
                    );

                var shardedShopState = new ShardedShopState(shardedShopAddress);
                shardedShopState.Register(shopItem);
                Assert.Single(shardedShopState.Products);
                previousStates = previousStates.SetState(
                    shardedShopAddress,
                    shardedShopState.Serialize());
            }
            else
            {
                Assert.Null(previousStates.GetState(shardedShopAddress));
            }

            var sellAction = new Sell6
            {
                sellerAvatarAddress = _avatarAddress,
                tradableId          = tradableItem.TradableId,
                count       = itemCount,
                price       = price,
                itemSubType = tradableItem.ItemSubType,
            };
            var nextState = sellAction.Execute(new ActionContext
            {
                BlockIndex     = 1,
                PreviousStates = previousStates,
                Rehearsal      = false,
                Signer         = _agentAddress,
                Random         = new TestRandom(),
            });

            const long expiredBlockIndex = Sell6.ExpiredBlockIndex + 1;

            // Check AvatarState and Inventory
            var nextAvatarState = nextState.GetAvatarState(_avatarAddress);

            Assert.Single(nextAvatarState.inventory.Items);
            Assert.True(nextAvatarState.inventory.TryGetTradableItems(
                            tradableItem.TradableId,
                            expiredBlockIndex,
                            1,
                            out var inventoryItems));
            Assert.Single(inventoryItems);
            ITradableItem nextTradableItem = (ITradableItem)inventoryItems.First().item;

            Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex);

            // Check ShardedShopState and ShopItem
            var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress);

            Assert.NotNull(nextSerializedShardedShopState);
            var nextShardedShopState =
                new ShardedShopState((Dictionary)nextSerializedShardedShopState);

            Assert.Equal(expectedProductsCount, nextShardedShopState.Products.Count);

            var           nextShopItem = nextShardedShopState.Products.Values.First(s => s.ExpiredBlockIndex == expiredBlockIndex);
            ITradableItem nextTradableItemInShopItem;

            switch (itemType)
            {
            case ItemType.Consumable:
            case ItemType.Equipment:
                nextTradableItemInShopItem = nextShopItem.ItemUsable;
                break;

            case ItemType.Costume:
                nextTradableItemInShopItem = nextShopItem.Costume;
                break;

            case ItemType.Material:
                nextTradableItemInShopItem = nextShopItem.TradableFungibleItem;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null);
            }

            Assert.Equal(price, nextShopItem.Price);
            Assert.Equal(expectedProductId, nextShopItem.ProductId);
            Assert.Equal(expiredBlockIndex, nextShopItem.ExpiredBlockIndex);
            Assert.Equal(_agentAddress, nextShopItem.SellerAgentAddress);
            Assert.Equal(_avatarAddress, nextShopItem.SellerAvatarAddress);
            Assert.Equal(expiredBlockIndex, nextTradableItemInShopItem.RequiredBlockIndex);

            var mailList = nextAvatarState.mailBox.Where(m => m is SellCancelMail).ToList();

            Assert.Single(mailList);
            var mail = mailList.First() as SellCancelMail;

            Assert.NotNull(mail);
            Assert.Equal(expiredBlockIndex, mail.requiredBlockIndex);

            ITradableItem attachmentItem;
            int           attachmentCount = 0;

            switch (itemType)
            {
            case ItemType.Consumable:
            case ItemType.Equipment:
                Assert.NotNull(mail.attachment.itemUsable);
                attachmentItem = mail.attachment.itemUsable;
                Assert.Equal(tradableItem, mail.attachment.itemUsable);
                break;

            case ItemType.Costume:
                Assert.NotNull(mail.attachment.costume);
                attachmentItem = mail.attachment.costume;
                Assert.Equal(tradableItem, mail.attachment.costume);
                break;

            case ItemType.Material:
                Assert.NotNull(mail.attachment.tradableFungibleItem);
                attachmentItem  = mail.attachment.tradableFungibleItem;
                attachmentCount = mail.attachment.tradableFungibleItemCount;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null);
            }

            Assert.Equal(attachmentCount, nextShopItem.TradableFungibleItemCount);
            Assert.Equal(nextTradableItem, attachmentItem);
            Assert.Equal(nextTradableItemInShopItem, attachmentItem);
        }
Esempio n. 8
0
        public void Execute_20210604(
            long blockIndex,
            ItemSubType itemSubType,
            string agentAddressHex,
            string avatarAddressHex,
            long shopExpiredBlockIndex,
            long expectedBlockIndex,
            int shopItemCount,
            int itemCount,
            int shopPrice,
            int expectedPrice)
        {
            var avatarState         = _initialState.GetAvatarState(_avatarAddress);
            var sellerAgentAddress  = new Address(agentAddressHex);
            var sellerAvatarAddress = new Address(avatarAddressHex);

            ITradableItem tradableItem;

            switch (itemSubType)
            {
            case ItemSubType.Weapon:
                tradableItem = ItemFactory.CreateItemUsable(
                    _tableSheets.EquipmentItemSheet.OrderedList.First(row => row.ItemSubType == ItemSubType.Weapon),
                    Guid.NewGuid(),
                    1);
                break;

            case ItemSubType.Hourglass:
                var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList
                                          .First(row => row.ItemSubType == ItemSubType.Hourglass);
                tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow);
                tradableItem.RequiredBlockIndex = 1;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(itemSubType), itemSubType, null);
            }

            Assert.Equal(1, tradableItem.RequiredBlockIndex);
            avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount);

            var previousStates = _initialState;

            previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize());
            var currencyState      = previousStates.GetGoldCurrency();
            var price              = new FungibleAssetValue(currencyState, expectedPrice, 0);
            var productId          = new Guid("6f460c1a755d48e4ad6765d5f519dbc8");
            var shardedShopAddress = ShardedShopState.DeriveAddress(
                tradableItem.ItemSubType,
                productId);

            Assert.Equal(1, tradableItem.RequiredBlockIndex);
            var shopItem = new ShopItem(
                sellerAgentAddress,
                sellerAvatarAddress,
                Guid.NewGuid(),
                new FungibleAssetValue(currencyState, shopPrice, 0),
                shopExpiredBlockIndex,
                tradableItem,
                shopItemCount
                );

            var shardedShopState = new ShardedShopState(shardedShopAddress);

            shardedShopState.Register(shopItem);
            Assert.Single(shardedShopState.Products);
            previousStates = previousStates.SetState(
                shardedShopAddress,
                shardedShopState.Serialize());

            var sellAction = new Sell6
            {
                sellerAvatarAddress = _avatarAddress,
                tradableId          = tradableItem.TradableId,
                count       = itemCount,
                price       = price,
                itemSubType = tradableItem.ItemSubType,
            };
            var nextState = sellAction.Execute(new ActionContext
            {
                BlockIndex     = blockIndex,
                PreviousStates = previousStates,
                Rehearsal      = false,
                Signer         = _agentAddress,
                Random         = new TestRandom(),
            });

            // Check AvatarState and Inventory
            var nextAvatarState = nextState.GetAvatarState(_avatarAddress);

            Assert.Single(nextAvatarState.inventory.Items);
            Assert.True(nextAvatarState.inventory.TryGetTradableItems(
                            tradableItem.TradableId,
                            expectedBlockIndex,
                            itemCount,
                            out var inventoryItems));
            Assert.Single(inventoryItems);
            ITradableItem nextTradableItem = (ITradableItem)inventoryItems.First().item;

            Assert.Equal(expectedBlockIndex, nextTradableItem.RequiredBlockIndex);

            // Check ShardedShopState and ShopItem
            var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress);

            Assert.NotNull(nextSerializedShardedShopState);
            var nextShardedShopState =
                new ShardedShopState((Dictionary)nextSerializedShardedShopState);

            Assert.Equal(2, nextShardedShopState.Products.Count);
            Assert.Single(nextShardedShopState.Products.Values.Where(s => s.Equals(shopItem)));
            Assert.Single(nextShardedShopState.Products.Values.Where(s => !s.Equals(shopItem)));
            ShopItem nextShopItem = nextShardedShopState.Products.Values.First(s => !s.Equals(shopItem));

            ITradableItem innerShopItem = nextShopItem.TradableFungibleItem;
            int           fungibleCount = itemCount;

            if (itemSubType == ItemSubType.Weapon)
            {
                innerShopItem = nextShopItem.ItemUsable;
                fungibleCount = 0;
            }

            Assert.Equal(price, nextShopItem.Price);
            Assert.Equal(expectedBlockIndex, nextShopItem.ExpiredBlockIndex);
            Assert.Equal(_agentAddress, nextShopItem.SellerAgentAddress);
            Assert.Equal(_avatarAddress, nextShopItem.SellerAvatarAddress);
            Assert.Equal(expectedBlockIndex, innerShopItem.RequiredBlockIndex);
            Assert.Equal(fungibleCount, nextShopItem.TradableFungibleItemCount);

            var mailList = nextAvatarState.mailBox.Where(m => m is SellCancelMail).ToList();

            Assert.Single(mailList);
            var mail = mailList.First() as SellCancelMail;

            Assert.NotNull(mail);
            Assert.Equal(expectedBlockIndex, mail.requiredBlockIndex);

            ITradableItem attachmentItem = itemSubType == ItemSubType.Weapon
                ? (ITradableItem)mail.attachment.itemUsable
                : mail.attachment.tradableFungibleItem;

            Assert.Equal(itemSubType == ItemSubType.Weapon, mail.attachment.tradableFungibleItem is null);
            Assert.Equal(fungibleCount, mail.attachment.tradableFungibleItemCount);
            Assert.Equal(nextTradableItem, attachmentItem);
        }
Esempio n. 9
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);
            Address shopAddress             = ShardedShopStateV2.DeriveAddress(itemSubType, orderId);
            Address itemAddress             = Addresses.GetItemAddress(tradableId);
            Address orderAddress            = Order.DeriveAddress(orderId);
            Address orderReceiptAddress     = OrderDigestListState.DeriveAddress(sellerAvatarAddress);

            if (context.Rehearsal)
            {
                return(states
                       .SetState(context.Signer, MarkChanged)
                       .SetState(shopAddress, MarkChanged)
                       .SetState(itemAddress, MarkChanged)
                       .SetState(orderAddress, MarkChanged)
                       .SetState(orderReceiptAddress, MarkChanged)
                       .SetState(inventoryAddress, MarkChanged)
                       .SetState(worldInformationAddress, MarkChanged)
                       .SetState(questListAddress, MarkChanged)
                       .SetState(sellerAvatarAddress, MarkChanged));
            }

            var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress);

            var sw = new Stopwatch();

            sw.Start();
            var started = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Sell exec started", addressesHex);

            if (price.Sign < 0)
            {
                throw new InvalidPriceException(
                          $"{addressesHex}Aborted as the price is less than zero: {price}.");
            }

            if (!states.TryGetAgentAvatarStatesV2(
                    context.Signer,
                    sellerAvatarAddress,
                    out _,
                    out var avatarState))
            {
                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();
            Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            Order order = OrderFactory.Create(context.Signer, sellerAvatarAddress, orderId, price, tradableId,
                                              context.BlockIndex, itemSubType, count);

            order.Validate(avatarState, count);

            ITradableItem tradableItem = order.Sell(avatarState);

            var shardedShopState = states.TryGetState(shopAddress, out Dictionary serializedState)
                ? new ShardedShopStateV2(serializedState)
                : new ShardedShopStateV2(shopAddress);

            sw.Stop();
            Log.Verbose(
                "{AddressesHex}Sell Get ShardedShopState: {Elapsed}",
                addressesHex,
                sw.Elapsed);
            sw.Restart();

            var         costumeStatSheet = states.GetSheet <CostumeStatSheet>();
            OrderDigest orderDigest      = order.Digest(avatarState, costumeStatSheet);

            shardedShopState.Add(orderDigest, context.BlockIndex);

            avatarState.updatedAt  = context.BlockIndex;
            avatarState.blockIndex = context.BlockIndex;

            var orderReceiptList = states.TryGetState(orderReceiptAddress, out Dictionary receiptDict)
                ? new OrderDigestListState(receiptDict)
                : new OrderDigestListState(orderReceiptAddress);

            orderReceiptList.Add(orderDigest);

            states = states.SetState(orderReceiptAddress, orderReceiptList.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}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            states = states
                     .SetState(itemAddress, tradableItem.Serialize())
                     .SetState(orderAddress, order.Serialize())
                     .SetState(shopAddress, shardedShopState.Serialize());
            sw.Stop();
            var ended = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed);
            Log.Verbose(
                "{AddressesHex}Sell Total Executed Time: {Elapsed}",
                addressesHex,
                ended - started);

            return(states);
        }