Beispiel #1
0
        private static void AddItemsForTest(
            AvatarState avatarState,
            IRandom random,
            CostumeItemSheet costumeItemSheet,
            MaterialItemSheet materialItemSheet,
            EquipmentItemSheet equipmentItemSheet
            )
        {
            foreach (var row in costumeItemSheet.OrderedList)
            {
                avatarState.inventory.AddItem2(ItemFactory.CreateCostume(row, random.GenerateRandomGuid()));
            }

            foreach (var row in materialItemSheet.OrderedList)
            {
                avatarState.inventory.AddItem2(ItemFactory.CreateMaterial(row), 10);

                if (row.ItemSubType == ItemSubType.Hourglass ||
                    row.ItemSubType == ItemSubType.ApStone)
                {
                    avatarState.inventory.AddItem2(ItemFactory.CreateTradableMaterial(row), 100);
                }
            }

            foreach (var row in equipmentItemSheet.OrderedList.Where(row =>
                                                                     row.Id > GameConfig.DefaultAvatarWeaponId))
            {
                var itemId = random.GenerateRandomGuid();
                avatarState.inventory.AddItem2(ItemFactory.CreateItemUsable(row, itemId, default));
            }
        }
Beispiel #2
0
 public StageSimulatorSheets(
     MaterialItemSheet materialItemSheet,
     SkillSheet skillSheet,
     SkillBuffSheet skillBuffSheet,
     BuffSheet buffSheet,
     CharacterSheet characterSheet,
     CharacterLevelSheet characterLevelSheet,
     EquipmentItemSetEffectSheet equipmentItemSetEffectSheet,
     StageSheet stageSheet,
     StageWaveSheet stageWaveSheet,
     EnemySkillSheet enemySkillSheet
     ) : base(
         materialItemSheet,
         skillSheet,
         skillBuffSheet,
         buffSheet,
         characterSheet,
         characterLevelSheet,
         equipmentItemSetEffectSheet
         )
 {
     StageSheet      = stageSheet;
     StageWaveSheet  = stageWaveSheet;
     EnemySkillSheet = enemySkillSheet;
 }
Beispiel #3
0
        public static AvatarState CreateAvatarState(string name,
                                                    Address avatarAddress,
                                                    IActionContext ctx,
                                                    MaterialItemSheet materialItemSheet,
                                                    Address rankingMapAddress)
        {
            var state           = ctx.PreviousStates;
            var gameConfigState = state.GetGameConfigState();
            var avatarState     = new AvatarState(
                avatarAddress,
                ctx.Signer,
                ctx.BlockIndex,
                state.GetAvatarSheets(),
                gameConfigState,
                rankingMapAddress,
                name
                );

            if (GameConfig.IsEditor)
            {
                var costumeItemSheet   = ctx.PreviousStates.GetSheet <CostumeItemSheet>();
                var equipmentItemSheet = ctx.PreviousStates.GetSheet <EquipmentItemSheet>();
                AddItemsForTest(avatarState, ctx.Random, costumeItemSheet, materialItemSheet, equipmentItemSheet);
            }

            return(avatarState);
        }
Beispiel #4
0
        public static AvatarState CreateAvatarState(string name,
                                                    Address avatarAddress,
                                                    IActionContext ctx,
                                                    MaterialItemSheet materialItemSheet,
                                                    Address rankingMapAddress)
        {
            var state           = ctx.PreviousStates;
            var gameConfigState = state.GetGameConfigState();
            var avatarState     = new AvatarState(
                avatarAddress,
                ctx.Signer,
                ctx.BlockIndex,
                state.GetAvatarSheets(),
                gameConfigState,
                rankingMapAddress,
                name
                );

#if LIB9C_DEV_EXTENSIONS || UNITY_EDITOR
            var data               = TestbedHelper.LoadData <TestbedCreateAvatar>("TestbedCreateAvatar");
            var costumeItemSheet   = ctx.PreviousStates.GetSheet <CostumeItemSheet>();
            var equipmentItemSheet = ctx.PreviousStates.GetSheet <EquipmentItemSheet>();
            AddItemsForTest(
                avatarState: avatarState,
                random: ctx.Random,
                costumeItemSheet: costumeItemSheet,
                materialItemSheet: materialItemSheet,
                equipmentItemSheet: equipmentItemSheet,
                data.MaterialCount,
                data.TradableMaterialCount);

            var skillSheet  = ctx.PreviousStates.GetSheet <SkillSheet>();
            var optionSheet = ctx.PreviousStates.GetSheet <EquipmentItemOptionSheet>();

            var items = data.CustomEquipmentItems;
            foreach (var item in items)
            {
                AddCustomEquipment(
                    avatarState: avatarState,
                    random: ctx.Random,
                    skillSheet: skillSheet,
                    equipmentItemSheet: equipmentItemSheet,
                    equipmentItemOptionSheet: optionSheet,
                    // Set level of equipment here.
                    level: item.Level,
                    // Set recipeId of target equipment here.
                    recipeId: item.ID,
                    // Add optionIds here.
                    item.OptionIds);
            }
#endif

            return(avatarState);
        }
