public override void Render(IActionContext context, IAccountStateDelta nextStates)
        {
            GameState gameState;

            if (nextStates.TryGetState(SessionState.Address, out Bencodex.Types.Dictionary bdict))
            {
                var sessionState = new SessionState(bdict);
                sessionState.sessions.TryGetValue(SessionID, out gameState);
            }
            else
            {
                Debug.LogError("SessionState is empty on render.");
                return;
            }

            if (gameState is null)
            {
                Debug.LogError("GameState is null on rendering.");
                return;
            }

            if (gameState.SessionID == GameManager.instance.currentSession)
            {
                Agent.instance.RunOnMainThread(() =>
                {
                    GameManager.instance.gameboard?.PlaceNode(false, gameState.Turn, Index);
                    GameManager.instance.SetMyTurn(gameState.Turn == gameState.Players.IndexOf(Agent.instance.Address));
                    GameManager.instance.gameboard.UpdateInfo();
                });
            }
        }
Exemple #2
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta state = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(state
                       .SetState(ActivatedAccountsState.Address, MarkChanged));
            }

            if (!state.TryGetState(ActivatedAccountsState.Address, out Dictionary accountsAsDict))
            {
                throw new ActivatedAccountsDoesNotExistsException();
            }

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

            var accounts = new ActivatedAccountsState(accountsAsDict);

            return(state.SetState(
                       ActivatedAccountsState.Address,
                       accounts.AddAccount(Address).Serialize()
                       ));
        }
        public override void Render(IActionContext context, IAccountStateDelta nextStates)
        {
            GameState gameState;

            if (nextStates.TryGetState(SessionState.Address, out Bencodex.Types.Dictionary bdict))
            {
                var sessionState = new SessionState(bdict);
                sessionState.sessions.TryGetValue(SessionID, out gameState);
            }
            else
            {
                Debug.LogError("SessionState is empty on render.");
                return;
            }

            if (gameState is null)
            {
                Debug.LogError("GameState is null on rendering.");
                return;
            }

            if (gameState.SessionID == GameManager.instance.currentSession)
            {
                Agent.instance.RunOnMainThread(() =>
                {
                    Debug.LogWarning("Render Resign Action");
                    GameManager.instance.gameboard?.SetResult(gameState.Winner == Agent.instance.Address);
                });
            }
        }
Exemple #4
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta state = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(state
                       .SetState(ActivatedAccountsState.Address, MarkChanged)
                       .SetState(PendingAddress, MarkChanged));
            }

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

            if (!state.TryGetState(ActivatedAccountsState.Address, out Dictionary accountsAsDict))
            {
                throw new ActivatedAccountsDoesNotExistsException();
            }
            if (!state.TryGetState(PendingAddress, out Dictionary pendingAsDict))
            {
                throw new PendingActivationDoesNotExistsException(PendingAddress);
            }

            var accounts = new ActivatedAccountsState(accountsAsDict);
            var pending  = new PendingActivationState(pendingAsDict);

            if (pending.Verify(Signature))
            {
                // We left this log message to track activation history.
                // Please delete it if we have an API for evaluation results on the Libplanet side.
                Log.Information("{pendingAddress} is activated by {signer} now.", pending.address, context.Signer);
                return(state.SetState(
                           ActivatedAccountsState.Address,
                           accounts.AddAccount(context.Signer).Serialize()
                           ).SetState(
                           pending.address,
                           new Bencodex.Types.Null()
                           ));
            }
            else
            {
                throw new InvalidSignatureException(pending, Signature);
            }
        }
