Ejemplo n.º 1
0
        public void DeriveAddress(int index, string expected)
        {
            var state = new WeeklyArenaState(index);

            Assert.Equal(new Address(expected), state.address);
        }
Ejemplo n.º 2
0
        public static WeeklyArenaState GetWeeklyArenaState(this IAccountStateDelta states, int index)
        {
            var address = WeeklyArenaState.DeriveAddress(index);

            return(GetWeeklyArenaState(states, address));
        }
Ejemplo n.º 3
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                states = states.SetState(RankingMapAddress, MarkChanged);
                states = states.SetState(avatarAddress, MarkChanged);
                states = states.SetState(WeeklyArenaAddress, MarkChanged);
                return(states.SetState(ctx.Signer, MarkChanged));
            }

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

            var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress);

            var sw = new Stopwatch();

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

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

            if (!states.TryGetAvatarState(ctx.Signer, avatarAddress, out AvatarState avatarState))
            {
                throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load.");
            }

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

            sw.Restart();

            if (avatarState.RankingMapAddress != RankingMapAddress)
            {
                throw new InvalidAddressException($"{addressesHex}Invalid ranking map address");
            }

            // worldId와 stageId가 유효한지 확인합니다.
            var worldSheet = states.GetSheet <WorldSheet>();

            if (!worldSheet.TryGetValue(worldId, out var worldRow, false))
            {
                throw new SheetRowNotFoundException(addressesHex, nameof(WorldSheet), worldId);
            }

            if (stageId < worldRow.StageBegin ||
                stageId > worldRow.StageEnd)
            {
                throw new SheetRowColumnException(
                          $"{addressesHex}{worldId} world is not contains {worldRow.Id} stage: " +
                          $"{worldRow.StageBegin}-{worldRow.StageEnd}");
            }

            var stageSheet = states.GetSheet <StageSheet>();

            if (!stageSheet.TryGetValue(stageId, out var stageRow))
            {
                throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId);
            }

            var worldInformation = avatarState.worldInformation;

            if (!worldInformation.TryGetWorld(worldId, out var world))
            {
                // NOTE: Add new World from WorldSheet
                worldInformation.AddAndUnlockNewWorld(worldRow, ctx.BlockIndex, worldSheet);
            }

            if (!world.IsUnlocked)
            {
                throw new InvalidWorldException($"{addressesHex}{worldId} is locked.");
            }

            if (world.StageBegin != worldRow.StageBegin ||
                world.StageEnd != worldRow.StageEnd)
            {
                worldInformation.UpdateWorld(worldRow);
            }

            if (world.IsStageCleared && stageId > world.StageClearedId + 1 ||
                !world.IsStageCleared && stageId != world.StageBegin)
            {
                throw new InvalidStageException(
                          $"{addressesHex}Aborted as the stage ({worldId}/{stageId}) is not cleared; " +
                          $"cleared stage: {world.StageClearedId}"
                          );
            }

            avatarState.ValidateEquipments(equipments, context.BlockIndex);
            avatarState.ValidateConsumable(foods, context.BlockIndex);
            avatarState.ValidateCostume(new HashSet <int>(costumes));

            var costumeStatSheet = states.GetSheet <CostumeStatSheet>();

            sw.Restart();
            if (avatarState.actionPoint < stageRow.CostAP)
            {
                throw new NotEnoughActionPointException(
                          $"{addressesHex}Aborted due to insufficient action point: " +
                          $"{avatarState.actionPoint} < {stageRow.CostAP}"
                          );
            }

            avatarState.actionPoint -= stageRow.CostAP;

            avatarState.EquipCostumes(new HashSet <int>(costumes));

            avatarState.EquipEquipments(equipments);
            sw.Stop();
            Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed);

            sw.Restart();
            var characterSheet = states.GetSheet <CharacterSheet>();
            var simulator      = new StageSimulator(
                ctx.Random,
                avatarState,
                foods,
                worldId,
                stageId,
                states.GetStageSimulatorSheets(),
                costumeStatSheet
                );

            sw.Stop();
            Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed);

            sw.Restart();
            simulator.SimulateV1();
            sw.Stop();
            Log.Verbose("{AddressesHex}HAS Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed);

            Log.Verbose(
                "{AddressesHex}Execute HackAndSlash({AvatarAddress}); worldId: {WorldId}, stageId: {StageId}, result: {Result}, " +
                "clearWave: {ClearWave}, totalWave: {TotalWave}",
                addressesHex,
                avatarAddress,
                worldId,
                stageId,
                simulator.Log.result,
                simulator.Log.clearedWaveNumber,
                simulator.Log.waveCount
                );

            sw.Restart();
            if (simulator.Log.IsClear)
            {
                var worldUnlockSheet = states.GetSheet <WorldUnlockSheet>();
                simulator.Player.worldInformation.ClearStage(
                    worldId,
                    stageId,
                    ctx.BlockIndex,
                    worldSheet,
                    worldUnlockSheet
                    );
            }

            sw.Stop();
            Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed);

            sw.Restart();
            avatarState.Update(simulator);
            var materialSheet = states.GetSheet <MaterialItemSheet>();

            avatarState.UpdateQuestRewards2(materialSheet);

            //Avoid InvalidBlockStateRootHashException to 50000 index.
            if (avatarState.questList.Any(q => q.Complete && !q.IsPaidInAction))
            {
                var prevIds = avatarState.questList.completedQuestIds;
                avatarState.UpdateQuestRewards(materialSheet);
                avatarState.questList.completedQuestIds = prevIds;
            }

            avatarState.updatedAt = ctx.BlockIndex;
            states = states.SetState(avatarAddress, avatarState.Serialize());

            sw.Stop();
            Log.Verbose("{AddressesHex}HAS Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed);

            sw.Restart();
            if (states.TryGetState(RankingMapAddress, out Dictionary d) && simulator.Log.IsClear)
            {
                var ranking = new RankingMapState(d);
                ranking.Update(avatarState);

                sw.Stop();
                Log.Verbose("{AddressesHex}HAS Update RankingState: {Elapsed}", addressesHex, sw.Elapsed);
                sw.Restart();

                var serialized = ranking.Serialize();

                sw.Stop();
                Log.Verbose("{AddressesHex}HAS Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed);
                sw.Restart();
                states = states.SetState(RankingMapAddress, serialized);
            }

            sw.Stop();
            Log.Verbose("{AddressesHex}HAS Set RankingState: {Elapsed}", addressesHex, sw.Elapsed);

            sw.Restart();
            if (simulator.Log.stageId >= GameConfig.RequireClearedStageLevel.ActionsInRankingBoard &&
                simulator.Log.IsClear &&
                states.TryGetState(WeeklyArenaAddress, out Dictionary weeklyDict))
            {
                var weekly = new WeeklyArenaState(weeklyDict);
                if (!weekly.Ended)
                {
                    if (weekly.ContainsKey(avatarAddress))
                    {
                        var info = weekly[avatarAddress];
                        info.UpdateV2(avatarState, characterSheet, costumeStatSheet);
                        weekly.Update(info);
                    }
                    else
                    {
                        weekly.SetV2(avatarState, characterSheet, costumeStatSheet);
                    }

                    sw.Stop();
                    Log.Verbose("{AddressesHex}HAS Update WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed);

                    sw.Restart();
                    var weeklySerialized = weekly.Serialize();
                    sw.Stop();
                    Log.Verbose("{AddressesHex}HAS Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed);

                    states = states.SetState(weekly.address, weeklySerialized);
                }
            }

            Result = simulator.Log;

            var ended = DateTimeOffset.UtcNow;

            Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, ended - started);
            return(states);
        }
Ejemplo n.º 4
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                states = states.SetState(RankingMapAddress, MarkChanged);
                states = states.SetState(avatarAddress, MarkChanged);
                return(states.SetState(WeeklyArenaAddress, MarkChanged));
            }

            var sw = new Stopwatch();

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

            Log.Debug("Mimisbrunnr exec started.");

            if (!states.TryGetAvatarState(ctx.Signer, avatarAddress, out AvatarState avatarState))
            {
                throw new FailedLoadStateException("Aborted as the avatar state of the signer was failed to load.");
            }

            sw.Stop();
            Log.Debug("Mimisbrunnr Get AgentAvatarStates: {Elapsed}", sw.Elapsed);

            sw.Restart();

            if (avatarState.RankingMapAddress != RankingMapAddress)
            {
                throw new InvalidAddressException("Invalid ranking map address");
            }

            var worldSheet       = states.GetSheet <WorldSheet>();
            var worldUnlockSheet = states.GetSheet <WorldUnlockSheet>();

            if (!worldSheet.TryGetValue(worldId, out var worldRow, false))
            {
                throw new SheetRowNotFoundException(nameof(WorldSheet), worldId);
            }

            if (stageId < worldRow.StageBegin ||
                stageId > worldRow.StageEnd)
            {
                throw new SheetRowColumnException(
                          $"{worldId} world is not contains {worldRow.Id} stage: " +
                          $"{worldRow.StageBegin}-{worldRow.StageEnd}");
            }

            var stageSheet = states.GetSheet <StageSheet>();

            if (!stageSheet.TryGetValue(stageId, out var stageRow))
            {
                throw new SheetRowNotFoundException(nameof(StageSheet), stageId);
            }

            var worldInformation = avatarState.worldInformation;

            if (!worldInformation.TryGetWorld(worldId, out var world))
            {
                // NOTE: Add new World from WorldSheet
                worldInformation.AddAndUnlockMimisbrunnrWorld(worldRow, ctx.BlockIndex, worldSheet, worldUnlockSheet);
                if (!worldInformation.TryGetWorld(worldId, out world))
                {
                    // Do nothing.
                }
            }

            if (!world.IsUnlocked)
            {
                var worldUnlockSheetRow = worldUnlockSheet.OrderedList.FirstOrDefault(row => row.WorldIdToUnlock == worldId);
                if (!(worldUnlockSheetRow is null) &&
                    worldInformation.IsWorldUnlocked(worldUnlockSheetRow.WorldId) &&
                    worldInformation.IsStageCleared(worldUnlockSheetRow.StageId))
                {
                    worldInformation.UnlockWorld(worldId, ctx.BlockIndex, worldSheet);
                    if (!worldInformation.TryGetWorld(worldId, out world))
                    {
                        // Do nothing.
                    }
                }
            }

            if (!world.IsUnlocked)
            {
                throw new InvalidWorldException($"{worldId} is locked.");
            }

            if (world.StageBegin != worldRow.StageBegin ||
                world.StageEnd != worldRow.StageEnd)
            {
                worldInformation.UpdateWorld(worldRow);
            }

            if (world.IsStageCleared && stageId > world.StageClearedId + 1 ||
                !world.IsStageCleared && stageId != world.StageBegin)
            {
                throw new InvalidStageException(
                          $"Aborted as the stage ({worldId}/{stageId}) is not cleared; " +
                          $"cleared stage: {world.StageClearedId}"
                          );
            }

            sw.Restart();
            var mimisbrunnrSheet = states.GetSheet <MimisbrunnrSheet>();

            if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow))
            {
                throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId);
            }

            foreach (var equipmentId in equipments)
            {
                if (avatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable))
                {
                    var elementalType = ((Equipment)itemUsable).ElementalType;
                    if (!mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType))
                    {
                        throw new InvalidElementalException(
                                  $"ElementalType of {equipmentId} does not match.");
                    }
                }
            }
            sw.Stop();
            Log.Debug("Mimisbrunnr Check Equipments ElementalType: {Elapsed}", sw.Elapsed);

            avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex);
            avatarState.ValidateConsumable(foods, context.BlockIndex);
            avatarState.ValidateCostume(costumes);

            sw.Restart();
            if (avatarState.actionPoint < stageRow.CostAP)
            {
                throw new NotEnoughActionPointException(
                          $"Aborted due to insufficient action point: " +
                          $"{avatarState.actionPoint} < {stageRow.CostAP}"
                          );
            }
            avatarState.actionPoint -= stageRow.CostAP;
            var equippableItem = new List <Guid>();

            equippableItem.AddRange(costumes);
            equippableItem.AddRange(equipments);
            avatarState.EquipItems(equippableItem);
            sw.Stop();
            Log.Debug("Mimisbrunnr Unequip items: {Elapsed}", sw.Elapsed);

            sw.Restart();
            var costumeStatSheet = states.GetSheet <CostumeStatSheet>();
            var simulator        = new StageSimulator(
                ctx.Random,
                avatarState,
                foods,
                worldId,
                stageId,
                states.GetStageSimulatorSheets(),
                costumeStatSheet,
                2);

            sw.Stop();
            Log.Debug("Mimisbrunnr Initialize Simulator: {Elapsed}", sw.Elapsed);

            sw.Restart();
            simulator.SimulateV2();
            sw.Stop();
            Log.Debug("Mimisbrunnr Simulator.Simulate(): {Elapsed}", sw.Elapsed);

            Log.Debug(
                "Execute Mimisbrunnr({AvatarAddress}); worldId: {WorldId}, stageId: {StageId}, result: {Result}, " +
                "clearWave: {ClearWave}, totalWave: {TotalWave}",
                avatarAddress,
                worldId,
                stageId,
                simulator.Log.result,
                simulator.Log.clearedWaveNumber,
                simulator.Log.waveCount
                );

            sw.Restart();
            if (simulator.Log.IsClear)
            {
                simulator.Player.worldInformation.ClearStage(
                    worldId,
                    stageId,
                    ctx.BlockIndex,
                    worldSheet,
                    worldUnlockSheet
                    );
            }
            sw.Stop();
            Log.Debug("Mimisbrunnr ClearStage: {Elapsed}", sw.Elapsed);

            sw.Restart();
            avatarState.Update(simulator);

            var materialSheet = states.GetSheet <MaterialItemSheet>();

            avatarState.UpdateQuestRewards(materialSheet);

            avatarState.updatedAt = ctx.BlockIndex;
            avatarState.mailBox.CleanUpV2();
            states = states.SetState(avatarAddress, avatarState.Serialize());

            sw.Stop();
            Log.Debug("Mimisbrunnr Set AvatarState: {Elapsed}", sw.Elapsed);

            sw.Restart();
            if (states.TryGetState(RankingMapAddress, out Dictionary d) && simulator.Log.IsClear)
            {
                var ranking = new RankingMapState(d);
                ranking.Update(avatarState);

                sw.Stop();
                Log.Debug("Mimisbrunnr Update RankingState: {Elapsed}", sw.Elapsed);
                sw.Restart();

                var serialized = ranking.Serialize();

                sw.Stop();
                Log.Debug("Mimisbrunnr Serialize RankingState: {Elapsed}", sw.Elapsed);
                sw.Restart();
                states = states.SetState(RankingMapAddress, serialized);
            }

            sw.Stop();
            Log.Debug("Mimisbrunnr Set RankingState: {Elapsed}", sw.Elapsed);

            sw.Restart();
            if (simulator.Log.stageId >= GameConfig.RequireClearedStageLevel.ActionsInRankingBoard &&
                simulator.Log.IsClear &&
                states.TryGetState(WeeklyArenaAddress, out Dictionary weeklyDict))
            {
                var weekly = new WeeklyArenaState(weeklyDict);
                if (!weekly.Ended)
                {
                    var characterSheet = states.GetSheet <CharacterSheet>();
                    if (weekly.ContainsKey(avatarAddress))
                    {
                        var info = weekly[avatarAddress];
                        info.Update(avatarState, characterSheet, costumeStatSheet);
                        weekly.Update(info);
                    }
                    else
                    {
                        weekly.SetV2(avatarState, characterSheet, costumeStatSheet);
                    }

                    sw.Stop();
                    Log.Debug("Mimisbrunnr Update WeeklyArenaState: {Elapsed}", sw.Elapsed);

                    sw.Restart();
                    var weeklySerialized = weekly.Serialize();
                    sw.Stop();
                    Log.Debug("Mimisbrunnr Serialize RankingState: {Elapsed}", sw.Elapsed);

                    states = states.SetState(weekly.address, weeklySerialized);
                }
            }

            Result = simulator.Log;

            var ended = DateTimeOffset.UtcNow;

            Log.Debug("Mimisbrunnr Total Executed Time: {Elapsed}", ended - started);
            return(states);
        }