Beispiel #5
0
        /// <summary>
        /// 완료된 퀘스트의 보상 처리를 한다.
        /// </summary>
        public void UpdateQuestRewards(MaterialItemSheet materialItemSheet)
        {
            var completedQuests = questList.Where(quest => quest.Complete && !quest.IsPaidInAction);
            // 완료되었지만 보상을 받지 않은 퀘스트를 return 문에서 Select 하지 않고 미리 저장하는 이유는
            // 지연된 실행에 의해, return 시점에서 이미 모든 퀘스트의 보상 처리가 완료된 상태에서
            // completed를 호출 시 where문의 predicate가 평가되어 컬렉션이 텅 비기 때문이다.
            var completedQuestIds = completedQuests.Select(quest => quest.Id).ToList();

            foreach (var quest in completedQuests)
            {
                UpdateFromQuestReward(quest, materialItemSheet);
            }

            questList.completedQuestIds = completedQuestIds;
        }
Beispiel #6
0
        public static AvatarState CreateAvatarState(string name,
                                                    Address avatarAddress,
                                                    IActionContext ctx,
                                                    MaterialItemSheet materialItemSheet,
                                                    Address rankingMapAddress)
        {
            var state           = ctx.PreviousStates;
            var gameConfigState = state.GetGameConfigState();
            var avatarState     = new AvatarState(
                avatarAddress,
                ctx.Signer,
                ctx.BlockIndex,
                state.GetAvatarSheets(),
                gameConfigState,
                rankingMapAddress,
                name
                );

            if (GameConfig.IsEditor)
            {
                var costumeItemSheet   = ctx.PreviousStates.GetSheet <CostumeItemSheet>();
                var equipmentItemSheet = ctx.PreviousStates.GetSheet <EquipmentItemSheet>();
                AddItemsForTest(
                    avatarState: avatarState,
                    random: ctx.Random,
                    costumeItemSheet: costumeItemSheet,
                    materialItemSheet: materialItemSheet,
                    equipmentItemSheet: equipmentItemSheet);

                var skillSheet  = ctx.PreviousStates.GetSheet <SkillSheet>();
                var optionSheet = ctx.PreviousStates.GetSheet <EquipmentItemOptionSheet>();

                AddCustomEquipment(
                    avatarState: avatarState,
                    random: ctx.Random,
                    skillSheet: skillSheet,
                    equipmentItemSheet: equipmentItemSheet,
                    equipmentItemOptionSheet: optionSheet,
                    // Set level of equipment here.
                    level: 2,
                    // Set recipeId of target equipment here.
                    recipeId: 10110000,
                    // Add optionIds here.
                    7, 9, 11);
            }

            return(avatarState);
        }
Beispiel #7
0
        public void UpdateFromQuestReward(Quest.Quest quest, MaterialItemSheet materialItemSheet)
        {
            var items = new List <Material>();

            foreach (var pair in quest.Reward.ItemMap.OrderBy(kv => kv.Key))
            {
                var row  = materialItemSheet.OrderedList.First(itemRow => itemRow.Id == pair.Key);
                var item = ItemFactory.CreateMaterial(row);
                var map  = inventory.AddItem(item, pair.Value);
                itemMap.Add(map);
                items.Add(item);
            }

            quest.IsPaidInAction = true;
            questList.UpdateCollectQuest(itemMap);
            questList.UpdateItemTypeCollectQuest(items);
            UpdateCompletedQuest();
        }