Exemple #5
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta states            = context.PreviousStates;
            Address            collectionAddress = MonsterCollectionState0.DeriveAddress(context.Signer, collectRound);

            if (context.Rehearsal)
            {
                return(states
                       .SetState(collectionAddress, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, collectionAddress, context.Signer));
            }

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

            AgentState agentState = states.GetAgentState(context.Signer);

            if (agentState is null)
            {
                throw new FailedLoadStateException("Aborted as the agent state failed to load.");
            }

            if (!states.TryGetState(collectionAddress, out Dictionary stateDict))
            {
                throw new FailedLoadStateException($"Aborted as the monster collection state failed to load.");
            }

            MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(stateDict);
            Currency               currency = states.GetGoldCurrency();
            FungibleAssetValue     balance  = 0 * currency;
            MonsterCollectionSheet monsterCollectionSheet = states.GetSheet <MonsterCollectionSheet>();
            int currentLevel = monsterCollectionState.Level;

            if (currentLevel <= level || level <= 0)
            {
                throw new InvalidLevelException($"The level must be greater than 0 and less than {currentLevel}.");
            }

            if (monsterCollectionState.End)
            {
                throw new MonsterCollectionExpiredException($"{collectionAddress} has already expired on {monsterCollectionState.ExpiredBlockIndex}");
            }

            long rewardLevel = monsterCollectionState.GetRewardLevel(context.BlockIndex);
            MonsterCollectionRewardSheet monsterCollectionRewardSheet = states.GetSheet <MonsterCollectionRewardSheet>();

            monsterCollectionState.Update(level, rewardLevel, monsterCollectionRewardSheet);
            for (int i = currentLevel; i > level; i--)
            {
                balance += monsterCollectionSheet[i].RequiredGold * currency;
            }

            return(states
                   .SetState(collectionAddress, monsterCollectionState.Serialize())
                   .TransferAsset(collectionAddress, context.Signer, balance));
        }
Exemple #6
0
        public static Currency GetGoldCurrency(this IAccountStateDelta states)
        {
            if (states.TryGetState(GoldCurrencyState.Address, out Dictionary asDict))
            {
                return(new GoldCurrencyState(asDict).Currency);
            }

            throw new InvalidOperationException(
                      "The states doesn't contain gold currency.\n" +
                      "Check the genesis block."
                      );
        }
Exemple #7
0
        public IAccountStateDelta ResetChallengeCount(IActionContext ctx, IAccountStateDelta states)
        {
            var gameConfigState = states.GetGameConfigState();
            var index           = Math.Max((int)ctx.BlockIndex / gameConfigState.WeeklyArenaInterval, 0);
            var weeklyAddress   = WeeklyArenaState.DeriveAddress(index);
            var rawWeekly       = (Dictionary)states.GetState(weeklyAddress);
            var resetIndex      = rawWeekly["resetIndex"].ToLong();

            if (ctx.BlockIndex - resetIndex >= gameConfigState.DailyArenaInterval)
            {
                var weekly = new WeeklyArenaState(rawWeekly);
                if (resetIndex >= RankingBattle.UpdateTargetBlockIndex)
                {
                    // Reset count each ArenaInfo.
                    weekly.ResetIndex = ctx.BlockIndex;
                    var listAddress = weeklyAddress.Derive("address_list");
                    if (states.TryGetState(listAddress, out List rawList))
                    {
                        var addressList = rawList.ToList(StateExtensions.ToAddress);
                        foreach (var address in addressList)
                        {
                            var infoAddress = weeklyAddress.Derive(address.ToByteArray());
                            if (states.TryGetState(infoAddress, out Dictionary rawInfo))
                            {
                                var info = new ArenaInfo(rawInfo);
                                info.ResetCount();
                                states = states.SetState(infoAddress, info.Serialize());
                            }
                        }
                    }
                }
                else
                {
                    // Run legacy ResetCount.
                    weekly.ResetCount(ctx.BlockIndex);
                }
                states = states.SetState(weeklyAddress, weekly.Serialize());
            }
            return(states);
        }
Exemple #8
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta state = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(state
                       .SetState(ActivatedAccountsState.Address, MarkChanged)
                       .SetState(PendingAddress, MarkChanged));
            }

            if (!state.TryGetState(ActivatedAccountsState.Address, out Dictionary accountsAsDict))
            {
                throw new ActivatedAccountsDoesNotExistsException();
            }
            if (!state.TryGetState(PendingAddress, out Dictionary pendingAsDict))
            {
                throw new PendingActivationDoesNotExistsException(PendingAddress);
            }

            var accounts = new ActivatedAccountsState(accountsAsDict);
            var pending  = new PendingActivationState(pendingAsDict);

            if (pending.Verify(this))
            {
                return(state.SetState(
                           ActivatedAccountsState.Address,
                           accounts.AddAccount(context.Signer).Serialize()
                           ).SetState(
                           pending.address,
                           new Bencodex.Types.Null()
                           ));
            }
            else
            {
                throw new InvalidSignatureException(pending, Signature);
            }
        }
        public override void Render(IActionContext context, IAccountStateDelta nextStates)
        {
            SessionState sessionState;

            if (nextStates.TryGetState(SessionState.Address, out Bencodex.Types.Dictionary bdict))
            {
                sessionState = new SessionState(bdict);
            }
            else
            {
                sessionState = new SessionState();
            }

            Agent.instance.RunOnMainThread(() =>
            {
                GameManager.instance.sessionUI?.UpdateUI(sessionState, SessionID);
            });
        }
