コード例 #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);
            }
        }
コード例 #2
0
        public void TryGetTradableItems_Material_Multiple_Slot()
        {
            var row = TableSheets.MaterialItemSheet.First;

            Assert.NotNull(row);

            var inventory = new Inventory();

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

            Assert.Equal(2, inventory.Items.Count);
            inventory.TryGetTradableItems(TradableMaterial.DeriveTradableId(row.ItemId), 1, 2, out var items);
            Assert.Equal(2, items.Count);
            for (var index = 0; index < items.Count; index++)
            {
                var item = items[index];
                Assert.Equal(1, item.count);
                var tradableItem = (ITradableItem)item.item;
                Assert.Equal(index, tradableItem.RequiredBlockIndex);
            }
        }
コード例 #3
0
ファイル: ShopItemTest.cs プロジェクト: dahlia/lib9c
        private static IEnumerable <object[]> GetShopItemsWithTradableMaterial()
        {
            var objects = new object[2];
            var index   = 0;

            foreach (var row in _tableSheets.MaterialItemSheet.OrderedList
                     .Where(e => e.ItemSubType == ItemSubType.Hourglass || e.ItemSubType == ItemSubType.ApStone))
            {
                var tradableMaterial = new TradableMaterial(row);
                var shopItem         = new ShopItem(
                    new PrivateKey().ToAddress(),
                    new PrivateKey().ToAddress(),
                    Guid.NewGuid(),
                    new FungibleAssetValue(_currency, 100, 0),
                    1,
                    tradableMaterial,
                    0);
                objects[index++] = shopItem;
            }

            return(new List <object[]>
            {
                objects,
            });
        }
コード例 #4
0
        public void Serialize()
        {
            Assert.NotNull(_materialRow);

            var material     = new TradableMaterial(_materialRow);
            var serialized   = material.Serialize();
            var deserialized = new TradableMaterial((Bencodex.Types.Dictionary)serialized);

            Assert.Equal(material, deserialized);
        }
コード例 #5
0
ファイル: ShopItemTest.cs プロジェクト: dahlia/lib9c
        private static ShopItem GetShopItemWithFirstMaterial()
        {
            var row = _tableSheets.MaterialItemSheet.First;
            var tradableMaterial = new TradableMaterial(row);

            return(new ShopItem(
                       new PrivateKey().ToAddress(),
                       new PrivateKey().ToAddress(),
                       Guid.NewGuid(),
                       new FungibleAssetValue(_currency, 100, 0),
                       1,
                       tradableMaterial,
                       0));
        }
コード例 #6
0
        public void SerializeWithDotNetApi()
        {
            Assert.NotNull(_materialRow);

            var material  = new TradableMaterial(_materialRow);
            var formatter = new BinaryFormatter();

            using var ms = new MemoryStream();
            formatter.Serialize(ms, material);
            ms.Seek(0, SeekOrigin.Begin);

            var deserialized = (TradableMaterial)formatter.Deserialize(ms);

            Assert.Equal(material, deserialized);
        }
コード例 #7
0
 public override OrderReceipt Transfer2(AvatarState seller, AvatarState buyer, long blockIndex)
 {
     if (seller.inventory.TryGetTradableItem(TradableId, ExpiredBlockIndex, ItemCount,
                                             out Inventory.Item inventoryItem))
     {
         TradableMaterial tradableItem = (TradableMaterial)inventoryItem.item;
         seller.inventory.RemoveTradableItem(tradableItem, ItemCount);
         TradableMaterial copy = (TradableMaterial)tradableItem.Clone();
         copy.RequiredBlockIndex = blockIndex;
         buyer.UpdateFromAddItem2(copy, ItemCount, false);
         return(new OrderReceipt(OrderId, buyer.agentAddress, buyer.address, blockIndex));
     }
     throw new ItemDoesNotExistException(
               $"Aborted because the tradable item({TradableId}) was failed to load from seller's inventory.");
 }
コード例 #8
0
        public void SellItem_Material_Multiple_Slot(int totalCount, int sellCount, int expectedCount)
        {
            var inventory = new Inventory();
            var row       = TableSheets.MaterialItemSheet.First;

            Assert.NotNull(row);
            for (var i = 1; i < totalCount + 1; i++)
            {
                var tradableItem = ItemFactory.CreateTradableMaterial(row);
                tradableItem.RequiredBlockIndex = i;
                inventory.AddItem(tradableItem, 1);
                Assert.True(inventory.TryGetTradableItems(tradableItem.TradableId, i, i, out _));
            }

            Assert.Equal(totalCount, inventory.Items.Count);
            Assert.True(inventory.HasItem(row.Id, totalCount));
            inventory.SellItem(TradableMaterial.DeriveTradableId(row.ItemId), totalCount, sellCount);
            Assert.Equal(expectedCount, inventory.Items.Count);
        }
コード例 #9
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);
        }