Beispiel #8
0
 protected SimulatorSheets(
     MaterialItemSheet materialItemSheet,
     SkillSheet skillSheet,
     SkillBuffSheet skillBuffSheet,
     BuffSheet buffSheet,
     CharacterSheet characterSheet,
     CharacterLevelSheet characterLevelSheet,
     EquipmentItemSetEffectSheet equipmentItemSetEffectSheet
     )
 {
     MaterialItemSheet           = materialItemSheet;
     SkillSheet                  = skillSheet;
     SkillBuffSheet              = skillBuffSheet;
     BuffSheet                   = buffSheet;
     CharacterSheet              = characterSheet;
     CharacterLevelSheet         = characterLevelSheet;
     EquipmentItemSetEffectSheet = equipmentItemSetEffectSheet;
 }
Beispiel #9
0
 protected Simulator(
     IRandom random,
     AvatarState avatarState,
     List <Guid> foods,
     SimulatorSheets simulatorSheets
     )
 {
     Random                      = random;
     MaterialItemSheet           = simulatorSheets.MaterialItemSheet;
     SkillSheet                  = simulatorSheets.SkillSheet;
     SkillBuffSheet              = simulatorSheets.SkillBuffSheet;
     BuffSheet                   = simulatorSheets.BuffSheet;
     CharacterSheet              = simulatorSheets.CharacterSheet;
     CharacterLevelSheet         = simulatorSheets.CharacterLevelSheet;
     EquipmentItemSetEffectSheet = simulatorSheets.EquipmentItemSetEffectSheet;
     Log    = new BattleLog();
     Player = new Player(avatarState, this);
     Player.Use(foods);
     Player.Stats.EqualizeCurrentHPWithHP();
 }
Beispiel #10
0
 public RankingSimulatorSheets(
     MaterialItemSheet materialItemSheet,
     SkillSheet skillSheet,
     SkillBuffSheet skillBuffSheet,
     BuffSheet buffSheet,
     CharacterSheet characterSheet,
     CharacterLevelSheet characterLevelSheet,
     EquipmentItemSetEffectSheet equipmentItemSetEffectSheet,
     WeeklyArenaRewardSheet weeklyArenaRewardSheet
     ) : base(
         materialItemSheet,
         skillSheet,
         skillBuffSheet,
         buffSheet,
         characterSheet,
         characterLevelSheet,
         equipmentItemSetEffectSheet
         )
 {
     WeeklyArenaRewardSheet = weeklyArenaRewardSheet;
 }