Exemple #10
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta state            = context.PreviousStates;
            Address            activatedAddress = context.Signer.Derive(ActivationKey.DeriveKey);

            if (context.Rehearsal)
            {
                return(state
                       .SetState(activatedAddress, MarkChanged)
                       .SetState(PendingAddress, MarkChanged));
            }

            if (!(state.GetState(activatedAddress) is null))
            {
                throw new AlreadyActivatedException($"{context.Signer} already activated.");
            }
            if (!state.TryGetState(PendingAddress, out Dictionary pendingAsDict))
            {
                throw new PendingActivationDoesNotExistsException(PendingAddress);
            }

            var pending = new PendingActivationState(pendingAsDict);

            if (pending.Verify(this))
            {
                // We left this log message to track activation history.
                // Please delete it if we have an API for evaluation results on the Libplanet side.
                Log.Information("{pendingAddress} is activated by {signer} now.", pending.address, context.Signer);
                return(state
                       .SetState(activatedAddress, true.Serialize())
                       .SetState(pending.address, new Bencodex.Types.Null()));
            }
            else
            {
                throw new InvalidSignatureException(pending, Signature);
            }
        }
        public void TargetBlock()
        {
            var rb = new RankingBattle
            {
                avatarAddress      = _avatar1Address,
                enemyAddress       = _avatar3Address,
                weeklyArenaAddress = _weeklyArenaAddress,
                costumeIds         = new List <Guid>(),
                equipmentIds       = new List <Guid>(),
            };

            var arenaInfoAddress  = _weeklyArenaAddress.Derive(_avatar1Address.ToByteArray());
            var arenaInfo2Address = _weeklyArenaAddress.Derive(_avatar2Address.ToByteArray());
            var arenaInfo3Address = _weeklyArenaAddress.Derive(_avatar3Address.ToByteArray());
            var listAddress       = _weeklyArenaAddress.Derive("address_list");

            Assert.False(_initialState.TryGetState(arenaInfoAddress, out Dictionary _));
            Assert.False(_initialState.TryGetState(arenaInfo2Address, out Dictionary _));
            Assert.False(_initialState.TryGetState(arenaInfo3Address, out Dictionary _));
            Assert.False(_initialState.TryGetState(listAddress, out List _));

            var testRandom = new TestRandom();
            var blockIndex = RankingBattle.UpdateTargetBlockIndex;
            var nextState  = rb.Execute(new ActionContext
            {
                PreviousStates = _initialState,
                Signer         = _agent1Address,
                Random         = testRandom,
                Rehearsal      = false,
                BlockIndex     = blockIndex,
            });

            Assert.True(nextState.TryGetState(arenaInfoAddress, out Dictionary rawInfo));
            Assert.True(nextState.TryGetState(arenaInfo3Address, out Dictionary _));
            Assert.True(nextState.TryGetState(listAddress, out List rawList));

            var info        = new ArenaInfo(rawInfo);
            var addressList = rawList.ToList(StateExtensions.ToAddress);

            Assert.Equal(4, info.DailyChallengeCount);
            Assert.Contains(_avatar1Address, addressList);
            Assert.DoesNotContain(_avatar2Address, addressList);
            Assert.Contains(_avatar3Address, addressList);

            var rg = new RewardGold();

            var updatedState = rg.Execute(new ActionContext
            {
                PreviousStates = nextState,
                Signer         = _agent1Address,
                Random         = testRandom,
                Rehearsal      = false,
                BlockIndex     = blockIndex,
            });

            Assert.True(updatedState.TryGetState(arenaInfoAddress, out Dictionary updatedRawInfo));
            Assert.True(updatedState.TryGetState(arenaInfo2Address, out Dictionary _));
            Assert.True(updatedState.TryGetState(arenaInfo3Address, out Dictionary _));
            Assert.True(updatedState.TryGetState(listAddress, out List updatedRawList));

            var updatedInfo        = new ArenaInfo(updatedRawInfo);
            var updatedAddressList = updatedRawList.ToList(StateExtensions.ToAddress);

            Assert.Equal(5, updatedInfo.DailyChallengeCount);
            Assert.Contains(_avatar1Address, updatedAddressList);
            Assert.Contains(_avatar2Address, updatedAddressList);
            Assert.Contains(_avatar3Address, updatedAddressList);
        }
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta states                  = context.PreviousStates;
            Address            inventoryAddress        = avatarAddress.Derive(LegacyInventoryKey);
            Address            worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey);
            Address            questListAddress        = avatarAddress.Derive(LegacyQuestListKey);

            if (context.Rehearsal)
            {
                return(states
                       .SetState(avatarAddress, MarkChanged)
                       .SetState(inventoryAddress, MarkChanged)
                       .SetState(worldInformationAddress, MarkChanged)
                       .SetState(questListAddress, MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 0), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 1), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 2), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 3), MarkChanged));
            }

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

            if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out AgentState agentState, out AvatarState avatarState))
            {
                throw new FailedLoadStateException($"Aborted as the avatar state of the signer failed to load.");
            }

            Address collectionAddress = MonsterCollectionState.DeriveAddress(context.Signer, agentState.MonsterCollectionRound);

            if (!states.TryGetState(collectionAddress, out Dictionary stateDict))
            {
                throw new FailedLoadStateException($"Aborted as the monster collection state failed to load.");
            }

            var monsterCollectionState = new MonsterCollectionState(stateDict);
            List <MonsterCollectionRewardSheet.RewardInfo> rewards =
                monsterCollectionState.CalculateRewards(
                    states.GetSheet <MonsterCollectionRewardSheet>(),
                    context.BlockIndex
                    );

            if (rewards.Count == 0)
            {
                throw new RequiredBlockIndexException($"{collectionAddress} is not available yet");
            }

            Guid id     = context.Random.GenerateRandomGuid();
            var  result = new MonsterCollectionResult(id, avatarAddress, rewards);
            var  mail   = new MonsterCollectionMail(result, context.BlockIndex, id, context.BlockIndex);

            avatarState.Update(mail);

            ItemSheet itemSheet = states.GetItemSheet();

            foreach (MonsterCollectionRewardSheet.RewardInfo rewardInfo in rewards)
            {
                ItemSheet.Row row  = itemSheet[rewardInfo.ItemId];
                ItemBase      item = row is MaterialItemSheet.Row materialRow
                    ? ItemFactory.CreateTradableMaterial(materialRow)
                    : ItemFactory.CreateItem(row, context.Random);
                avatarState.inventory.AddItem2(item, rewardInfo.Quantity);
            }
            monsterCollectionState.Claim(context.BlockIndex);

            return(states
                   .SetState(avatarAddress, avatarState.SerializeV2())
                   .SetState(inventoryAddress, avatarState.inventory.Serialize())
                   .SetState(worldInformationAddress, avatarState.worldInformation.Serialize())
                   .SetState(questListAddress, avatarState.questList.Serialize())
                   .SetState(collectionAddress, monsterCollectionState.Serialize()));
        }