Ejemplo n.º 5
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx              = context;
            var            states           = ctx.PreviousStates;
            var            weeklyArenaState = new WeeklyArenaState(0);

            var rankingState = new RankingState0(Ranking);

            if (ctx.Rehearsal)
            {
                states = states.SetState(RankingState0.Address, MarkChanged);
                states = states.SetState(ShopState.Address, MarkChanged);
#pragma warning disable LAA1002
                states = TableSheets
                         .Aggregate(states, (current, pair) =>
                                    current.SetState(Addresses.TableSheet.Derive(pair.Key), MarkChanged));
                states = rankingState.RankingMap
                         .Aggregate(states, (current, pair) =>
                                    current.SetState(pair.Key, MarkChanged));
#pragma warning restore LAA1002
                states = states.SetState(weeklyArenaState.address, MarkChanged);
                states = states.SetState(GameConfigState.Address, MarkChanged);
                states = states.SetState(RedeemCodeState.Address, MarkChanged);
                states = states.SetState(AdminState.Address, MarkChanged);
                states = states.SetState(ActivatedAccountsState.Address, MarkChanged);
                states = states.SetState(GoldCurrencyState.Address, MarkChanged);
                states = states.SetState(Addresses.GoldDistribution, MarkChanged);
                foreach (var rawPending in PendingActivations)
                {
                    states = states.SetState(
                        new PendingActivationState((Bencodex.Types.Dictionary)rawPending).address,
                        MarkChanged
                        );
                }
                states = states.SetState(AuthorizedMinersState.Address, MarkChanged);
                states = states.SetState(CreditsState.Address, MarkChanged);
                return(states);
            }

            if (ctx.BlockIndex != 0)
            {
                return(states);
            }