Beispiel #11
0
        public static List <ItemBase> SetReward(
            WeightedSelector <StageSheet.RewardData> itemSelector,
            int maxCount,
            IRandom random,
            MaterialItemSheet materialItemSheet
            )
        {
            var reward = new List <ItemBase>();

            while (reward.Count < maxCount)
            {
                try
                {
                    var data = itemSelector.Select(1).First();
                    if (materialItemSheet.TryGetValue(data.ItemId, out var itemData))
                    {
                        var count = random.Next(data.Min, data.Max + 1);
                        for (var i = 0; i < count; i++)
                        {
                            var item = ItemFactory.CreateMaterial(itemData);
                            if (reward.Count < maxCount)
                            {
                                reward.Add(item);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }
                catch (ListEmptyException)
                {
                    break;
                }
            }

            reward = reward.OrderBy(r => r.Id).ToList();
            return(reward);
        }
Beispiel #12
0
        public static void AddItem(CostumeItemSheet costumeItemSheet,
                                   EquipmentItemSheet equipmentItemSheet,
                                   EquipmentItemOptionSheet optionSheet,
                                   SkillSheet skillSheet,
                                   MaterialItemSheet materialItemSheet,
                                   ConsumableItemSheet consumableItemSheet,
                                   IRandom random,
                                   Item item,
                                   AddedItemInfo addedItemInfo,
                                   AvatarState avatarState)
        {
            switch (item.ItemSubType)
            {
            case ItemSubType.FullCostume:
            case ItemSubType.HairCostume:
            case ItemSubType.EarCostume:
            case ItemSubType.EyeCostume:
            case ItemSubType.TailCostume:
            case ItemSubType.Title:
                if (costumeItemSheet.TryGetValue(item.ID, out var costumeRow))
                {
                    var costume =
                        ItemFactory.CreateCostume(costumeRow, addedItemInfo.TradableId);
                    avatarState.inventory.AddItem(costume);
                }

                break;

            case ItemSubType.Weapon:
            case ItemSubType.Armor:
            case ItemSubType.Belt:
            case ItemSubType.Necklace:
            case ItemSubType.Ring:
                if (equipmentItemSheet.TryGetValue(item.ID, out var equipmentRow))
                {
                    var equipment = (Equipment)ItemFactory.CreateItemUsable(equipmentRow,
                                                                            addedItemInfo.TradableId,
                                                                            0,
                                                                            item.Level);

                    if (item.OptionIds.Length > 0)
                    {
                        var optionRows = new List <EquipmentItemOptionSheet.Row>();
                        foreach (var optionId in item.OptionIds)
                        {
                            if (!optionSheet.TryGetValue(optionId, out var optionRow))
                            {
                                continue;
                            }

                            optionRows.Add(optionRow);
                        }

                        AddOption(skillSheet, equipment, optionRows, random);
                    }

                    avatarState.inventory.AddItem(equipment);
                }

                break;

            case ItemSubType.Hourglass:
            case ItemSubType.ApStone:
                if (materialItemSheet.TryGetValue(item.ID, out var materialRow))
                {
                    var material = ItemFactory.CreateTradableMaterial(materialRow);
                    avatarState.inventory.AddItem(material, item.Count);
                    addedItemInfo.TradableId = material.TradableId;
                }

                break;

            case ItemSubType.Food:
                if (consumableItemSheet.TryGetValue(item.ID, out var consumableRow))
                {
                    var consumable = (Consumable)ItemFactory.CreateItemUsable(consumableRow,
                                                                              addedItemInfo.TradableId,
                                                                              0,
                                                                              item.Level);
                    avatarState.inventory.AddItem(consumable);
                }

                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
Beispiel #13
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                foreach (var purchaseInfo in purchaseInfos)
                {
                    Address shardedShopAddress =
                        ShardedShopState.DeriveAddress(purchaseInfo.itemSubType, purchaseInfo.productId);
                    states = states
                             .SetState(shardedShopAddress, MarkChanged)
                             .SetState(purchaseInfo.sellerAvatarAddress, MarkChanged)
                             .MarkBalanceChanged(
                        GoldCurrencyMock,
                        ctx.Signer,
                        purchaseInfo.sellerAgentAddress,
                        GoldCurrencyState.Address);
                }
                return(states
                       .SetState(buyerAvatarAddress, MarkChanged)
                       .SetState(ctx.Signer, MarkChanged)
                       .SetState(Addresses.Shop, MarkChanged));
            }

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

            var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress);

            var sw = new Stopwatch();

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

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

            if (!states.TryGetAvatarState(ctx.Signer, buyerAvatarAddress, out var buyerAvatarState))
            {
                throw new FailedLoadStateException(
                          $"{addressesHex}Aborted as the avatar state of the buyer was failed to load.");
            }

            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Get Buyer AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            if (!buyerAvatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop))
            {
                buyerAvatarState.worldInformation.TryGetLastClearedStageId(out var current);
                throw new NotEnoughClearedStageLevelException(addressesHex,
                                                              GameConfig.RequireClearedStageLevel.ActionsInShop, current);
            }

            List <Buy7.PurchaseResult> purchaseResults = new List <Buy7.PurchaseResult>();
            List <Buy7.SellerResult>   sellerResults   = new List <Buy7.SellerResult>();
            MaterialItemSheet          materialSheet   = states.GetSheet <MaterialItemSheet>();

            buyerMultipleResult  = new Buy7.BuyerMultipleResult();
            sellerMultipleResult = new Buy7.SellerMultipleResult();

            foreach (var purchaseInfo in purchaseInfos)
            {
                Buy7.PurchaseResult purchaseResult     = new Buy7.PurchaseResult(purchaseInfo.productId);
                Address             shardedShopAddress =
                    ShardedShopState.DeriveAddress(purchaseInfo.itemSubType, purchaseInfo.productId);
                Address sellerAgentAddress  = purchaseInfo.sellerAgentAddress;
                Address sellerAvatarAddress = purchaseInfo.sellerAvatarAddress;
                Guid    productId           = purchaseInfo.productId;

                purchaseResults.Add(purchaseResult);

                if (purchaseInfo.sellerAgentAddress == ctx.Signer)
                {
                    purchaseResult.errorCode = ErrorCodeInvalidAddress;
                    continue;
                }

                if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict))
                {
                    ShardedShopState shardedShopState = new ShardedShopState(shardedShopAddress);
                    shopStateDict = (Dictionary)shardedShopState.Serialize();
                }

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

                Log.Verbose(
                    "{AddressesHex}Execute Buy; buyer: {Buyer} seller: {Seller}",
                    addressesHex,
                    buyerAvatarAddress,
                    sellerAvatarAddress);
                // Find product from ShardedShopState.
                List       products              = (List)shopStateDict[ProductsKey];
                IValue     productIdSerialized   = productId.Serialize();
                IValue     sellerAgentSerialized = purchaseInfo.sellerAgentAddress.Serialize();
                Dictionary productSerialized     = products
                                                   .Select(p => (Dictionary)p)
                                                   .FirstOrDefault(p =>
                                                                   p[LegacyProductIdKey].Equals(productIdSerialized) &&
                                                                   p[LegacySellerAgentAddressKey].Equals(sellerAgentSerialized));

                bool fromLegacy = false;
                if (productSerialized.Equals(Dictionary.Empty))
                {
                    if (purchaseInfo.itemSubType == ItemSubType.Hourglass ||
                        purchaseInfo.itemSubType == ItemSubType.ApStone)
                    {
                        purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                        continue;
                    }
                    // Backward compatibility.
                    IValue rawShop = states.GetState(Addresses.Shop);
                    if (!(rawShop is null))
                    {
                        Dictionary legacyShopDict = (Dictionary)rawShop;
                        Dictionary legacyProducts = (Dictionary)legacyShopDict[LegacyProductsKey];
                        IKey       productKey     = (IKey)productId.Serialize();
                        // SoldOut
                        if (!legacyProducts.ContainsKey(productKey))
                        {
                            purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                            continue;
                        }

                        productSerialized = (Dictionary)legacyProducts[productKey];
                        legacyProducts    = (Dictionary)legacyProducts.Remove(productKey);
                        legacyShopDict    = legacyShopDict.SetItem(LegacyProductsKey, legacyProducts);
                        states            = states.SetState(Addresses.Shop, legacyShopDict);
                        fromLegacy        = true;
                    }
                }

                ShopItem shopItem = new ShopItem(productSerialized);
                if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress))
                {
                    purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                    continue;
                }

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

                if (0 < shopItem.ExpiredBlockIndex && shopItem.ExpiredBlockIndex < context.BlockIndex)
                {
                    purchaseResult.errorCode = ErrorCodeShopItemExpired;
                    continue;
                }

                if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState))
                {
                    purchaseResult.errorCode = ErrorCodeFailedLoadingState;
                    continue;
                }

                sw.Stop();
                Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed);
                sw.Restart();

                // Check Balance.
                FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency());
                if (buyerBalance < shopItem.Price)
                {
                    purchaseResult.errorCode = ErrorCodeInsufficientBalance;
                    continue;
                }

                var tax        = shopItem.Price.DivRem(100, out _) * TaxRate;
                var taxedPrice = shopItem.Price - tax;

                // Transfer tax.
                states = states.TransferAsset(
                    context.Signer,
                    GoldCurrencyState.Address,
                    tax);

                // Transfer seller.
                states = states.TransferAsset(
                    context.Signer,
                    sellerAgentAddress,
                    taxedPrice
                    );

                products      = (List)products.Remove(productSerialized);
                shopStateDict = shopStateDict.SetItem(ProductsKey, new List <IValue>(products));

                ITradableItem tradableItem;
                int           count = 1;
                if (!(shopItem.ItemUsable is null))
                {
                    tradableItem = shopItem.ItemUsable;
                }