Exemple #13
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta states = context.PreviousStates;
            Address            monsterCollectionAddress = MonsterCollectionState0.DeriveAddress(context.Signer, collectionRound);

            if (context.Rehearsal)
            {
                return(states
                       .SetState(monsterCollectionAddress, MarkChanged)
                       .SetState(context.Signer, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, monsterCollectionAddress));
            }

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

            MonsterCollectionSheet monsterCollectionSheet = states.GetSheet <MonsterCollectionSheet>();

            AgentState agentState = states.GetAgentState(context.Signer);

            if (agentState is null)
            {
                throw new FailedLoadStateException("Aborted as the agent state failed to load.");
            }

            if (agentState.MonsterCollectionRound != collectionRound)
            {
                throw new InvalidMonsterCollectionRoundException(
                          $"Expected collection round is {agentState.MonsterCollectionRound}, but actual collection round is {collectionRound}.");
            }

            if (!monsterCollectionSheet.TryGetValue(level, out MonsterCollectionSheet.Row _))
            {
                throw new SheetRowNotFoundException(nameof(MonsterCollectionSheet), level);
            }

            Currency currency = states.GetGoldCurrency();
            // Set default gold value.
            FungibleAssetValue requiredGold = currency * 0;
            FungibleAssetValue balance      = states.GetBalance(context.Signer, states.GetGoldCurrency());

            MonsterCollectionState0 monsterCollectionState;
            int currentLevel = 1;
            MonsterCollectionRewardSheet monsterCollectionRewardSheet = states.GetSheet <MonsterCollectionRewardSheet>();

            if (states.TryGetState(monsterCollectionAddress, out Dictionary stateDict))
            {
                monsterCollectionState = new MonsterCollectionState0(stateDict);

                if (monsterCollectionState.ExpiredBlockIndex < context.BlockIndex)
                {
                    throw new MonsterCollectionExpiredException(
                              $"{monsterCollectionAddress} has already expired on {monsterCollectionState.ExpiredBlockIndex}");
                }

                if (monsterCollectionState.Level >= level)
                {
                    throw new InvalidLevelException($"The level must be greater than {monsterCollectionState.Level}.");
                }

                currentLevel = monsterCollectionState.Level + 1;
                long rewardLevel = monsterCollectionState.GetRewardLevel(context.BlockIndex);
                monsterCollectionState.Update(level, rewardLevel, monsterCollectionRewardSheet);
            }
            else
            {
                monsterCollectionState = new MonsterCollectionState0(monsterCollectionAddress, level, context.BlockIndex, monsterCollectionRewardSheet);
            }

            for (int i = currentLevel; i < level + 1; i++)
            {
                requiredGold += currency * monsterCollectionSheet[i].RequiredGold;
            }

            if (balance < requiredGold)
            {
                throw new InsufficientBalanceException(context.Signer, requiredGold,
                                                       $"There is no sufficient balance for {context.Signer}: {balance} < {requiredGold}");
            }
            states = states.TransferAsset(context.Signer, monsterCollectionAddress, requiredGold);
            states = states.SetState(monsterCollectionAddress, monsterCollectionState.Serialize());
            return(states);
        }