#pragma warning disable LAA1002
            states = TableSheets
                     .Aggregate(states, (current, pair) =>
                                current.SetState(Addresses.TableSheet.Derive(pair.Key), pair.Value.Serialize()));
            states = rankingState.RankingMap
                     .Aggregate(states, (current, pair) =>
                                current.SetState(pair.Key, new RankingMapState(pair.Key).Serialize()));
#pragma warning restore LAA1002
            states = states
                     .SetState(weeklyArenaState.address, weeklyArenaState.Serialize())
                     .SetState(RankingState0.Address, Ranking)
                     .SetState(ShopState.Address, Shop)
                     .SetState(GameConfigState.Address, GameConfig)
                     .SetState(RedeemCodeState.Address, RedeemCode)
                     .SetState(AdminState.Address, AdminAddress)
                     .SetState(ActivatedAccountsState.Address, ActivatedAccounts)
                     .SetState(GoldCurrencyState.Address, GoldCurrency)
                     .SetState(Addresses.GoldDistribution, GoldDistributions);

            if (!(AuthorizedMiners is null))
            {
                states = states.SetState(
                    AuthorizedMinersState.Address,
                    AuthorizedMiners
                    );
            }

            foreach (var rawPending in PendingActivations)
            {
                states = states.SetState(
                    new PendingActivationState((Bencodex.Types.Dictionary)rawPending).address,
                    rawPending
                    );
            }

            if (!(Credits is null))
            {
                states = states.SetState(CreditsState.Address, Credits);
            }

            var currency = new GoldCurrencyState(GoldCurrency).Currency;
            states = states.MintAsset(GoldCurrencyState.Address, currency * 1000000000);
            return(states);
        }