Beispiel #14
0
 public SimulatorTest()
 {
     _materialItemSheet = new MaterialItemSheet();
     _materialItemSheet.Set(TableSheetsImporter.ImportSheets()[nameof(MaterialItemSheet)]);
     _random = new ItemEnhancementTest.TestRandom();
 }
Beispiel #15
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                foreach (var purchaseInfo in purchaseInfos)
                {
                    Address shardedShopAddress =
                        ShardedShopState.DeriveAddress(purchaseInfo.itemSubType, purchaseInfo.productId);
                    states = states
                             .SetState(shardedShopAddress, MarkChanged)
                             .SetState(purchaseInfo.sellerAvatarAddress, MarkChanged)
                             .MarkBalanceChanged(
                        GoldCurrencyMock,
                        ctx.Signer,
                        purchaseInfo.sellerAgentAddress,
                        GoldCurrencyState.Address);
                }
                return(states
                       .SetState(buyerAvatarAddress, MarkChanged)
                       .SetState(ctx.Signer, MarkChanged)
                       .SetState(Addresses.Shop, MarkChanged));
            }

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

            var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress);

            var sw = new Stopwatch();

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

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

            if (!states.TryGetAvatarState(ctx.Signer, buyerAvatarAddress, out var buyerAvatarState))
            {
                throw new FailedLoadStateException(
                          $"{addressesHex}Aborted as the avatar state of the buyer was failed to load.");
            }

            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Get Buyer AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            if (!buyerAvatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop))
            {
                buyerAvatarState.worldInformation.TryGetLastClearedStageId(out var current);
                throw new NotEnoughClearedStageLevelException(addressesHex,
                                                              GameConfig.RequireClearedStageLevel.ActionsInShop, current);
            }

            List <Buy7.PurchaseResult> purchaseResults = new List <Buy7.PurchaseResult>();
            List <Buy7.SellerResult>   sellerResults   = new List <Buy7.SellerResult>();
            MaterialItemSheet          materialSheet   = states.GetSheet <MaterialItemSheet>();

            buyerMultipleResult  = new Buy7.BuyerMultipleResult();
            sellerMultipleResult = new Buy7.SellerMultipleResult();

            foreach (var purchaseInfo in purchaseInfos)
            {
                Buy7.PurchaseResult purchaseResult     = new Buy7.PurchaseResult(purchaseInfo.productId);
                Address             shardedShopAddress =
                    ShardedShopState.DeriveAddress(purchaseInfo.itemSubType, purchaseInfo.productId);
                Address sellerAgentAddress  = purchaseInfo.sellerAgentAddress;
                Address sellerAvatarAddress = purchaseInfo.sellerAvatarAddress;
                Guid    productId           = purchaseInfo.productId;

                purchaseResults.Add(purchaseResult);

                if (purchaseInfo.sellerAgentAddress == ctx.Signer)
                {
                    purchaseResult.errorCode = ErrorCodeInvalidAddress;
                    continue;
                }

                if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict))
                {
                    ShardedShopState shardedShopState = new ShardedShopState(shardedShopAddress);
                    shopStateDict = (Dictionary)shardedShopState.Serialize();
                }

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

                Log.Verbose(
                    "{AddressesHex}Execute Buy; buyer: {Buyer} seller: {Seller}",
                    addressesHex,
                    buyerAvatarAddress,
                    sellerAvatarAddress);
                // Find product from ShardedShopState.
                List       products            = (List)shopStateDict[ProductsKey];
                IValue     productIdSerialized = productId.Serialize();
                Dictionary productSerialized   = products
                                                 .Select(p => (Dictionary)p)
                                                 .FirstOrDefault(p => p[LegacyProductIdKey].Equals(productIdSerialized));

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

                bool fromLegacy = false;
                if (productSerialized.Equals(Dictionary.Empty))
                {
                    // Backward compatibility.
                    IValue rawShop = states.GetState(Addresses.Shop);
                    if (!(rawShop is null))
                    {
                        Dictionary legacyShopDict = (Dictionary)rawShop;
                        Dictionary legacyProducts = (Dictionary)legacyShopDict[LegacyProductsKey];
                        IKey       productKey     = (IKey)productId.Serialize();
                        // SoldOut
                        if (!legacyProducts.ContainsKey(productKey))
                        {
                            purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                            continue;
                        }

                        productSerialized = (Dictionary)legacyProducts[productKey];
                        legacyProducts    = (Dictionary)legacyProducts.Remove(productKey);
                        legacyShopDict    = legacyShopDict.SetItem(LegacyProductsKey, legacyProducts);
                        states            = states.SetState(Addresses.Shop, legacyShopDict);
                        fromLegacy        = true;
                    }
                }

                ShopItem shopItem = new ShopItem(productSerialized);
                if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress))
                {
                    purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                    continue;
                }

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

                if (0 < shopItem.ExpiredBlockIndex && shopItem.ExpiredBlockIndex < context.BlockIndex)
                {
                    purchaseResult.errorCode = ErrorCodeShopItemExpired;
                    continue;
                }

                if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState))
                {
                    purchaseResult.errorCode = ErrorCodeFailedLoadingState;
                    continue;
                }

                sw.Stop();
                Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed);
                sw.Restart();

                // Check Balance.
                FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency());
                if (buyerBalance < shopItem.Price)
                {
                    purchaseResult.errorCode = ErrorCodeInsufficientBalance;
                    continue;
                }

                var tax        = shopItem.Price.DivRem(100, out _) * TaxRate;
                var taxedPrice = shopItem.Price - tax;

                // Transfer tax.
                states = states.TransferAsset(
                    context.Signer,
                    GoldCurrencyState.Address,
                    tax);

                // Transfer seller.
                states = states.TransferAsset(
                    context.Signer,
                    sellerAgentAddress,
                    taxedPrice
                    );

                products      = (List)products.Remove(productSerialized);
                shopStateDict = shopStateDict.SetItem(ProductsKey, new List <IValue>(products));

                INonFungibleItem nonFungibleItem = (INonFungibleItem)shopItem.ItemUsable ?? shopItem.Costume;
                if (!sellerAvatarState.inventory.RemoveNonFungibleItem(nonFungibleItem) && !fromLegacy)
                {
                    purchaseResult.errorCode = ErrorCodeItemDoesNotExist;
                    continue;
                }

                nonFungibleItem.RequiredBlockIndex = context.BlockIndex;

                // Send result mail for buyer, seller.
                purchaseResult.shopItem   = shopItem;
                purchaseResult.itemUsable = shopItem.ItemUsable;
                purchaseResult.costume    = shopItem.Costume;
                var buyerMail = new BuyerMail(purchaseResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(),
                                              ctx.BlockIndex);
                purchaseResult.id = buyerMail.id;

                var sellerResult = new Buy7.SellerResult
                {
                    shopItem   = shopItem,
                    itemUsable = shopItem.ItemUsable,
                    costume    = shopItem.Costume,
                    gold       = taxedPrice
                };
                var sellerMail = new SellerMail(sellerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(),
                                                ctx.BlockIndex);
                sellerResult.id = sellerMail.id;
                sellerResults.Add(sellerResult);

                buyerAvatarState.Update(buyerMail);
                if (purchaseResult.itemUsable != null)
                {
                    buyerAvatarState.UpdateFromAddItem2(purchaseResult.itemUsable, false);
                }

                if (purchaseResult.costume != null)
                {
                    buyerAvatarState.UpdateFromAddCostume(purchaseResult.costume, false);
                }

                sellerAvatarState.Update(sellerMail);

                // Update quest.
                buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, shopItem.Price);
                sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, shopItem.Price);

                sellerAvatarState.updatedAt  = ctx.BlockIndex;
                sellerAvatarState.blockIndex = ctx.BlockIndex;

                buyerAvatarState.UpdateQuestRewards2(materialSheet);
                sellerAvatarState.UpdateQuestRewards2(materialSheet);

                states = states.SetState(sellerAvatarAddress, sellerAvatarState.Serialize());
                sw.Stop();
                Log.Verbose("{AddressesHex}Buy Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
                sw.Restart();
                states = states.SetState(shardedShopAddress, shopStateDict);
                sw.Stop();
                Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed);
            }

            buyerMultipleResult.purchaseResults = purchaseResults;
            sellerMultipleResult.sellerResults  = sellerResults;

            buyerAvatarState.updatedAt  = ctx.BlockIndex;
            buyerAvatarState.blockIndex = ctx.BlockIndex;

            states = states.SetState(buyerAvatarAddress, buyerAvatarState.Serialize());
            sw.Stop();
            Log.Verbose("{AddressesHex}Buy Set Buyer AvatarState: {Elapsed}", addressesHex, sw.Elapsed);
            sw.Restart();

            var ended = DateTimeOffset.UtcNow;

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

            return(states);
        }
Beispiel #16
0
 public static Material CreateMaterial(MaterialItemSheet sheet, int itemId)
 {
     return(!sheet.TryGetValue(itemId, out var itemData)
         ? null
         : CreateMaterial(itemData));
 }