Exemple #14
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta states = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(states
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 0), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 1), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 2), MarkChanged)
                       .SetState(MonsterCollectionState.DeriveAddress(context.Signer, 3), MarkChanged)
                       .SetState(context.Signer, MarkChanged)
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 0))
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 1))
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 2))
                       .MarkBalanceChanged(GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 3)));
            }

            MonsterCollectionSheet monsterCollectionSheet = states.GetSheet <MonsterCollectionSheet>();

            AgentState agentState = states.GetAgentState(context.Signer);

            if (agentState is null)
            {
                throw new FailedLoadStateException("Aborted as the agent state failed to load.");
            }

            if (level < 0 || level > 0 && !monsterCollectionSheet.TryGetValue(level, out MonsterCollectionSheet.Row _))
            {
                throw new MonsterCollectionLevelException();
            }

            Currency currency = states.GetGoldCurrency();
            // Set default gold value.
            FungibleAssetValue requiredGold             = currency * 0;
            Address            monsterCollectionAddress = MonsterCollectionState.DeriveAddress(
                context.Signer,
                agentState.MonsterCollectionRound
                );

            if (states.TryGetState(monsterCollectionAddress, out Dictionary stateDict))
            {
                var existingStates = new MonsterCollectionState(stateDict);
                int previousLevel  = existingStates.Level;
                // Check collection level and required block index
                if (level < previousLevel && existingStates.IsLocked(context.BlockIndex))
                {
                    throw new RequiredBlockIndexException();
                }

                if (level == previousLevel)
                {
                    throw new MonsterCollectionLevelException();
                }

                if (existingStates.CalculateStep(context.BlockIndex) > 0)
                {
                    throw new MonsterCollectionExistingClaimableException();
                }

                // Refund holding NCG to user
                FungibleAssetValue gold = states.GetBalance(monsterCollectionAddress, currency);
                states = states.TransferAsset(monsterCollectionAddress, context.Signer, gold);
            }

            if (level == 0)
            {
                return(states.SetState(monsterCollectionAddress, new Null()));
            }

            FungibleAssetValue balance = states.GetBalance(context.Signer, currency);
            var monsterCollectionState = new MonsterCollectionState(monsterCollectionAddress, level, context.BlockIndex);

            for (int i = 0; i < level; i++)
            {
                requiredGold += currency * monsterCollectionSheet[i + 1].RequiredGold;
            }

            if (balance < requiredGold)
            {
                throw new InsufficientBalanceException(context.Signer, requiredGold,
                                                       $"There is no sufficient balance for {context.Signer}: {balance} < {requiredGold}");
            }
            states = states.TransferAsset(context.Signer, monsterCollectionAddress, requiredGold);
            states = states.SetState(monsterCollectionAddress, monsterCollectionState.Serialize());
            return(states);
        }