Ejemplo n.º 6
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx    = context;
            var            states = ctx.PreviousStates;

            if (ctx.Rehearsal)
            {
                states = states.SetState(RankingState.Address, MarkChanged);
                states = states.SetState(avatarAddress, MarkChanged);
                states = states.SetState(WeeklyArenaAddress, MarkChanged);
                return(states.SetState(ctx.Signer, MarkChanged));
            }

            var sw = new Stopwatch();

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

            Log.Debug("HAS exec started.");

            if (!states.TryGetAgentAvatarStates(
                    ctx.Signer,
                    avatarAddress,
                    out AgentState agentState,
                    out AvatarState avatarState))
            {
                // FIXME we should create dedicated type for this exception.
                throw new InvalidOperationException(
                          "Aborted as the avatar state of the signer was failed to load."
                          );
            }

            sw.Stop();
            Log.Debug("HAS Get AgentAvatarStates: {Elapsed}", sw.Elapsed);

            sw.Restart();
            var tableSheetState = TableSheetsState.FromActionContext(ctx);

            sw.Stop();
            Log.Debug("HAS Get TableSheetsState: {Elapsed}", sw.Elapsed);

            sw.Restart();
            var tableSheets = TableSheets.FromTableSheetsState(tableSheetState);

            sw.Stop();
            Log.Debug("HAS Initialize TableSheets: {Elapsed}", sw.Elapsed);

            // worldId와 stageId가 유효한지 확인합니다.

            if (!tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow))
            {
                // FIXME we should create dedicated type for this exception.
                throw new InvalidOperationException(
                          $"Not fount {worldId} in TableSheets.WorldSheet."
                          );
            }

            if (stageId < worldRow.StageBegin ||
                stageId > worldRow.StageEnd)
            {
                // FIXME we should create dedicated type for this exception.
                throw new InvalidOperationException(
                          $"{worldId} world is not contains {worldRow.Id} stage: " +
                          $"{worldRow.StageBegin}-{worldRow.StageEnd}"
                          );
            }

            if (!tableSheets.StageSheet.TryGetValue(stageId, out var stageRow))
            {
                // FIXME we should create dedicated type for this exception.
                throw new InvalidOperationException(
                          $"Not fount stage id in TableSheets.StageSheet: {stageId}"
                          );
            }

            var worldInformation = avatarState.worldInformation;

            if (!worldInformation.TryGetWorld(worldId, out var world))
            {
                // NOTE: 이 경우는 아바타 생성 시에는 WorldSheet에 없던 worldId가 새로 추가된 경우로 볼 수 있습니다.
                if (!worldInformation.TryAddWorld(worldRow, out world))
                {
                    // FIXME we should create dedicated type for this exception.
                    throw new InvalidOperationException(
                              $"Failed to add {worldId} world to WorldInformation."
                              );
                }
            }

            if (!world.IsUnlocked)
            {
                // FIXME we should create dedicated type for this exception.
                throw new InvalidOperationException(
                          $"Aborted as the world {worldId} is locked."
                          );
            }

            if (world.StageBegin != worldRow.StageBegin ||
                world.StageEnd != worldRow.StageEnd)
            {
                // NOTE: 이 경우는 아바타 생성 이후에 worldId가 포함하는 stageId의 범위가 바뀐 경우로 볼 수 있습니다.
                if (!worldInformation.TryUpdateWorld(worldRow, out world))
                {
                    // FIXME we should create dedicated type for this exception.
                    throw new InvalidOperationException(
                              $"Failed to update {worldId} world in WorldInformation."
                              );
                }

                if (world.StageBegin != worldRow.StageBegin ||
                    world.StageEnd != worldRow.StageEnd)
                {
                    // FIXME we should create dedicated type for this exception.
                    throw new InvalidOperationException(
                              $"Failed to update {worldId} world in WorldInformation."
                              );
                }
            }

            if (world.IsStageCleared && stageId > world.StageClearedId + 1 ||
                !world.IsStageCleared && stageId != world.StageBegin)
            {
                // FIXME we should create dedicated type for this exception.
                throw new InvalidOperationException(
                          $"Aborted as the stage ({worldId}/{stageId}) is not cleared; " +
                          $"cleared stage: {world.StageClearedId}"
                          );
            }

            // 장비가 유효한지 검사한다.
            if (!avatarState.ValidateEquipments(equipments, context.BlockIndex))
            {
                // FIXME we should create dedicated type for this exception.
                throw new InvalidOperationException("Aborted as the equipment is invalid.");
            }

            sw.Restart();
            if (avatarState.actionPoint < stageRow.CostAP)
            {
                // FIXME we should create dedicated type for this exception.
                throw new InvalidOperationException(
                          $"Aborted due to insufficient action point: " +
                          $"{avatarState.actionPoint} < {stageRow.CostAP}"
                          );
            }

            avatarState.actionPoint -= stageRow.CostAP;

            avatarState.EquipCostumes(costumes);

            avatarState.EquipEquipments(equipments);
            sw.Stop();
            Log.Debug("HAS Unequip items: {Elapsed}", sw.Elapsed);

            sw.Restart();
            var simulator = new StageSimulator(
                ctx.Random,
                avatarState,
                foods,
                worldId,
                stageId,
                tableSheets
                );

            sw.Stop();
            Log.Debug("HAS Initialize Simulator: {Elapsed}", sw.Elapsed);

            sw.Restart();
            simulator.Simulate();
            sw.Stop();
            Log.Debug("HAS Simulator.Simulate(): {Elapsed}", sw.Elapsed);

            Log.Debug(
                "Execute HackAndSlash({AvatarAddress}); worldId: {WorldId}, stageId: {StageId}, result: {Result}, " +
                "clearWave: {ClearWave}, totalWave: {TotalWave}",
                avatarAddress,
                worldId,
                stageId,
                simulator.Log.result,
                simulator.Log.clearedWaveNumber,
                simulator.Log.waveCount
                );

            sw.Restart();
            if (simulator.Log.IsClear)
            {
                simulator.Player.worldInformation.ClearStage(
                    worldId,
                    stageId,
                    ctx.BlockIndex,
                    tableSheets.WorldSheet,
                    tableSheets.WorldUnlockSheet
                    );
            }

            sw.Stop();
            Log.Debug("HAS ClearStage: {Elapsed}", sw.Elapsed);

            sw.Restart();
            avatarState.Update(simulator);

            avatarState.UpdateQuestRewards(ctx);

            avatarState.updatedAt = DateTimeOffset.UtcNow;
            states = states.SetState(avatarAddress, avatarState.Serialize());

            sw.Stop();
            Log.Debug("HAS Set AvatarState: {Elapsed}", sw.Elapsed);

            sw.Restart();
            if (states.TryGetState(RankingState.Address, out Dictionary d) && simulator.Log.IsClear)
            {
                var ranking = new RankingState(d);
                ranking.Update(avatarState);

                sw.Stop();
                Log.Debug("HAS Update RankingState: {Elapsed}", sw.Elapsed);
                sw.Restart();

                var serialized = ranking.Serialize();

                sw.Stop();
                Log.Debug("HAS Serialize RankingState: {Elapsed}", sw.Elapsed);
                sw.Restart();
                states = states.SetState(RankingState.Address, serialized);
            }

            sw.Stop();
            Log.Debug("HAS Set RankingState: {Elapsed}", sw.Elapsed);

            sw.Restart();
            if (states.TryGetState(WeeklyArenaAddress, out Dictionary weeklyDict))
            {
                var weekly = new WeeklyArenaState(weeklyDict);
                if (!weekly.Ended)
                {
                    if (weekly.ContainsKey(avatarAddress))
                    {
                        var info = weekly[avatarAddress];
                        info.Update(avatarState, tableSheets.CharacterSheet);
                        weekly.Update(info);
                    }
                    else
                    {
                        weekly.Set(avatarState, tableSheets.CharacterSheet);
                    }

                    sw.Stop();
                    Log.Debug("HAS Update WeeklyArenaState: {Elapsed}", sw.Elapsed);

                    sw.Restart();
                    var weeklySerialized = weekly.Serialize();
                    sw.Stop();
                    Log.Debug("HAS Serialize RankingState: {Elapsed}", sw.Elapsed);

                    states = states.SetState(weekly.address, weeklySerialized);
                }
            }

            Result = simulator.Log;

            var ended = DateTimeOffset.UtcNow;

            Log.Debug("HAS Total Executed Time: {Elapsed}", ended - started);
            return(states.SetState(ctx.Signer, agentState.Serialize()));
        }