Exemple #15
0
        public IAccountStateDelta PrepareNextArena(IActionContext ctx, IAccountStateDelta states)
        {
            var gameConfigState = states.GetGameConfigState();
            var index           = Math.Max((int)ctx.BlockIndex / gameConfigState.WeeklyArenaInterval, 0);
            var weeklyAddress   = WeeklyArenaState.DeriveAddress(index);
            var rawWeekly       = (Dictionary)states.GetState(weeklyAddress);
            var nextIndex       = index + 1;
            var nextWeekly      = states.GetWeeklyArenaState(nextIndex);

            if (nextWeekly is null)
            {
                nextWeekly = new WeeklyArenaState(nextIndex);
                states     = states.SetState(nextWeekly.address, nextWeekly.Serialize());
            }

            // Beginning block of a new weekly arena.
            if (ctx.BlockIndex % gameConfigState.WeeklyArenaInterval == 0 && index > 0)
            {
                var prevWeeklyAddress = WeeklyArenaState.DeriveAddress(index - 1);
                var rawPrevWeekly     = (Dictionary)states.GetState(prevWeeklyAddress);
                if (!rawPrevWeekly["ended"].ToBoolean())
                {
                    rawPrevWeekly = rawPrevWeekly.SetItem("ended", true.Serialize());
                    var weekly      = new WeeklyArenaState(rawWeekly);
                    var prevWeekly  = new WeeklyArenaState(rawPrevWeekly);
                    var listAddress = weekly.address.Derive("address_list");
                    // Set ArenaInfo, address list for new RankingBattle.
                    var addressList = states.TryGetState(listAddress, out List rawList)
                        ? rawList.ToList(StateExtensions.ToAddress)
                        : new List <Address>();
                    if (ctx.BlockIndex >= RankingBattle.UpdateTargetBlockIndex)
                    {
                        weekly.ResetIndex = ctx.BlockIndex;

                        // Copy Map to address list.
                        if (ctx.BlockIndex == RankingBattle.UpdateTargetBlockIndex)
                        {
                            foreach (var kv in prevWeekly.Map)
                            {
                                var address  = kv.Key;
                                var lazyInfo = kv.Value;
                                var info     = new ArenaInfo(lazyInfo.State);
                                states = states.SetState(
                                    weeklyAddress.Derive(address.ToByteArray()), info.Serialize());
                                if (!addressList.Contains(address))
                                {
                                    addressList.Add(address);
                                }
                            }
                        }
                        else
                        {
                            // Copy addresses from prev weekly address list.
                            var prevListAddress = prevWeekly.address.Derive("address_list");

                            if (states.TryGetState(prevListAddress, out List prevRawList))
                            {
                                var prevList = prevRawList.ToList(StateExtensions.ToAddress);
                                foreach (var address in prevList.Where(address => !addressList.Contains(address)))
                                {
                                    addressList.Add(address);
                                }
                            }

                            // Copy ArenaInfo from prev ArenaInfo.
                            foreach (var address in addressList)
                            {
                                if (states.TryGetState(
                                        prevWeekly.address.Derive(address.ToByteArray()),
                                        out Dictionary rawInfo))
                                {
                                    var prevInfo = new ArenaInfo(rawInfo);
                                    var info     = new ArenaInfo(prevInfo);
                                    states = states.SetState(
                                        weeklyAddress.Derive(address.ToByteArray()),
                                        info.Serialize());
                                }
                            }
                        }

                        // Set address list.
                        states = states.SetState(listAddress,
                                                 addressList.Aggregate(List.Empty,
                                                                       (current, address) => current.Add(address.Serialize())));
                    }
                    // Run legacy Update.
                    else
                    {
                        weekly.Update(prevWeekly, ctx.BlockIndex);
                    }

                    states = states.SetState(prevWeeklyAddress, rawPrevWeekly);
                    states = states.SetState(weeklyAddress, weekly.Serialize());
                }
            }
            return(states);
        }
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta states            = context.PreviousStates;
            Address            collectionAddress = MonsterCollectionState0.DeriveAddress(context.Signer, collectionRound);

            if (context.Rehearsal)
            {
                return(states
                       .SetState(context.Signer, MarkChanged)
                       .SetState(avatarAddress, MarkChanged)
                       .SetState(collectionAddress, MarkChanged));
            }

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

            if (!states.TryGetAgentAvatarStates(context.Signer, avatarAddress, out AgentState agentState, out AvatarState avatarState))
            {
                throw new FailedLoadStateException($"Aborted as the avatar state of the signer failed to load.");
            }

            if (!states.TryGetState(collectionAddress, out Dictionary stateDict))
            {
                throw new FailedLoadStateException($"Aborted as the monster collection state failed to load.");
            }

            MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(stateDict);

            if (monsterCollectionState.End)
            {
                throw new MonsterCollectionExpiredException($"{collectionAddress} has already expired on {monsterCollectionState.ExpiredBlockIndex}");
            }

            if (!monsterCollectionState.CanReceive(context.BlockIndex))
            {
                throw new RequiredBlockIndexException(
                          $"{collectionAddress} is not available yet; it will be available after {Math.Max(monsterCollectionState.StartedBlockIndex, monsterCollectionState.ReceivedBlockIndex) + MonsterCollectionState0.RewardInterval}");
            }

            long      rewardLevel = monsterCollectionState.GetRewardLevel(context.BlockIndex);
            ItemSheet itemSheet   = states.GetItemSheet();

            for (int i = 0; i < rewardLevel; i++)
            {
                int level = i + 1;
                if (level <= monsterCollectionState.RewardLevel)
                {
                    continue;
                }

                List <MonsterCollectionRewardSheet.RewardInfo> rewards = monsterCollectionState.RewardLevelMap[level];
                Guid id = context.Random.GenerateRandomGuid();
                MonsterCollectionResult result = new MonsterCollectionResult(id, avatarAddress, rewards);
                MonsterCollectionMail   mail   = new MonsterCollectionMail(result, context.BlockIndex, id, context.BlockIndex);
                avatarState.Update(mail);
                foreach (var rewardInfo in rewards)
                {
                    var row  = itemSheet[rewardInfo.ItemId];
                    var item = row is MaterialItemSheet.Row materialRow
                        ? ItemFactory.CreateTradableMaterial(materialRow)
                        : ItemFactory.CreateItem(row, context.Random);
                    avatarState.inventory.AddItem2(item, rewardInfo.Quantity);
                }
                monsterCollectionState.UpdateRewardMap(level, result, context.BlockIndex);
            }

            // Return gold at the end of monster collect.
            if (rewardLevel == 4)
            {
                MonsterCollectionSheet monsterCollectionSheet = states.GetSheet <MonsterCollectionSheet>();
                Currency currency = states.GetGoldCurrency();
                // Set default gold value.
                FungibleAssetValue gold = currency * 0;
                for (int i = 0; i < monsterCollectionState.Level; i++)
                {
                    int level = i + 1;
                    gold += currency * monsterCollectionSheet[level].RequiredGold;
                }
                agentState.IncreaseCollectionRound();
                states = states.SetState(context.Signer, agentState.Serialize());
                if (gold > currency * 0)
                {
                    states = states.TransferAsset(collectionAddress, context.Signer, gold);
                }
            }

            return(states
                   .SetState(avatarAddress, avatarState.Serialize())
                   .SetState(collectionAddress, monsterCollectionState.Serialize()));
        }