Ejemplo n.º 7
0
        public void Execute()
        {
            var tableSheets  = TableSheets.FromTableSheetsState(_tableSheetsState);
            var itemId       = tableSheets.WeeklyArenaRewardSheet.Values.First().Reward.ItemId;
            var privateKey   = new PrivateKey();
            var agentAddress = privateKey.PublicKey.ToAddress();
            var agent        = new AgentState(agentAddress);

            var avatarAddress = agentAddress.Derive("avatar");
            var avatarState   = new AvatarState(avatarAddress, agentAddress, 0, tableSheets, new GameConfigState());

            avatarState.worldInformation.ClearStage(
                1,
                GameConfig.RequireClearedStageLevel.ActionsInRankingBoard,
                1,
                tableSheets.WorldSheet,
                tableSheets.WorldUnlockSheet
                );
            agent.avatarAddresses.Add(0, avatarAddress);

            Assert.False(avatarState.inventory.HasItem(itemId));

            var avatarAddress2 = agentAddress.Derive("avatar2");
            var avatarState2   = new AvatarState(avatarAddress2, agentAddress, 0, tableSheets, new GameConfigState());

            avatarState2.worldInformation.ClearStage(
                1,
                GameConfig.RequireClearedStageLevel.ActionsInRankingBoard,
                1,
                tableSheets.WorldSheet,
                tableSheets.WorldUnlockSheet
                );
            agent.avatarAddresses.Add(1, avatarAddress);

            var weekly = new WeeklyArenaState(0);

            weekly.Set(avatarState, tableSheets.CharacterSheet);
            weekly[avatarAddress].Activate();
            weekly.Set(avatarState2, tableSheets.CharacterSheet);
            weekly[avatarAddress2].Activate();

            var state = new State(ImmutableDictionary <Address, IValue> .Empty
                                  .Add(_tableSheetsState.address, _tableSheetsState.Serialize())
                                  .Add(weekly.address, weekly.Serialize())
                                  .Add(agentAddress, agent.Serialize())
                                  .Add(avatarAddress, avatarState.Serialize())
                                  .Add(avatarAddress2, avatarState2.Serialize()));

            var action = new RankingBattle
            {
                AvatarAddress      = avatarAddress,
                EnemyAddress       = avatarAddress2,
                WeeklyArenaAddress = weekly.address,
                costumeIds         = new List <int>(),
                equipmentIds       = new List <Guid>(),
                consumableIds      = new List <Guid>(),
            };

            Assert.Null(action.Result);

            var nextState = action.Execute(new ActionContext()
            {
                PreviousStates = state,
                Signer         = agentAddress,
                Random         = new ItemEnhancementTest.TestRandom(),
                Rehearsal      = false,
            });

            var newState = nextState.GetAvatarState(avatarAddress);

            Assert.True(newState.inventory.HasItem(itemId));
            Assert.NotNull(action.Result);
            Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType()));
        }